Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
Assigneesstack72

Relationships

↑ child of #662

#685 serve-auth: mode: token — server token authentication on WebSocket connections

Opened by stack72 · 6/18/2026· Shipped 6/19/2026

Parent

Sub-issue of #662 (serve authentication & authorization). Layer 4, item 2.

Dependencies

  • ✅ #673 — Server token model (mint/redeem/revoke/expire)
  • ✅ #674 — Client credential storage (ServerCredentialRepository)
  • ✅ #682 — Auth config schema (`mode: token`)
  • ✅ #683 — Admin materialization

Summary

When `swamp serve` runs with `--auth-mode token`, WebSocket connections must present a server token to authenticate. This is the simplest authentication mode — no OAuth provider, no browser flow. An admin mints tokens on the server, distributes them to users, and users present them when connecting.

This completes the authentication loop: the server knows WHO is connecting and can stamp the connection with a principal identity.

The flow

Admin mints a token (on the server host)

```bash swamp access token mint --name adam-token --principal user:adam → Token: adam-token.a1b2c3d4e5f6... Give this to the user. It cannot be retrieved again. ```

This creates a ServerToken model instance (#673) — stores the hash in a vault, returns the plaintext once.

User connects with the token

```bash swamp model method run hello execute \ --input 'run=echo hello' \ --server wss://swamp.acme.internal:9090 \ --token adam-token.a1b2c3d4e5f6... ```

Or store the token for reuse: ```bash

Save the token for this server

swamp auth server-login --server wss://swamp.acme.internal:9090 \ --token adam-token.a1b2c3d4e5f6...

Now --server uses the stored token automatically

swamp model method run hello execute \ --input 'run=echo hello' \ --server wss://swamp.acme.internal:9090 ```

Server authenticates the connection

On WebSocket upgrade:

  1. Client presents the token (in a header or as the first frame)
  2. Server splits `.`, looks up the ServerToken model instance by name
  3. Calls the `redeem` method — validates via timing-safe comparison, checks not expired/revoked, updates `lastUsedAt`
  4. On success, issues a session credential via the existing `SessionCredentialService` (same as worker enrollment)
  5. Stamps the connection with the principal (`user:` from the token record)

Unauthenticated connections are rejected

When `mode: token`, any WebSocket connection that doesn't present a valid token is rejected before it can send any frames. When `mode: none`, authentication is skipped (today's behavior preserved).

What to build

1. Token presentation on connection

Extend the WebSocket connection handler in `src/serve/connection.ts` to:

  • Check `authConfig.mode` from ConnectionContext
  • If `token`: require token presentation, validate, stamp connection with principal
  • If `none`: skip authentication (existing behavior)

The token can be presented as:

  • A query parameter on the WebSocket URL: `wss://host:9090/?token=name.secret`
  • Or as the first message after connection (a pre-auth frame)

The URL query parameter is simpler — the WebSocket API supports it natively and no protocol changes are needed. The server reads it during the upgrade.

2. Principal on connection

After successful authentication, the connection carries a `Principal` (`user:`). This is passed to downstream handlers so they know who's acting. The `ConnectionContext` (or a per-connection object) gains the authenticated principal.

3. `swamp access token mint` CLI command

A new command for admins to mint server tokens:

```bash swamp access token mint --name --principal user:id [--duration-days ] ```

Calls the ServerToken model's `mint` method (#673). Outputs the plaintext token once. Default duration 30 days.

4. `swamp access token list` CLI command

List active server tokens (without secrets):

```bash swamp access token list → NAME PRINCIPAL CREATED EXPIRES LAST USED adam-token user:adam 2026-06-18 2026-07-18 2026-06-18 ```

5. `swamp access token revoke` CLI command

```bash swamp access token revoke adam-token ```

Calls the ServerToken model's `revoke` method. Takes effect immediately — the next request from that token is rejected.

6. Client-side token storage

When connecting with `--server`, the CLI looks up a stored credential via `ServerCredentialRepository` (#674). If `--token` is explicitly provided, it takes precedence. The `swamp auth server-login` command stores the token for a server URL.

7. Session credential issuance

After token validation, the server issues a session credential via `SessionCredentialService` — the same mechanism workers use after enrollment. The session credential is short-lived (15 min, sliding window) and used for subsequent data plane requests. This reuses all existing session machinery.

Scope

  • Token presentation and validation on WebSocket connections
  • Principal stamping on authenticated connections
  • `swamp access token mint/list/revoke` CLI commands
  • `--token` flag on `--server` commands
  • `swamp auth server-login` for storing tokens
  • Client-side credential lookup from ServerCredentialRepository
  • Session credential issuance after token validation
  • Tests for all paths

Out of scope

  • OAuth login flow — layer 4, item 3
  • Authorization enforcement (checking what the principal can DO) — layer 5
  • `can-i` command — layer 5

References

  • Server token model: `src/domain/models/access/server_token_model.ts`
  • Client credential storage: `src/domain/auth/server_credential.ts`, `src/infrastructure/persistence/server_credential_repository.ts`
  • Session credential service: `src/domain/remote/session_credential.ts`
  • Worker enrollment (pattern to follow): `src/serve/worker_gateway.ts`
  • Connection handler: `src/serve/connection.ts`
  • Auth config: `src/domain/access/serve_auth_config.ts`
  • Remote run (client side): `src/cli/remote_run.ts`
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

6/19/2026, 9:41:27 AM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack726/18/2026, 11:48:15 PM
stack72 linked parent of #6626/18/2026, 10:55:19 PM

Sign in to post a ripple.