Fundamentos de MQTT

Cómo resolver "MQTT Connection Refused: Not Authorized" (rc 5 y 0x87)

¿Te aparece "Connection Refused: not authorised" o el reason code 0x87? Recorre esta lista de comprobación sobre credenciales, vinculación del client ID, puertos y TLS y cuentas por broker para encontrar la causa rápidamente.

Un broker rechaza el inicio de sesión de un dispositivo para ilustrar el error MQTT de conexión rechazada por no estar autorizado
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

"MQTT connection refused: not authorized" significa que el broker recibió tu paquete CONNECT, lo entendió y decidió que este cliente no puede conectarse. En MQTT 3.1.1 es el código de retorno CONNACK 5; en MQTT 5 es el reason code 0x87. Esta guía te ofrece una lista de comprobación paso a paso para encontrar la causa, desde las credenciales y los client ID hasta las confusiones de puertos y las cuentas por broker, y muestra cómo probar cada paso desde la línea de comandos.

Qué te dice realmente "Not Authorized"

El error es más informativo de lo que parece. Para recibirlo, varias cosas tienen que funcionar ya:

  • El DNS resolvió el host del broker.
  • Se estableció la conexión TCP y, si se usa, TLS.
  • El broker interpretó un paquete CONNECT válido.
  • Se aceptó la versión del protocolo.

Así que la red está bien. El problema es de identidad o de permisos. Lo verás redactado de distintas formas según la herramienta:

Herramienta Mensaje típico
mosquitto_pub / mosquitto_sub Connection error: Connection Refused: not authorised.
Paho Python 2.x reason_code es igual a Not authorized en on_connect
MQTT.js Connection refused: Not authorized (err.code === 5) o reason code 135 con MQTT 5
Nodo MQTT de Node-RED El estado muestra "connecting" y el registro indica conexión rechazada, no autorizado

Conviene distinguir pronto un código relacionado. El código de retorno 4 (0x86 en MQTT 5) significa "bad user name or password": las propias credenciales no coincidían. El código 5 (0x87) es más amplio: el broker rechaza a este cliente por un motivo de autorización. Algunos brokers también devuelven 5 ante contraseñas erróneas, así que trata ambos como "revisar la identidad", pero lee el código exacto. La guía de códigos de retorno CONNACK de MQTT explica todos los valores.

Lista de comprobación para el error Not Authorized

Recorre estos pasos en orden. La mayoría de los casos se resuelven antes del paso 4.

  1. Confirma a qué broker te conectas. Imprime el host y el puerto que tu código usa realmente en tiempo de ejecución, no los que crees haber configurado. Las variables de entorno y los archivos de configuración se sobrescriben entre sí con frecuencia.
  2. Vuelve a copiar las credenciales. Pega el usuario y la contraseña directamente desde la página de credenciales del broker. Busca espacios al principio o al final, saltos de línea al copiar y usuario y contraseña intercambiados.
  3. Usa el client ID que pertenece a las credenciales. Muchos brokers MQTT gestionados vinculan un client ID a la cuenta emitida. Si tu código genera un ID aleatorio o reutiliza uno de otro proyecto, el broker puede rechazarlo aunque la contraseña sea correcta.
  4. Comprueba que la librería envía las credenciales. En Paho, hay que llamar a username_pw_set() antes de connect(). En PubSubClient de Arduino, usa la sobrecarga connect(clientId, user, pass), no connect(clientId).
  5. Revisa el entrecomillado del shell. Bash altera las contraseñas que contienen $, ! o &. Usa comillas simples: -P 'p@ss$word!'.
  6. Verifica el puerto y el transporte. El puerto 1883 es TCP sin cifrar, el 8883 es TLS, el 8083 es WebSocket y el 8084 es WebSocket seguro con la ruta /mqtt. Una discrepancia suele producir un timeout o un error de TLS en lugar de "not authorized", pero un cliente mal configurado puede acabar hablando con otro listener u otro entorno distinto del previsto.
  7. Confirma que la cuenta está activa. Puede que las credenciales se hayan regenerado, que un broker se haya eliminado o recreado, o que estés usando credenciales de staging contra producción.
  8. Comprueba las cuotas de conexión. Si se alcanza el límite de conexiones del plan, los clientes MQTT 5 reciben 0x97 Quota exceeded, pero algunas librerías 3.1.1 muestran cualquier rechazo de forma genérica. Cuenta tus conexiones activas.
  9. Deja de reintentar en bucle. Cada intento fallido es, desde el punto de vista del broker, un inicio de sesión fallido. Espera entre reintentos mientras investigas.

Probar las credenciales desde la línea de comandos

Aísla tu aplicación del problema probando exactamente las mismas credenciales con las herramientas de Mosquitto. Primero, demuestra que la ruta de red funciona con el servidor MQTT público gratuito, que es anónimo:

mosquitto_sub -h broker.connectmq.com -p 1883 -i auth-check-2d9f \
  -t "connectmq/demo/2d9f/#" -d -C 1 -W 5

Si ves received CONNACK (0), tu equipo puede llegar al broker y hablar MQTT. A continuación, prueba tus credenciales privadas sobre TLS:

mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u 'YOUR_USERNAME' -P 'YOUR_PASSWORD' -i 'YOUR_CLIENT_ID' \
  -t "test/#" -d -V mqttv5

Con -V mqttv5, un rechazo imprime el reason code de MQTT 5, que es más específico que el código de 3.1.1. Si este comando se conecta pero tu aplicación no, el problema está en la configuración de la aplicación, no en la cuenta. Si este comando también falla, el problema son las credenciales, el client ID o la cuenta.

En Windows, --capath no está disponible de la misma forma; usa en su lugar --cafile con un archivo de bundle de CA, o prueba con un cliente MQTT gráfico.

Solucionarlo en el código

Este es un cliente Python que imprime la configuración que realmente está usando y se detiene ante errores de autorización en lugar de reintentar indefinidamente:

import os
import ssl
import paho.mqtt.client as mqtt

HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "8883"))
USERNAME = os.getenv("MQTT_USERNAME", "YOUR_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD", "YOUR_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", "YOUR_CLIENT_ID")

print(f"Connecting to {HOST}:{PORT} as user={USERNAME!r} client_id={CLIENT_ID!r}")

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Refused: {reason_code} (0x{reason_code.value:02X})")
        if reason_code.value in (0x85, 0x86, 0x87):
            print("Identity problem: check username, password and client ID")
            client.disconnect()
        return
    print("Connected")

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id=CLIENT_ID,
    protocol=mqtt.MQTTv5,
)
client.username_pw_set(USERNAME, PASSWORD)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.on_connect = on_connect
client.connect(HOST, PORT, keepalive=60)
client.loop_forever()

El formato !r imprime los valores entre comillas, lo que deja a la vista los espacios invisibles. Esa sola línea ha resuelto muchas investigaciones del tipo "mi contraseña es correcta, seguro".

En un ESP32 con PubSubClient, el error habitual está en la sobrecarga:

// Incorrecto: no se envían credenciales
mqtt.connect("YOUR_CLIENT_ID");

// Correcto: client ID, usuario y contraseña
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD");

Not Authorized después de conectar

A veces la conexión tiene éxito pero las operaciones posteriores fallan con "not authorized". En MQTT 5 verás 0x87 en un SUBACK o PUBACK, o en un DISCONNECT. Eso significa que el cliente puede conectarse pero no tiene permiso para usar un topic concreto, por ejemplo un prefijo reservado. Con MQTT 3.1.1 no hay reason code para una publicación rechazada, así que el mensaje puede simplemente desaparecer. Si los mensajes se esfuman, conéctate temporalmente con MQTT 5 para ver el reason code real. La referencia de reason codes de MQTT recoge todos los códigos por tipo de paquete.

Cómo funciona en ConnectMQ

En ConnectMQ, cada broker que creas en el portal recibe un usuario, una contraseña y un client ID generados, y el client ID está vinculado a esas credenciales. Eso es lo primero que hay que comprobar cuando ves 0x87: ¿estás usando los tres valores del mismo broker? Los brokers están aislados por inquilino, así que las credenciales de un broker no funcionan en otro, ni siquiera dentro de la misma cuenta.

Conviene conocer otros dos comportamientos de la plataforma:

  • Los inicios de sesión fallidos están limitados. Tras varios inicios de sesión fallidos, se bloquea la dirección IP de origen. Un dispositivo atascado en un bucle de reconexión rápido con credenciales caducadas puede dejar fuera a otros dispositivos que estén detrás del mismo NAT. Usa backoff exponencial.
  • Los límites de conexión dependen del plan. El plan Serverless gratuito permite hasta 25 conexiones simultáneas. Superarlo produce 0x97 Quota exceeded en los clientes MQTT 5, no "not authorized", lo que es una razón más para preferir MQTT 5 mientras depuras.

Para reforzar la seguridad más allá de la autenticación, consulta las buenas prácticas de seguridad MQTT, y el tutorial de MQTT para tener una visión más amplia del protocolo.

Conéctate de forma segura con ConnectMQ

Un broker MQTT gestionado que emite credenciales y client ID que encajan entre sí elimina la mayoría de las causas de este error antes de que se produzcan. Regístrate en app.connectmq.com para obtener un broker Serverless gratuito sin tarjeta de crédito y usa el cliente MQTT en el navegador para confirmar que tus credenciales funcionan antes de flashear un solo dispositivo. Descubre más sobre cómo usar un broker MQTT en la nube.

Preguntas frecuentes

¿Qué significa "Connection Refused: not authorised" en las herramientas de Mosquitto?

Es el código de retorno CONNACK 5. El broker recibió un CONNECT válido pero rechazó al cliente, normalmente por credenciales erróneas, un client ID no vinculado a la cuenta o credenciales de otro broker.

¿"Not authorized" es lo mismo que "bad username or password"?

No exactamente. Bad username or password es el código 4 (0x86) y significa que las credenciales no coincidían; not authorized es el código 5 (0x87) y abarca problemas de permisos más amplios. Algunos brokers usan el código 5 para ambos, así que, en cualquier caso, revisa primero las credenciales.

¿Por qué mi cliente se conecta con mosquitto_sub pero no desde mi aplicación?

La aplicación está enviando algo distinto: otro host, un client ID generado, credenciales que faltan o una contraseña alterada. Imprime la configuración efectiva en tiempo de ejecución y compárala con el comando que funciona.

¿Pueden causar problemas los inicios de sesión fallidos repetidos?

Sí. Muchos brokers, incluido ConnectMQ, bloquean direcciones IP tras varios inicios de sesión fallidos. Usa backoff exponencial y deja de reintentar ante errores de autorización.

Artículos relacionados

¿Quién inventó MQTT? La historia, de los oleoductos al IoT

MQTT lo inventaron en 1999 Andy Stanford-Clark, de IBM, y Arlen Nipper, de Arcom, para monitorizar oleoductos vía satélite. Sigue su camino hacia el open source, OASIS, ISO y los brokers MQTT gestionados en la nube de hoy.

8 min de lectura

Por qué tu cliente MQTT se desconecta constantemente (y cómo solucionarlo)

Los bucles de reconexión son uno de los problemas más habituales de MQTT. Conoce las cinco causas típicas, desde los client ID duplicados hasta los timeouts de keep alive y los paquetes demasiado grandes, y sigue un flujo de diagnóstico que localiza la tuya.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.