Skip to main content

Realtime authentication

Realtime supports a full auth ladder — from fully open to enterprise SSO — so you pick the level that fits each interface. The organisation API key is a backend-only credential; never ship it to a browser.

ModeHowUse when
Open / publicpublic: true on the interface — no credentialNon-sensitive public feeds (scores, status, prices)
Air Pipe issues the tokenUsers log in through an Air Pipe interface; add_jwt mints a JWTYou have no identity provider
Your JWT — shared secretClient sends its JWT; verified with is_valid_jwt (HS256)You have a backend, no IdP
Your JWT — JWKSVerified against your IdP's JWKS (RS256/ES256)You already have Auth0/Cognito/etc.
Backend API keyAuthorization: Bearer <key> (server-to-server)Trusted backend publishing

How end-users present a credential

  • WebSocket, browser: ?token=<jwt> on the URL — browsers can't set WebSocket headers.
  • WebSocket, native: the airpipe-jwt header (or ?token=).
  • Backend (WS): the org API key via Authorization: Bearer, x-api-key, or ?api_key=.
  • MQTT: username = <ORG_UUID>, password = <API_KEY> — or no password for public.

The interface authenticates the user in-pipeline (an is_valid_jwt assert), and channel access is gated by subscribe_authorizer — so the ACL is your pipeline logic on your claims.

Open / public

public: true lets a client connect with no credential at all — the Ably/Pusher "public channel" model. Safe on shared infra: the per-org abuse floors still bound a public interface, and (managed) the connection stays scoped to your org.

feed:
ws: "on"
public: true
actions:
- name: Ping
input: a|body|

Public MQTT works the same way: an anonymous MQTT client (no password) may pub/sub only to public: true interfaces — never a private topic (enforced fail-closed at subscribe and publish). On managed hosts pass username = <ORG_UUID> with no password so the stream is scoped + accounted to your org; pure self-hosted allows fully anonymous (single-tenant, like EMQX allow_anonymous).

Air Pipe issues the token

No IdP? Log users in through an Air Pipe interface and mint a JWT with add_jwt; Air Pipe verifies its own token on the socket.

login:
output: http
method: POST
actions:
- name: Validate # replace with a real credential check (DB lookup + bcrypt)
input: a|body|
assert:
tests: [{ value: username, is_not_null: true }, { value: password, is_not_null: true }]
- name: Mint
run_when_succeeded: { actions: [Validate] }
input: a|Validate|
post_transforms:
- add_jwt: { key: token, secret: a|ap_var::JWT_SECRET|, exp: 24h, data: [username] }

The browser opens wss://…/ws/<org>/production/live?token=<the token>; authorize-channel verifies it with the same secret.

Your JWT — shared secret

The client brings a JWT signed with a secret you store in Air Pipe:

authorize-channel:                # internal authorizer — run by the socket, not an HTTP route
actions:
- name: Verify
input: a|headers
assert:
http_code_on_error: 403
tests:
- value: airpipe-jwt
is_valid_jwt: a|ap_var::JWT_SECRET|

Your JWT — JWKS

For an enterprise IdP, swap the assert to the JWKS form (RS256/ES256) — no other change:

          - value: airpipe-jwt
is_valid_jwt:
jwks_url: a|ap_var::JWKS_URL|

Next: Limits & entitlements →