KubeMQ
ConnectorsMQTTHow-to guides

Authentication

How an MQTT client authenticates to KubeMQ — password-as-JWT in the CONNECT packet, ClientID as identity, ACL authorization, and the open no-auth default.

This guide explains how a native MQTT client proves who it is to the KubeMQ MQTT connector and what it may do once connected. MQTT carries the credential in the standard Password field of the CONNECT packet — there is no MQTT-specific auth flag — so any off-the-shelf MQTT 3.1.1 / 5.0 client authenticates by setting username/password the way it already does.

On a stock dev broker, authentication is off — every CONNECT succeeds, the Password field is ignored, and ACL checks are skipped. So the examples clone-and-run with no credentials. To turn auth on, configure an auth provider at the server level (there is no MQTT-specific auth env var); see Auth & security.

Password-as-JWT

The connector uses password-as-JWT authentication. The MQTT Password field carries a KubeMQ JWT; the Username field is accepted but is display-only — it is stored for the connections endpoint and never checked against the token.

MQTT CONNECT fieldRoleNotes
PasswordThe credential — a KubeMQ JWTMust be set when auth is on; the MQTT spec requires PasswordFlag=true when a password is present
UsernameDisplay / audit labelSurfaced on the connections endpoint; never validated against the token
ClientIDThe identityBecomes the KubeMQ ClientID; used for ACL checks and the $reply/<clientID>/... reply namespace

A CONNECT therefore looks like this — the JWT goes in the password slot, not the username:

CONNECT
  ClientID:     my-client          ← the identity
  Username:     alice              ← display label only
  Password:     <KubeMQ JWT>       ← the actual credential
  PasswordFlag: true

Authentication is checked once, at CONNECT time only. There is no mid-connection token recheck — revoking a token does not disconnect an already-authenticated session.

Connecting with a JWT

Put the JWT in the password slot; the username is cosmetic. The same code works against an auth-disabled broker (the server simply does not validate the token).

// paho.mqtt.golang (MQTT 3.1.1) — username is display-only, password is the KubeMQ JWT.
opts := mqtt.NewClientOptions().
    AddBroker("tcp://broker:1883").
    SetClientID("my-client").
    SetUsername("alice").                       // display-only
    SetPassword(os.Getenv("KUBEMQ_MQTT_JWT"))   // KubeMQ JWT token
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
    log.Fatalf("connect (bad/expired JWT? auth-enabled broker?): %v", token.Error())
}
# paho-mqtt (MQTT 5.0) — username is display-only, password is the KubeMQ JWT.
import os
import paho.mqtt.client as mqtt
from paho.mqtt.enums import CallbackAPIVersion

client = mqtt.Client(
    callback_api_version=CallbackAPIVersion.VERSION2,
    client_id="my-client",
    protocol=mqtt.MQTTv5,
)
client.username_pw_set("alice", os.environ["KUBEMQ_MQTT_JWT"])  # (display, JWT)
client.connect("broker", 1883, keepalive=30)
// mqtt.js (MQTT 5.0) — username is display-only, password is the KubeMQ JWT.
import * as mqtt from "mqtt";

const client = mqtt.connect("tcp://broker:1883", {
  protocolVersion: 5,
  clientId: "my-client",
  username: "alice",                      // display-only
  password: process.env.KUBEMQ_MQTT_JWT,  // KubeMQ JWT token
  clean: true,
});

CONNACK reason codes

What the broker returns at CONNECT time depends on whether auth is enabled and whether the JWT is valid:

ScenarioPacketReason code
Auth disabled — any password acceptedCONNACK0x00 success
Auth enabled — valid JWTCONNACK0x00 success
Auth enabled — empty password (or PasswordFlag=false)CONNACK0x86 bad username/password
Auth enabled — invalid JWT or bad signatureCONNACK0x86 bad username/password

When auth is enabled, a CONNECT with an empty Password is refused immediately with 0x86; the connection is never established. Clients must always supply the token in the Password field.

ACL authorization

Once connected, every publish and subscribe is checked against the KubeMQ ACL. Rules are evaluated per (pattern, channel, read/write) tuple, against the identity derived from the ClientID.

OutcomePacketReason code
Allowed (publish)PUBACK0x00
Allowed (subscribe)SUBACK0x01 (granted QoS)
Denied on publishPUBACK0x87 not authorized
Denied on subscribeSUBACK0x87 not authorized

Two namespace rules sit alongside the ACL:

  • $reply/<clientID>/<suffix> — a client's own reply namespace is always allowed, regardless of ACL rules. This is the local topic an RPC requester subscribes to for responses; see Topic mapping.
  • Other $-prefixed topics (except $share/) are denied by default.

Authorization is checked at CONNECT for the password, then at each publish/subscribe for the ACL — but never again on an open subscription. A denied publish returns PUBACK 0x87; treat it as a permission error, not a transient failure to retry.

Open (no-auth) default

When no auth provider is configured the broker is open: the Password field is ignored, every CONNECT succeeds, and ACL checks are skipped. This matches KubeMQ's gRPC and REST parity behaviour and is the mode every example in the docs assumes.

To enable authentication, configure the KubeMQ server with an auth provider (the shared server-level auth block) — no MQTT-specific environment variable controls auth. Because it is a shared setting, the same JWT model applies across all KubeMQ connectors; see Auth & security.

Quick decision guide

You want…Do this
Clone-and-run on a stock dev brokerConnect with no credentials (auth is off)
Authenticate with a KubeMQ identityPut the JWT in the Password field; Username is cosmetic
Set a stable identity for ACL / RPC repliesSet a meaningful ClientID — it is the identity, not the username
Diagnose a rejected CONNECTLook for CONNACK 0x86 (bad/empty JWT)
Diagnose a rejected publish/subscribeLook for PUBACK / SUBACK 0x87 (ACL deny)

Was this page helpful?

On this page