MQTT.Agent - open protocol for AI agents

Zurück zum Blog
Auth Security

Auth Without the JWT Headache

How short-lived MQTT credentials remove the rotation, revocation, and refresh problems baked into long-lived JWTs.

January 14, 2026
6 Min. Lesezeit
By CloudSignal Team

What’s hard about JWTs for real-time

The standard pattern for real-time auth looks reasonable on paper. Your server signs a JWT, the client connects with it as a Bearer token, and your gateway verifies the signature. Then you try to operate it.

Revocation is the first gap. A signed JWT is valid until it expires, period. If a user leaves, a session is hijacked, or you ship a bug, you cannot pull the token back. You either keep a revocation list and check it on every connect (which is just a session store with extra steps), or you accept that compromised tokens stay good for hours.

Refresh on a persistent socket is the second gap. HTTP middleware handles refresh tokens fine because every request is a fresh authorization moment. A live MQTT connection is not. Do you tear down and reconnect when the access token rotates? Do you run a sidecar endpoint that issues new tokens and somehow signals the broker? Every answer here is awkward.

Key rotation is the third gap. Rotate your signing key and every active session breaks unless you implement key IDs and run two verifiers in parallel during the cutover. Most teams ship one, never rotate, and hope.

Our model: short-lived MQTT credentials

We took a different path. Your backend holds a server API key (the sk_ kind, scoped to your organisation). When a user starts a session, your backend exchanges that API key for a short-lived MQTT username and password, scoped to that user and that organisation, with a 60-minute TTL by default.

The client never sees the API key. The client just gets a username like user_xyz@org_k7xm4pqr2n5t and a password that starts with tkn_, and it connects to the broker over WSS or MQTTS exactly like every MQTT client library has done for the last decade. Username and password. No bearer header, no token verifier, no key ID negotiation.

The broker authenticates the credential against our store on connect. The credential carries the per-user scoping. When the TTL is up, the credential is gone. There is no parallel JWT lifecycle to reason about. The unit of authorisation is the credential itself.

The exchange

The server-side exchange is one HTTP call. Your backend holds the API key and hands back what the client needs:

// Server-side handler: exchange API key for a short-lived MQTT credential
const res = await fetch('https://api.cloudsignal.app/v2/tokens', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CLOUDSIGNAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ user_id: session.user.id, ttl_seconds: 3600 }),
})
const { username, password, expires_at } = await res.json()

The API key never leaves the server. Only the resulting credential reaches the browser or device, and it carries a fixed expiry. If it leaks from a client, the blast radius is bounded by that expiry. If it leaks from your server, you have bigger problems and rotating the API key cuts off new issuance immediately.

Why 60 minutes

Sixty minutes is a deliberate choice, not a hedge. Shorter TTLs sound safer until you try to operate them on mobile. A phone in your pocket sits in background for half an hour at a time, then wakes up and tries to publish. If your credential expired ten minutes ago, your app pays a reconnect every time the user comes back to it. The user sees a stutter. Your support inbox sees the complaint.

Longer TTLs sound friendlier until you imagine a credential extracted from a compromised browser profile or a misconfigured log line. A week-long credential is a week of exposure. An eight-hour credential is most of a working day.

Sixty minutes lands in the middle on purpose. It covers a typical user session without a forced rotation. It bounds the damage from a leak to under an hour, automatically, with no revocation API call required. Credentials die on their own. That property is the entire point.

For workloads where you want a different number, the TTL is configurable per-token at issue time. Sixty is the default because it is the right starting place for human-driven sessions.

What this doesn’t solve

We want to be honest about the boundary. This model removes a class of JWT pain. It does not remove every auth problem.

The API key on your server is still your problem to protect. Put it in a vault, rotate it on a schedule, and never commit it. If your backend gets compromised, an attacker can mint credentials for any user in your organisation until you rotate. Short-lived credentials shrink client-side blast radius, not server-side blast radius.

Per-user rate limiting layers on top of authentication, it does not replace it. A valid credential can still be used abusively. We enforce per-organisation rate limits at the broker, and you should enforce per-user quotas at your application layer if a single user can plausibly flood a topic.

Cross-organisation permissions for power users (the customer-service rep who needs to look at three tenants in one shift) are not in scope of the credential itself. The credential is scoped to one org. Your application is the right place to decide which org-scoped credential to mint for which user at which moment.

What we want to add next

The next step we are thinking about is per-resource scoping on the credential itself. Today a credential is bounded by user, organisation, and time. We want to make it possible to mint a credential bounded by conversation, document, or workflow run, so that even if a credential is leaked it can only touch the single resource it was issued for, not the whole organisation slice the user is allowed to see.

The shape we are exploring is an optional scope expression at issue time, enforced at the broker on every publish and subscribe. We are not committing dates. We are committing that this is the direction, because the property that makes 60-minute credentials worth using (a leak dies on a clock, by itself, without a revocation API) gets sharper the smaller the scope of each credential.

Ready to get started?

Try CloudSignal free and connect your first agents in minutes.

Start Building Free