Skip to main content

Federated identity layer

AlphaSwarm centralizes identity and token operations through the unified alphaswarm_auth service, or the pluggable :class:alphaswarm.auth.providers.IdentityProvider for legacy/local deployments. The auth service acts as the central Identity and Access Management (IAM) hub, providing both user authentication (login, JWT validation, refresh) and centralized authorization resolution (Scope Resolution).

The pieces port (with attribution) from alphaswarm_snippets/inspiration/auth0-server-python-main (MIT, Copyright Auth0, Inc.) into AlphaSwarm-native modules.

Architecture

Components

ComponentPath
Provider ABC + metaclassalphaswarm/auth/providers/protocol.py
Unified Auth Servicealphaswarm_auth/
Auth0 / generic OIDC / mock concrete providersalphaswarm/auth/providers/
OIDC HTTP plumbing (discovery, JWKS, token endpoint)alphaswarm/auth/oidc_client.py
PKCE helpers (RFC 7636 S256)alphaswarm/auth/pkce.py
Cookie / Redis session storesalphaswarm/auth/session/
JWE cookie crypto (HKDF-SHA256 + A256CBC-HS512)alphaswarm/auth/session/crypto.py
M2M token issueralphaswarm/auth/m2m.py
Login / callback / logout routesalphaswarm/api/routes/auth.py
Backend JWT validatoralphaswarm/auth/oidc.py

Centralized Authorization (Scope Resolution)

Per the CP-side maturation, AlphaSwarm now centralizes authorization resolution in the alphaswarm_auth service. Instead of each service independently re-deriving the AlphaSwarm scope set from raw JWT claims, they call the introspection endpoint:

  1. Consuming Service (e.g., alphaswarm_controller) receives a bearer token.
  2. Consuming Service calls POST /auth/introspect on the alphaswarm_auth service.
  3. Auth Service validates the token and resolves the full AlphaSwarm scope set using the canonical RBAC lattice and custom group/role mappings.
  4. Auth Service returns the HydratedUser including the resolved scopes.
  5. Consuming Service enforces access based on these resolved scopes.

This ensures consistent authorization logic across the platform and simplifies the addition of new identity providers. For more details, see the alphaswarm_auth architecture documentation.

Login flow (backend session)

  1. Browser hits GET /auth/login (optionally with a return_to).
  2. AlphaSwarm generates a PKCE verifier + state, stashes them in an encrypted transaction cookie (10-minute TTL), redirects to the provider's authorize URL.
  3. Provider posts the authorization code to GET /auth/callback.
  4. AlphaSwarm looks up the transaction cookie by state, calls provider.exchange_code(...), and stores the resulting token set in an encrypted session cookie (or Redis).
  5. Subsequent requests carry the cookie; AlphaSwarm decrypts it on demand and exposes the user via the existing current_user dep.

The bearer-token flow (Authorization: Bearer) keeps working unchanged — the SPA can pick either path via the backend_session_supported flag in /auth/config.

M2M flow

When ALPHASWARM_AUTH_M2M_ENABLED=true:

  1. AlphaSwarm startup calls alphaswarm.auth.m2m.install_m2m_store(), which adds :class:M2MStore (priority 10) to the credential resolver chain.
  2. A service like polaris_client resolves CredentialKey("polaris", "oauth") through :func:alphaswarm.credentials.get_resolver.
  3. The M2M store fetches provider.m2m_token(audience, scope) (Auth0 client_credentials grant) and returns a Credential with access_token/token set.
  4. The resolver merges this hit with the env-store payload (which carries the static client_id), so consumers see one merged Credential.
  5. Tokens cache in M2MTokenIssuer until expiry minus a 30-second skew, so we don't mint per request.

The resolver chain falls through to the file/env stores if the M2M issuer fails or is disabled — you never get a worse outcome than the pre-M2M state.

Configuration

The full env knob set lives in .env.example under the "Federated identity (M2 / M3)" section. The minimum for an Auth0 deployment:

ALPHASWARM_AUTH_PROVIDER=auth0
ALPHASWARM_AUTH_OIDC_ISSUER=https://your-tenant.auth0.com
ALPHASWARM_AUTH_OIDC_AUDIENCE=https://alphaswarm.local/api
ALPHASWARM_AUTH_OIDC_CLIENT_ID=...
ALPHASWARM_AUTH_OIDC_CLIENT_SECRET=...
ALPHASWARM_AUTH_LOGIN_CALLBACK=http://localhost:8000/auth/callback
ALPHASWARM_AUTH_LOGOUT_CALLBACK=http://localhost:3000/
ALPHASWARM_AUTH_SESSION_SECRET=$(openssl rand -hex 32)
ALPHASWARM_AUTH_M2M_ENABLED=true
ALPHASWARM_AUTH_M2M_AUDIENCE=https://alphaswarm.local/services

Adding a new provider

  1. Subclass :class:alphaswarm.auth.providers.IdentityProvider and set provider_kind (the dispatch key matched against ALPHASWARM_AUTH_PROVIDER).
  2. Either inherit from :class:alphaswarm.auth.providers.GenericOidcProvider (and override only the bits that diverge) or roll your own.
  3. The metaclass auto-registers; restart the API and set ALPHASWARM_AUTH_PROVIDER=<your_kind>.

Testing

tests/auth/ contains the canonical test patterns:

  • test_pkce.py — RFC 7636 conformance.
  • test_session_crypto.py — JWE round-trips, wrong-key rejection.
  • test_oidc_client.py — token endpoint mock-driven tests.
  • test_providers.py — Auth0 / generic OIDC / mock dispatch.
  • test_m2m.py — issuer caching, resolver integration.

All tests run hermetic; nothing hits the network.

Account management surface (Phase 7)

Phase 7 adds a dedicated account-management API surface under /me/* implemented in alphaswarm/api/routes/me.py. These routes expose profile updates, MFA and session operations, linked identity management, and self-service account actions while keeping the Auth0 Management API boundary centralized.

The Auth0 Management API integration lives in alphaswarm/auth/management_api.py. Scope enforcement for protected endpoints is available through alphaswarm/auth/auth0_fastapi.py via Auth0FastAPI opt-in dependencies. Audit and invite persistence for this surface is recorded in alphaswarm/persistence/models_audit.py (security_audit_events and tenancy_invites), and events are emitted through alphaswarm/auth/audit.py.

Microsoft Entra ID secondary IdP (Phase 7)

AlphaSwarm's primary Microsoft pattern is federation through Auth0 Universal Login using an Auth0 Microsoft Enterprise Connection, documented in alphaswarm_docs/auth0-microsoft-federation.md. This keeps Auth0 as the default IdP while preserving one hosted login surface and one claims projection path.

Direct Entra authentication remains supported as a fallback through alphaswarm/auth/providers/msal_entra.py. When ALPHASWARM_AUTH_PROVIDER=msal_entra, the legacy MsalEntraProvider path activates without changing the backend tenancy-link semantics.

Legacy demo SAML provisioning

This section documents the retired demo-only SAML provisioning gate. The current alpha-testing topology uses the active Entra/Cloudflare Access paths: MacBook client apps and local servers target the platform hosted on the local Ubuntu box. Do not enable demo provisioning for the current alpha stack.

The legacy hosted demo (demo.alpha-swarm.ai) could provision real AlphaSwarm accounts for a curated set of design partners who signed in through a SAML identity provider, while denying everyone else. SAML was brokered upstream (an Auth0 Enterprise SAML connection or an Entra SAML/WS-Fed enterprise app) and arrived at the backend as a normal OIDC JWT — there is no native SAML SP.

Provisioning is gated by a per-email allowlist:

  • The demo_approved_emails table (models_tenancy.py, Alembic 0089) holds the approved emails (lifecycle approved / revoked, plus a reserved pending), each with a default_role and optional scope.
  • On first login, provision_user_from_claims calls is_demo_gated_login(claims) and, when true, evaluate_demo_approval. An approved email mints a real User + membership at the row's role; an unapproved email is denied before any row is created (ProvisioningDenied → HTTP 403 with an RFC 9470 WWW-Authenticate challenge) in strict mode, or logged-and-allowed in permissive mode.
  • Revoking an approval ejects an already-provisioned demo user on their next request (strict mode) via the _DEMO_APPROVAL_REJECT_STATUSES check in alphaswarm/api/security.py, mirroring the EntraTenantLink suspend/revoke handling.

A login only counts as "demo SAML" when the brokering connection (connection / idp claim, or a samlp|<conn>|… subject) is listed in ALPHASWARM_AUTH_DEMO_CONNECTION_NAMES or backed by an IdpConnectionRecord (kind generic_saml) whose config.demo is true — so production Entra logins are never gated.

Admins manage the allowlist through /tenancy/orgs/{org_id}/idp-connections/{connection_id}/approved-emails (admin role + step-up MFA + audit), surfaced in the operator UI by ApprovedEmailsEditor.

Configuration

# Legacy demo backend only (NOT the current alpha or hosted-platform deployment).
ALPHASWARM_AUTH_DEMO_PROVISIONING_ENABLED=true
ALPHASWARM_AUTH_DEMO_ENFORCE=permissive # flip to strict after burn-in
ALPHASWARM_AUTH_DEMO_CONNECTION_NAMES=demo-saml

ALPHASWARM_AUTH_DEMO_PROVISIONING_ENABLED is mutually exclusive with ALPHASWARM_AUTH_HOSTED_PLATFORM_MODE (a boot validator refuses both) so the gate can never run on a production Entra surface. For the current alpha stack, leave this disabled. Full historical design + rollout runbook lives in the monolith repo at .cursor/plans/saml-demo-approved-email-provisioning.plan.md.