Skip to main content
Mesh defaults to local email-and-password login. External mode delegates browser sign-in to a trusted service that implements Mesh’s launch-code protocol. Mesh verifies its signed assertion and creates a short-lived local session. This is not a generic OIDC discovery, OAuth login, or SAML integration; an identity provider needs an adapter that implements the protocol below. External mode disables local password login, first-administrator setup, and local invitation flows. Configure and test the external service before switching the instance. It changes operator authentication, not a conversational participant’s identity links.

Configuration

Set these on the Mesh process and restart it: Every field is required in external mode. PUBLIC_URL must be HTTPS even for a development instance using this flow. Other endpoint URLs require HTTPS except that configuration permits HTTP in MESH_ENV=development or test, or for hosts ending in .internal. That hostname exception is a deployment assumption, not proof that the network is private. Register the exact callback PUBLIC_URL/auth/callback with the external service. The authorization endpoint must be reachable by the browser; exchange and JWKS endpoints must be reachable by the Mesh server. Keep the exchange secret out of browser configuration and source control.

Launch protocol

  1. The browser opens GET /api/auth/external/start. Mesh creates a five-minute launch cookie containing a random state and verifier.
  2. Mesh redirects to the authorization URL with instance_id, state, challenge (base64url SHA-256 of the verifier), and redirect_uri.
  3. The external service authenticates the operator and redirects to /auth/callback?code=...&state=....
  4. Mesh checks the returned state, then POSTs JSON containing code, code_verifier, and instance_id to the exchange URL, authenticated with Authorization: Bearer MESH_AUTH_EXCHANGE_SECRET.
  5. The exchange service returns HTTP 200 with {"assertion":"SIGNED_JWT"}. Mesh verifies it, consumes its unique ID against replay, and opens a local session before redirecting to /agents.
The external service must bind its codes to the instance, redirect, and challenge and validate the verifier during exchange. Its implementation is outside this repository. JWKS and exchange HTTP calls each have a ten-second client timeout.

Assertion requirements

Mesh accepts an ES256 JWT with header typ: mesh-launch+jwt and a kid selecting an EC P-256 key from the configured JWKS. The payload must contain:
  • Matching iss, string aud, and instance_id.
  • Nonempty sub and single-use jti.
  • token_use: mesh_launch and schema_version: 1.
  • email_verified: true and an email for first-time profile creation.
  • A capabilities array containing mesh:admin.
  • iat, nbf, and exp compatible with the verifier’s time checks: maximum declared lifetime 90 seconds and 30 seconds of allowed clock skew.
The external service is responsible for deciding who may receive that capability. Mesh’s operator roles and agent-access checks still govern the resulting user’s API access; see the operator API.

Accounts and sessions

An external user is identified by (issuer, subject), never by email. Different external identities can share an email, and an external identity can share one with a local account. Sign-in does not claim the older account, change its password, transfer its API tokens, or rewrite its attribution. The operator roster can therefore show distinct users with the same email. External sessions last 15 minutes and use the secure, HttpOnly, host-only __Host-mesh_session cookie. Session records are held by Mesh. Logout or local session revocation ends that Mesh session; this flow does not implement logout from the external identity provider. Disabling a Mesh operator also blocks their session access. Switching back to local mode does not give external users passwords. Local password and invitation lookups consider local accounts only. Preserve existing identity mappings and use a compatible database/binary when changing auth modes; do not repair a login issue by merging accounts on email. Implementation: internal/admin/auth_external.go (repository), internal/admin/jwt.go (repository), and internal/config/config.go (repository).