Skip to content

ADR-002: Authentication Stack

Status: Accepted

arpi needs a user authentication mechanism for its CLI, control plane API, and web UI. Requirements include device code flow for CLI, standard OIDC for API auth, service accounts for CI/CD, and self-hosted operation.

Self-hosted Zitadel (OIDC identity provider) for all authentication.

  • Single Go binary, simpler ops than Ory (Kratos + Hydra)
  • Native multi-org support maps to arpi’s org-scoped roles
  • Built-in service accounts for machine identity (agents, CI/CD)
  • Full OIDC provider with device code flow (RFC 8628) and PKCE
  • CLI: PKCE localhost callback with device code fallback (auto-detect)
  • Server: Standard OIDC JWT validation via coreos/go-oidc with OIDC_ISSUER_URL/OIDC_CLIENT_ID
  • Custom claims: Zitadel Actions map roles to arpi_roles JWT claim
  • Org ID: Zitadel org ID used directly as org_id claim
  • CI/CD: arpi login --token accepts pre-exchanged Zitadel access token
  • Dev: Zitadel in docker-compose reusing arpi-postgres (port 8084)
  • Staging/prod: Helm deployment
  • Tokens stored locally in ~/.config/arpi/auth.json (JSON, 0600 permissions)
  • Server validates JWTs via standard OIDC discovery
  • AuthTransport handles Bearer injection and proactive refresh on expiry
  • Generic OIDC_* env vars — provider-agnostic configuration
  • 2026-03-25: Original decision — self-hosted Zitadel
  • 2026-03-30: Override to WorkOS AuthKit (velocity, zero ops)
  • 2026-03-31: Back to self-hosted Zitadel (WorkOS not friendly to local/self-hosted)