L'authentification JWT MQTT consiste, pour un client, à placer un JSON Web Token signé dans le champ mot de passe de son paquet CONNECT ; le broker valide alors la signature et les claims au lieu de comparer un mot de passe stocké. Cet article explique ce qui se passe sur le réseau, comment gérer l'expiration et le renouvellement des jetons sur de vrais appareils, et dans quels cas cette complexité supplémentaire vaut la peine par rapport à des identifiants statiques par appareil.
Comment un JWT voyage dans le CONNECT MQTT
L'authentification de base de MQTT ne prévoit aucun champ dédié aux jetons. Elle dispose d'une chaîne username et d'un champ password que la spécification définit comme des données binaires. Les brokers compatibles JWT réutilisent ce champ :
CONNECT
Client ID: sensor-0042
Username: sensor-0042 (often ignored or matched to a claim)
Password: eyJhbGciOiJSUzI1NiIs... (the encoded JWT)
Un JWT comporte trois parties encodées en base64url et séparées par des points : un en-tête qui indique l'algorithme, un payload de claims et une signature. Les claims d'un jeton d'appareil typique ressemblent à ceci :
{
"iss": "https://auth.example.com",
"sub": "sensor-0042",
"aud": "mqtt.example.com",
"iat": 1792130400,
"exp": 1792134000,
"topics": {
"pub": ["plant1/line3/sensor-0042/#"],
"sub": ["plant1/line3/sensor-0042/cmd/#"]
}
}
Le broker, ou un plugin d'authentification qui lui est rattaché, procède ensuite ainsi :
- Il décode le jeton et vérifie la signature avec un secret partagé (HS256) ou une clé publique (RS256, ES256).
- Il contrôle
exp,nbf,issetaud. - Il compare éventuellement
subau client ID ou au username. - Il lit éventuellement des claims personnalisés pour décider quels topics le client peut utiliser.
Comme le jeton circule en tant que mot de passe, la même règle s'applique qu'à toute authentification MQTT par nom d'utilisateur et mot de passe : il doit être envoyé via TLS. Un JWT est un identifiant au porteur. Quiconque l'intercepte peut l'utiliser jusqu'à son expiration.
L'authentification étendue de MQTT 5
MQTT 5.0 ajoute une propriété Authentication Method et un paquet AUTH, afin que le broker et le client puissent mener des échanges de type défi-réponse et se réauthentifier pendant une session active sans déconnexion. Certains brokers proposent des méthodes à jeton via ce mécanisme, ce qui permet de renouveler un jeton sur une connexion ouverte. La prise en charge varie beaucoup d'un broker et d'une bibliothèque client à l'autre ; la plupart des déploiements utilisent donc toujours l'approche plus simple du champ mot de passe et se reconnectent pour renouveler le jeton. La spécification OASIS MQTT 5.0 décrit le flux AUTH.
Quels brokers prennent en charge JWT ?
JWT est une fonctionnalité du broker ou d'un plugin, pas de MQTT lui-même. Certains brokers intègrent un authentificateur JWT, et Mosquitto peut valider des jetons via des plugins d'authentification tiers. Consultez la documentation de votre broker pour savoir quels algorithmes, quelles sources de clés (clé statique ou endpoint JWKS) et quels mappages de claims il prend en charge.
ConnectMQ ne prend pas en charge l'authentification JWT. Un broker ConnectMQ authentifie les clients avec le nom d'utilisateur et le mot de passe générés dans le portail, et lie chaque client ID aux identifiants qui lui ont été attribués. Les tenants sont isolés les uns des autres, si bien que ces identifiants ne donnent accès qu'à votre propre broker. Les compromis présentés ci-dessous vous aideront à déterminer si ce modèle ou un modèle à jetons convient à votre projet.
Émettre et utiliser un jeton : exemple
L'exemple ci-dessous montre les deux moitiés d'une configuration JWT face à un broker hypothétique sur mqtt.example.com qui valide des jetons RS256. D'abord, un service émetteur minimal signe un jeton pour un appareil :
# pip install pyjwt cryptography
import time
import jwt
PRIVATE_KEY = open("issuer_private.pem", "rb").read()
def issue_device_token(device_id: str, ttl_seconds: int = 3600) -> str:
now = int(time.time())
claims = {
"iss": "https://auth.example.com",
"sub": device_id,
"aud": "mqtt.example.com",
"iat": now,
"nbf": now - 30, # tolérer un léger décalage d'horloge
"exp": now + ttl_seconds,
}
return jwt.encode(claims, PRIVATE_KEY, algorithm="RS256")
if __name__ == "__main__":
print(issue_device_token("sensor-0042"))
Ensuite, l'appareil se connecte avec le jeton comme mot de passe et se reconnecte avec un jeton neuf avant l'expiration :
# pip install paho-mqtt requests
import ssl
import time
import requests
import paho.mqtt.client as mqtt
DEVICE_ID = "sensor-0042"
TOKEN_URL = "https://auth.example.com/device-token"
REFRESH_EVERY = 50 * 60 # renouveler 10 minutes avant une expiration à 60 minutes
def fetch_token() -> str:
# L'appareil s'authentifie auprès de l'émetteur avec son propre secret à long terme
r = requests.post(TOKEN_URL, json={"device": DEVICE_ID}, timeout=10)
r.raise_for_status()
return r.json()["token"]
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=DEVICE_ID, protocol=mqtt.MQTTv5)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.on_connect = on_connect
def connect_with_fresh_token():
client.username_pw_set(DEVICE_ID, fetch_token())
client.connect("mqtt.example.com", 8883, keepalive=60)
client.loop_start()
connect_with_fresh_token()
while True:
time.sleep(REFRESH_EVERY)
client.disconnect() # déconnexion propre, session conservée si configurée
client.loop_stop()
connect_with_fresh_token()
Notez le commentaire dans fetch_token : l'appareil a toujours besoin d'un secret ou d'un certificat à long terme pour prouver son identité auprès de l'émetteur. JWT déplace l'identifiant statique de la connexion au broker vers la connexion à l'émetteur ; il ne le supprime pas.
Modèles d'expiration et de renouvellement
L'expiration est la principale raison d'utiliser des jetons, et aussi la principale source d'incidents en production. Les brokers gèrent un jeton qui expire de l'une de ces deux façons :
| Comportement du broker | Effet sur le client |
|---|---|
Vérifie exp uniquement au CONNECT |
La session continue après l'expiration jusqu'à la prochaine reconnexion |
Déconnecte le client à exp |
Le client doit se reconnecter avec un nouveau jeton avant ce moment |
Les modèles qui fonctionnent sur les appareils :
- Renouveler tôt. Demandez un nouveau jeton vers 80 % de sa durée de vie, pas au moment de l'expiration.
- Se reconnecter proprement. Utilisez une session persistante (Clean Start à false avec une session expiry) pour que les abonnements et les messages QoS 1 en file d'attente survivent à la reconnexion.
- Garder l'horloge à l'heure. Un appareil dont l'horloge est fausse produira ou rejettera des jetons à tort. Synchronisez l'heure via NTP avant de demander un jeton et tolérez un léger décalage avec
nbf. - Gérer les pannes de l'émetteur. Si l'émetteur est indisponible, l'appareil conserve sa connexion actuelle et réessaie avec un backoff ; il ne doit pas abandonner une session qui fonctionne.
- Journaliser les codes de CONNACK. Un jeton expiré ou invalide produit généralement
0x86(Bad User Name or Password) ou0x87(Not authorized) en MQTT 5.
Pièges de sécurité des jetons
Les jetons échouent d'une manière différente des mots de passe ; passez donc ces points en revue avant la mise en production :
- Confusion d'algorithme. Configurez le broker pour n'accepter que l'algorithme que vous utilisez, par exemple RS256. N'acceptez jamais
noneet ne laissez jamais l'en-tête du jeton choisir entre vérification symétrique et asymétrique. - Secrets HMAC partagés sur les appareils. Si les appareils pouvaient signer leurs propres jetons HS256, chaque appareil détiendrait le secret maître. Signez les jetons uniquement côté serveur.
- Durées de vie trop longues. Un jeton valable un an n'est qu'un mot de passe avec des étapes en plus. Choisissez des durées de vie assez courtes pour qu'un jeton divulgué expire vite, et assez longues pour que les appareils sur des réseaux médiocres ne passent pas leur temps à se renouveler.
- Absence de contrôle de l'audience. Sans
aud, un jeton émis pour un autre service peut être rejoué contre votre broker. - Journalisation des jetons. Les jetons finissent dans les journaux de débogage et les rapports de plantage, exactement comme les mots de passe. Journalisez
subetexp, jamais le jeton.
JWT ou identifiants statiques par appareil
| Critère | JWT | Identifiants statiques par appareil |
|---|---|---|
| Durée de validité d'un secret volé sur le réseau | Jusqu'à l'expiration du jeton | Jusqu'à la rotation de l'identifiant |
| Infrastructure supplémentaire | Émetteur de jetons, gestion des clés, synchronisation d'horloge | Rien au-delà du broker |
| Révocation | Attendre l'expiration ou maintenir une liste de refus | Révoquer l'identifiant |
| Données d'autorisation | Peuvent être portées par des claims de topics dans le jeton | Gérées côté broker |
| Complexité côté appareil | Client HTTP, logique de renouvellement, NTP | Stocker un mot de passe |
| Débogage | Décoder le jeton, vérifier les claims et les horloges | Vérifier le nom d'utilisateur et le mot de passe |
JWT est à son avantage lorsque vous exploitez déjà un fournisseur d'identité, lorsque des utilisateurs (et pas seulement des appareils) se connectent depuis des navigateurs ou des applications mobiles avec des sessions courtes, ou lorsque vous voulez intégrer l'autorisation dans l'identifiant lui-même.
Les identifiants statiques par appareil sont à leur avantage pour les parcs d'appareils où la simplicité compte : microcontrôleurs aux ressources limitées, sites dont l'accès à un émetteur est peu fiable, équipes qui ne souhaitent pas exploiter un service d'authentification. Leur faiblesse, des secrets à longue durée de vie, se traite par un stockage protégé et une rotation planifiée. Consultez comment effectuer la rotation des identifiants MQTT et les notes de conception sur les concepts d'ACL MQTT pour le volet autorisation.
Un juste milieu avec un broker MQTT managé
Si ce que vous attendez de JWT, c'est surtout « aucun secret partagé et un moyen rapide de couper un appareil », vous pouvez l'obtenir en grande partie sans jetons : donnez à chaque appareil son propre jeu d'identifiants, liez-le à un client ID, conservez-le dans un stockage protégé et effectuez-en la rotation selon un calendrier. C'est le modèle qu'un broker MQTT managé comme ConnectMQ fournit d'emblée, avec TLS sur les ports 8883 et 8084, le blocage d'IP après des échecs de connexion répétés et l'isolation des tenants. Pour les applications web, conservez les identifiants du broker sur votre backend et laissez les utilisateurs s'authentifier auprès de ce backend avec le système de jetons que vous utilisez déjà.
Démarrer avec ConnectMQ
Vous voulez une authentification solide par appareil sans exploiter d'émetteur de jetons ? Créez gratuitement un broker MQTT cloud Serverless sur app.connectmq.com, sans carte bancaire, et connectez votre premier appareil via TLS avec des identifiants générés en quelques minutes.
Questions fréquentes
ConnectMQ prend-il en charge l'authentification JWT MQTT ?
Non. ConnectMQ authentifie les clients avec des noms d'utilisateur et mots de passe générés, chaque client ID étant lié aux identifiants qui lui ont été attribués. Les tenants sont isolés les uns des autres.
Où se place le JWT dans une connexion MQTT ?
La plupart des brokers l'attendent dans le champ mot de passe du CONNECT, parfois avec le username défini sur l'ID de l'appareil ou sur une valeur fixe. Certains brokers MQTT 5 acceptent aussi les jetons via l'authentification étendue.
Que se passe-t-il si le jeton expire pendant une session ?
Cela dépend du broker. Certains ne valident qu'au moment de la connexion, d'autres déconnectent le client à l'expiration ; les appareils doivent donc renouveler le jeton et se reconnecter avant qu'il n'expire.
JWT est-il plus sûr qu'un mot de passe ?
Un jeton de courte durée limite le temps pendant lequel un identifiant volé reste utilisable, mais les appareils ont toujours besoin d'un secret à long terme pour obtenir des jetons. La sécurité globale dépend de la façon dont ces deux secrets sont stockés et transportés.



