Authentication

Every connection authenticates with a token in its first frame. kraken does not decide by itself whether a token is good: it asks its auth backend, caches the answer, and enforces the grants that come back on every subscribe and publish.

BackendConfigure withTokensGood for
Static fileAUTH_BACKEND=static (the default)Any string you list in auth.jsonDevelopment, and small deployments with a fixed set of clients
Your own serviceAUTH_BACKEND=http, AUTH_HTTP_URLWhatever your service acceptsPlugging kraken into an existing user store
@nolag/coreAUTH_BACKEND=http, AUTH_HTTP_URL pointing at coreActor tokens and client tokens (JWTs)The full model: projects, apps, rooms, grants, access scopes and signing keys

The second and third are the same mechanism: @nolag/core is one implementation of kraken's HTTP auth contract.

Connecting with a token

The token is the first argument to the client. The SDK sends it in the auth frame as soon as the WebSocket opens.

import { NoLag } from "@nolag/js-sdk";

const client = NoLag("your_token", { url: "ws://localhost:8080/ws" });
await client.connect();

console.log("connected as", client.actorId);

A refused token fails connect() with the reason kraken gave, for example access_denied when the backend said no, authentication_failed when an HTTP backend answered with an error status, connection_failed when kraken could not reach it, or token_expired for an expired client token. The Wire Protocol lists them all.

Static tokens

With the default backend, tokens are the keys of a JSON file, and each entry carries the actor's identity and its grants:

{
  "tokens": {
    "dev-token-bob": {
      "actorTokenId": "bob",
      "actorType": "user",
      "allowedTopics": [
        { "pattern": "demo/general/#", "permission": "pubSub", "app_id": "demo-app", "app_name": "demo" }
      ]
    }
  }
}

A token can be any string, so use long random values. The file format, reloading and revocation are covered in Static Auth File.

Actor tokens (@nolag/core)

With @nolag/core, every identity that connects is an actor, and each actor has one access token:

at_live_<12 hex characters>.<43 base64url characters>
  • The part before the dot is the actor's public key id (for example at_live_43365b058a2e). It is safe to log and to store in your own database.
  • The part after the dot is the secret. Core stores only its SHA-256 hash, so the full token is shown once, when the actor is created, and cannot be recovered afterwards. Lose it and you create the actor again.
  • Actors are created by importing a project document, which returns every minted token once, or by your own host through core's ActorTokenFacade.
  • An actor has a status (active or disabled), an optional expiry, an optional access scope, and grants to apps and rooms.

After a successful connect, client.actorId holds the actor's id inside core (a UUID), not the key id.

Client tokens

Actor tokens are long-lived, so they belong on servers and devices you control. For a browser or a mobile app, your backend signs a short-lived JWT naming the actor, using a project signing key, and the client connects with that instead. The permissions are still the actor's. See Client Tokens.

Actor types

Every actor has a type. Core accepts seven:

actorTypeTypical use
deviceBrowsers, mobile apps, IoT hardware
userAuthenticated end users
serviceBackend services
sessionShort-lived or temporary connections
agentLong-running workers
orchestratorProcesses that hand work out to workers
observerRead-only monitoring connections

Permissions come from grants, not from the type, with two exceptions:

  • In @nolag/core, a room type grant names an actor type, and gives every actor of that type access to the room. The quickstart's vip room is private to service actors this way.
  • Core marks agent and orchestrator actors as wanting a persistent broker session, with the project's session expiry. kraken passes that request only to the MQTT broker backend, as a non-clean MQTT session; what the external broker keeps for a disconnected session is then up to it. The default syn broker ignores the request, so on a default kraken every actor type connects the same way.

With the static file, actorType is a label that defaults to user. kraken reports it back in the auth reply and otherwise ignores it.

Caching and revalidation

kraken keeps two kinds of state about a token, and both explain behaviour you will see:

A 30 second cache of successful validations, keyed on the token. A new connection with a token kraken validated in the last 30 seconds does not reach the backend. Refusals are not cached. So a token you add works at once, and a token you revoke can still open new connections for up to 30 seconds.

A copy of the grants in each connection, taken at connect time. Every subscribe and publish is checked against that copy, without calling the backend. The copy is refreshed by revalidation: when a connection sends a heartbeat (the SDKs send one every 30 seconds) and at least 10 minutes have passed since the last validation, kraken asks the backend again, by actor id.

  • If the actor is still valid, its grants are replaced with the fresh ones, so rooms and grants added since it connected become usable.
  • If the actor is no longer valid, kraken closes the connection with code 4001 and the reason as the close reason. From @nolag/core the reasons are token_not_found, token_revoked (the actor was disabled), token_expired, project_not_found and scope_inactive. From the static file, an actor whose entry was removed gets token_revoked.
  • If the backend cannot be reached or returns an error status, the connection stays up and kraken tries again at the next heartbeat.
  • If the refreshed answer puts the connection over the connection limit for its organization_id, kraken closes it with code 4002.

So revoking an actor takes effect for live connections within about ten minutes, not immediately.

With an HTTP backend there is one shortcut, and @nolag/core implements it. A subscribe to a topic that is not in the connection's copy of the grants (typically a room created after the actor connected) makes kraken ask the backend once whether access is now allowed. A publish has no such shortcut: it fails with unknown_topic until the next revalidation or a reconnect. See the HTTP auth contract.

Transport security

kraken listens for plain ws:// and http:// only. Tokens travel in the first frame, so outside your own machine always put kraken behind a reverse proxy that terminates TLS, and connect clients with wss://. See the Production Checklist.

Good practice

  • Keep long-lived tokens off untrusted clients. Static tokens and actor tokens go on servers and devices you control. Browsers and mobile apps get client tokens.
  • Grant narrowly. Give each token only the topics it needs, with subscribe or publish rather than pubSub where one direction is enough.
  • Rotate. With the static file, add the new token, move clients over, then delete the old entry. With core, create a new actor and disable the old one.
  • Never run AUTH_ALLOW_ALL=true outside local development. It accepts any token and grants every topic.

Next steps