openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
183 lines (166 loc) • 11.1 kB
Markdown
---
summary: "Handshake auth paths, device identity, pairing signatures, and TLS pinning"
read_when:
- Choosing an auth path for a gateway client
- Implementing device identity or signed pairing
- Pinning a gateway TLS certificate
title: "Gateway protocol auth"
sidebarTitle: "Auth and device identity"
doc-schema-version: 1
---
How a client proves who it is: the handshake auth paths, device identity and pairing signatures, and TLS pinning.
## Auth
Owner page: [Gateway authentication](/gateway/authentication) — auth modes, token/password setup, and the operator-facing policy this wire contract enforces.
- Shared-secret gateway auth accepts the configured secret in either
`connect.params.auth.token` or `connect.params.auth.password`.
`gateway.auth.mode: "token"` selects `gateway.auth.token`; `"password"`
selects `gateway.auth.password`. The mode selects the configured secret,
not the required wire field.
- Identity-bearing modes such as Tailscale Serve (`gateway.auth.allowTailscale: true`)
or non-loopback `gateway.auth.mode: "trusted-proxy"` satisfy the connect
auth check from request headers instead of `connect.params.auth.*`.
- Private-ingress `gateway.auth.mode: "none"` skips shared-secret connect auth
entirely; do not expose that mode on public/untrusted ingress.
- After pairing, the gateway issues a device token scoped to the connection
role + approved grant, returned in `hello-ok.auth.deviceToken`. Clients should
persist it with `hello-ok.auth.scopes` after a successful connect when the token
is new or different from the stored token.
- `hello-ok.auth.scopes` is the current socket's live authority and matches the
scopes enforced by RPC dispatch.
- When `hello-ok.auth.deviceToken` exactly matches the token already stored for
the same gateway, device, client, and role, preserve that record's stored scopes
instead of replacing them with a narrower live scope set. A newly issued or
rotated token uses `hello-ok.auth.scopes`; its approved grant matches that
connection when it is issued.
- Reconnecting with that stored device token should also reuse the stored
approved scope set for that token. This preserves read/probe/status access
already granted and avoids silently collapsing reconnects to a narrower
implicit admin-only scope.
- Client-side connect auth assembly (`selectConnectAuth` in
`packages/gateway-client/src/client.ts`):
- `auth.password` is always forwarded when set. Either shared-secret field
can carry the configured secret; when both are supplied, the Gateway
uses the field matching its auth mode.
- `auth.token` is populated in priority order: explicit shared token first,
then an explicit `deviceToken`, then a stored per-device token (keyed by
`deviceId` + `role`).
- `auth.bootstrapToken` is sent only when none of the above resolved
`auth.token`. A shared token or any resolved device token suppresses it.
- Auto-promotion of a stored device token on the one-shot
`AUTH_TOKEN_MISMATCH` retry is gated to trusted endpoints only: loopback,
or `wss://` with a pinned `tlsFingerprint`. Public `wss://` without pinning
does not qualify.
- Built-in setup-code bootstrap returns the primary node
`hello-ok.auth.deviceToken` plus a bounded operator token in
`hello-ok.auth.deviceTokens` for trusted mobile handoff. The operator token
includes `operator.talk.secrets` for native Talk configuration reads, but
excludes pairing-mutation scopes and `operator.admin`.
- `hello-ok.auth.deviceTokens` contains only additional bootstrap-handoff tokens.
Do not use it as metadata for the primary `deviceToken` reconnect record.
- While a non-baseline setup-code bootstrap waits for approval,
`PAIRING_REQUIRED` details include `recommendedNextStep: "wait_then_retry"`,
`retryable: true`, and `pauseReconnect: false`. Keep reconnecting with the
same bootstrap token until the request is approved or the token becomes
invalid.
- Persist `hello-ok.auth.deviceTokens` only when the connect used bootstrap
auth on a trusted transport such as `wss://` or loopback/local pairing.
- If a client supplies an explicit `deviceToken` or explicit `scopes`, that
caller-requested scope set remains authoritative for the live connection and
is reported in `hello-ok.auth.scopes`; cached token-grant scopes are only reused
when the client is reusing the stored per-device token.
- Device tokens can be rotated/revoked via `device.token.rotate` and
`device.token.revoke` (requires `operator.pairing`). Rotating or revoking a
node or other non-operator role also requires `operator.admin`.
- `device.token.rotate` returns rotation metadata. It echoes the replacement
bearer token only for same-device calls already authenticated with that
device token, so token-only clients can persist their replacement before
reconnecting. Shared/admin rotations do not echo the bearer token.
- Token issuance, rotation, and revocation stay bounded to the approved role
set recorded in that device's pairing entry; token mutation cannot expand or
target a device role that pairing approval never granted.
- For paired-device token sessions, device management is self-scoped unless
the caller also has `operator.admin`: non-admin callers can manage only the
operator token for their own device entry. Node and other non-operator token
management is admin-only, even for the caller's own device.
- `device.token.rotate` and `device.token.revoke` also check the target
operator token scope set against the caller's current session scopes.
Non-admin callers cannot rotate or revoke a broader operator token than they
already hold.
- Auth failures include `error.details.code` plus recovery hints:
- `error.details.canRetryWithDeviceToken` (boolean)
- `error.details.recommendedNextStep`: one of `retry_with_device_token`,
`update_auth_configuration`, `update_auth_credentials`,
`wait_then_retry`, `review_auth_configuration`
(`packages/gateway-protocol/src/connect-error-details.ts`).
- Client behavior for `AUTH_TOKEN_MISMATCH`:
- Trusted clients may attempt one bounded retry with a cached per-device
token.
- If that retry fails, stop automatic reconnect loops and surface operator
action guidance.
- `AUTH_SCOPE_MISMATCH` means the device token was recognized but does not
cover the requested role/scopes. Do not present this as a bad token; prompt
the operator to re-pair or approve the narrower/broader scope contract.
- `OPERATOR_ACCESS_DENIED` means the person authenticated, but the Gateway's
operator access policy (for example, a role bound to an `accessPolicyPlugin`)
currently grants no access. This is not a credential problem. Keep reconnecting
with backoff so newly granted access applies without user action, and show
administrator guidance to assign a role or grant access.
## Device identity and pairing
Owner page: [Gateway pairing](/gateway/pairing) — the approval flow, device records, and CLI surfaces behind these fields.
- Nodes should include a stable device identity (`device.id`) derived from a
keypair fingerprint.
- Gateways issue tokens per device + role.
- Pairing approvals are required for new device IDs unless local
auto-approval is enabled.
- If approval overlaps a reconnect, the Gateway checks the current paired device
before completing the handshake. The approved key, role, scopes, and pinned
client metadata must authorize that connection; a consumed request alone does
not grant access.
- Pairing auto-approval is centered on direct local loopback connects.
- OpenClaw also has a narrow backend/container-local self-connect path for
trusted shared-secret helper flows.
- Same-host tailnet or LAN connects are still treated as remote for pairing
and require approval.
- WS clients normally include `device` identity during `connect` (operator +
node). The only device-less operator exceptions are explicit trust paths:
- successful `gateway.auth.mode: "trusted-proxy"` operator Control UI auth.
- direct-loopback `gateway-client` backend RPCs on the reserved internal
helper path.
- Omitting device identity has scope consequences. When a device-less
operator connection is allowed through an explicit trust path, OpenClaw
still clears self-declared scopes to an empty set unless that path has a
named scope-preservation exception. Scope-gated methods then fail with
`missing scope`.
- The reserved direct-loopback `gateway-client` backend helper path preserves
scopes only for internal local control-plane RPCs; custom backend IDs do
not receive this exception.
- All connections must sign the server-provided `connect.challenge` nonce.
### Device auth migration diagnostics
For legacy clients that still use pre-challenge signing behavior, `connect`
returns `DEVICE_AUTH_*` detail codes under `error.details.code` with a stable
`error.details.reason`.
Common migration failures:
| Message | details.code | details.reason | Meaning |
| --------------------------- | -------------------------------- | ------------------------ | -------------------------------------------------- |
| `device nonce required` | `DEVICE_AUTH_NONCE_REQUIRED` | `device-nonce-missing` | Client omitted `device.nonce` (or sent blank). |
| `device nonce mismatch` | `DEVICE_AUTH_NONCE_MISMATCH` | `device-nonce-mismatch` | Client signed with a stale/wrong nonce. |
| `device signature invalid` | `DEVICE_AUTH_SIGNATURE_INVALID` | `device-signature` | Signature payload does not match v2 payload. |
| `device signature expired` | `DEVICE_AUTH_SIGNATURE_EXPIRED` | `device-signature-stale` | Signed timestamp is outside allowed skew. |
| `device identity mismatch` | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch` | `device.id` does not match public key fingerprint. |
| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key` | Public key format/canonicalization failed. |
Migration target:
- Always wait for `connect.challenge`.
- Use `connect.challenge.payload.ts` as `connect.params.device.signedAt`.
- Sign the v2 payload that includes the server nonce.
- Send the same nonce in `connect.params.device.nonce`.
- Preferred signature payload is `v3`
(`buildDeviceAuthPayloadV3` in `packages/gateway-client/src/device-auth.ts`),
which binds `platform` and `deviceFamily` in addition to
device/client/role/scopes/token/nonce fields.
- Legacy `v2` signatures remain accepted for compatibility, but paired-device
metadata pinning still controls command policy on reconnect.
## TLS and pinning
Owner page: [Remote access](/gateway/remote) — configuring `gateway.tls` and obtaining the fingerprint clients pin.
- TLS is supported for WS connections (`gateway.tls` config).
- Clients may optionally pin the gateway cert fingerprint via
`gateway.remote.tlsFingerprint` or CLI `--tls-fingerprint`.