javajwtxmppsmackmongoose-im

MongooseIM authentication with JWT and send message (XMPP)


MongooseIM has a provision to use JWT instead of username and password for authorization.

On the server-side, the docs suggest to modify the mongooseim.toml file (can be found at /etc/mongooseim/mongooseim.toml)

[auth]
  methods = ["jwt"]

  [auth.jwt]
    secret.value = "top-secret123"
    algorithm = "HS256"
    username_key = "user"

But how does then one authenticate from Gajim or from Java code?


Solution

  • Let's first understand what is happening behind the scenes.

    Instead of passing the username-password pair. We create a JWT token and send that. JWT tokens are stateless, which means if one has the secret key, one can decode and encode the token to/from the original message.

    Here is a working code in Java. We generate the JWT token and send that token instead of the password. To generate the JWT token, we have used Auth0 (you will need to add this in classpath). Link to the maven project.

    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.algorithms.Algorithm;
    import org.jivesoftware.smack.AbstractXMPPConnection;
    import org.jivesoftware.smack.ConnectionConfiguration;
    import org.jivesoftware.smack.chat2.Chat;
    import org.jivesoftware.smack.chat2.ChatManager;
    import org.jivesoftware.smack.tcp.XMPPTCPConnection;
    import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
    import org.jxmpp.jid.Jid;
    import org.jxmpp.jid.impl.JidCreate;
    
    import javax.net.ssl.X509TrustManager;
    import java.net.InetAddress;
    import java.util.Date;
    
    public class JWTMain {
        private final static String senderUsername = "jatin";
        private final static String senderPassword = "abcd";
    
        private final static String sendTo = "dad";
    
        public static void main(String[] args) throws Exception {
    
            Algorithm algorithm = Algorithm.HMAC256("top-secret123");
            String token = JWT.create()
                    .withClaim("user", senderUsername) // they key needs to match with `username_key` in mongooseim.toml file
                    .withClaim(senderUsername, senderPassword)
                    .sign(algorithm);
    
            System.out.println("Token generated: " + token);
    
            XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.required)
                    .setUsernameAndPassword("jatin", token)
                    .setXmppDomain(JidCreate.domainBareFrom("localhost"))
                    .setHostAddress(InetAddress.getByName("localhost"))
                    .setPort(5222)
                    .setCustomX509TrustManager(new TrustAllManager())
                    .addEnabledSaslMechanism("PLAIN")
                    .build();
    
            AbstractXMPPConnection connection = new XMPPTCPConnection(config);
            AbstractXMPPConnection connect = connection.connect();
            connection.login();
            sendMessage("This message is being sent programmatically? " + new Date(), sendTo + "@localhost", connect);
        }
    
        private static void sendMessage(String body, String toJid, AbstractXMPPConnection mConnection) throws Exception {
            Jid jid = JidCreate.from(toJid);
            Chat chat = ChatManager.getInstanceFor(mConnection)
                    .chatWith(jid.asEntityBareJidIfPossible());
            chat.send(body);
            System.out.println("Message sent to : " + toJid);
        }
    }
    
    
    class TrustAllManager implements X509TrustManager {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
        }
    
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
        }
    }
    

    If you wish to login to Gajim with the JWT token:

    The above program outputs the JWT token. You can use that token and provide the token in the password field.

    enter image description here