Auth models
RIVR authenticates three kinds of principal. Every API surface derives identity server-side from one of these — client-supplied identity, owner, signer, or wallet is never trusted to bypass server derivation.
Verified-principal rule
RIVR's correct trust primitive is Ed25519-signed events plus a peer registry.
Every feature routes identity through one of the three principals below; none
accepts an ambient claim (a header, a client-named scope_id, a caller-named
callback) as authority.
1. Session Session
The default for the browser app. A NextAuth v5 session cookie establishes the
signed-in user; server components and route handlers read it via auth() and
derive the acting agent id from the session — never from request input.
- Used by: all interactive pages, most
/api/**write routes, and MCP tools markedenabledFor: ["session"](e.g. account data export, which returns the user's complete personal data set and is intentionally session-only). - Credential authority: password verification is delegated to
app.rivr.social/api/federation/sso/issuewith a local bcrypt fallback, so sovereign instances can verify a global identity without holding its password.
2. MCP token MCP Token
A Bearer token presented to the MCP endpoint (POST /api/mcp). This is how
agents and automations act. authorizeMcpRequest tries, in order: a scoped
MCP token, the static AIAGENT_MCP_TOKEN, then a session — so a stale bearer
never masks a valid session. Either token path authorizes an act-as
context: the tool call runs as a specific actor/controller, and every invocation
is written to the MCP provenance log (rivr.audit.recent).
- Scoped token (the normal path):
POST /api/mcp/tokenissues it to a signed-in user (optionally scoped to a persona they own). Itsjtiis recorded inmcp_tokens, soPOST /api/mcp/token/revokecan revoke that one token without invalidating the user's others. - Static token:
AIAGENT_MCP_TOKENis the legacy internal/autobot path. It can only act as the instance's primary agent or a persona that agent owns. - Used by: MCP tools marked
enabledFor: ["session", "token"]— every tool exceptrivr.account.export_data, which returns the user's complete personal data set and is intentionally session-only. See the MCP tool reference for per-tool badges. - Discovery:
/.well-known/mcp,/.well-known/oauth-authorization-server,/.well-known/oauth-protected-resource, and/llms.txtadvertise the endpoint. - Device flow (RFC 8628):
POST /api/mcp/device/codereturns a user code, the human approves it at the/mcp/authorizepage (which posts to/api/mcp/device/approve), and the client pollsPOST /api/mcp/device/tokenfor the minted token. - OAuth 2.1 flow:
POST /api/mcp/oauth/register(RFC 7591 dynamic client registration) → consent at/mcp/oauth/authorize, which posts to/api/mcp/oauth/authorizeto mint the code →POST /api/mcp/oauth/tokenexchanges it (PKCE-verified, rotating refresh tokens). - Scope: a token call can only invoke tools whose
enabledForincludes"token"and that its granted scopes allow. Scopes use therivr.<area>.<read|write>vocabulary (src/lib/mcp/scopes.ts), with value movement and data egress carried by separately-grantable scopes —rivr.wallet.spend,rivr.wallet.read,rivr.account.export,rivr.admin— so "read my profile" can never imply "spend my wallet". Enforcement (scopesAllowTool) reads the scopes from themcp_tokensrow, not the JWT, so a grant stays narrowable and revocable after issuance. An EMPTY granted set denies every tool — declining every scope at consent grants nothing — and the only wildcard is the explicitrivr.legacy.unscopedmarker written onto pre-OAuth credentials by migration 0080, honoured until its sunset and refused after it. Nothing is inferred from scopes a credential lacks. Destructive, irreversible operations (e.g. account deletion) are deliberately withheld from MCP and require the interactive UI.
3. Peer signature Peer Signature
The federation trust rail. Sovereign instances exchange Ed25519-signed events
authenticated against a peer registry (/api/federation/peers,
/api/federation/remote-auth). The signature — not a header or an asserted node
id — is the principal. Nonce + signature dedup prevent replay; imported events
bind their actor to the materialized local agent id (FK-safe), never to the raw
remote id.
- Used by: the federation import/push/query endpoints — see the Federation protocol page for the full endpoint list and the signed-event model.
- Catch-up: a locally-initiated pull-sync cron may set an
allowHistoricalflag to replay events after >7-day downtime; push/import routes stay strict.
Public (auth-optional) Public
A subset of routes is intentionally public — discovery, health, manifests,
federation handshakes, Stripe webhooks, map tiles, and this docs site. These are
enumerated in PUBLIC_PAGE_PREFIXES / PUBLIC_API_PREFIXES
(src/lib/route-access.ts) and marked Public in the
REST reference.
See also
- Federation protocol — the signed-event endpoints and trust model.
- MCP tools — per-tool auth badges.
- Federation & SSO identity (wiki) — the user-facing side of sovereign homes and cross-instance identity.