feat(auth): add generic OAuth2 / OIDC (Authentik) dashboard SSO - #1
Open
thewillwilson wants to merge 1 commit into
Open
feat(auth): add generic OAuth2 / OIDC (Authentik) dashboard SSO#1thewillwilson wants to merge 1 commit into
thewillwilson wants to merge 1 commit into
Conversation
Adds a redirect-based Authorization-Code + PKCE single sign-on flow for
the web dashboard. Because the REST layer is Watson (not ASP.NET Core),
the flow is implemented by hand and, on success, mints the existing
AES-encrypted session token so the rest of the auth stack is unchanged.
Backend:
- OAuth2Settings (nested in ArmadaSettings): endpoints, client id/secret,
scopes, PKCE, claim mapping, auto-provision, tenant, verified-email gate.
- OAuth2Service + IOAuth2Service: PKCE (RFC 7636), single-use CSRF state
store, code->token->userinfo exchange, email-based user resolve/provision
into DefaultTenantId, session-token minting.
- OAuthRoutes: GET /api/v1/auth/oauth/{config,authorize,callback}
(NoAuthRequired); callback redirects to /dashboard#oauth_token=...
- Wired into ArmadaServer; AuthorizationConfig marks the routes public.
Dashboard:
- "Sign in with <provider>" button, config discovery, and #oauth_token /
#oauth_error fragment handling (token stripped from history on consume).
Security hardening (from self-review):
- Require the provider's email_verified claim before trusting email for
identity mapping (prevents takeover via unverified email).
- Never persist the callback's session token: empty redirect body and the
callback route is excluded from request-history capture.
- State store documents the residual login-CSRF (browser-binding) follow-up.
Tests: OAuth2Service unit suite (PKCE vector, state single-use, settings
gating, authorize URL, user resolve/provision) and an automated HTTP suite
for the config/authorize endpoints.
Docs: docs/REST_API.md SSO section, settings table, Authentik example.
Note: .NET/Node toolchains are unavailable in the authoring environment,
so the solution build and test suites were not executed locally.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
✅ Built & tested (Docker,
|
zarlo
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds generic OAuth2 / OIDC single sign-on for the Armada web dashboard, so users can log in via Authentik (or any OAuth2/OIDC provider) instead of only email/password or a pasted API key.
Because Armada's REST layer runs on Watson Webserver, not ASP.NET Core, turnkey OIDC middleware doesn't apply — this is a hand-rolled Authorization-Code + PKCE flow implemented as Watson routes. On success it mints the existing AES-encrypted session token (
SessionTokenService), so the entire downstream auth stack (AuthenticationService,whoami, the SPA'sX-Tokenflow) is untouched.Flow
GET /api/v1/auth/oauth/config→ shows a "Sign in with <DisplayName>" button when SSO is enabled.GET /api/v1/auth/oauth/authorize→ 302 to the provider (state + PKCE S256).GET /api/v1/auth/oauth/callback→ code→token→userinfo exchange, resolve/provision user, 302 to/dashboard#oauth_token=<session-token>.What's included
Backend (
Armada.Core/Armada.Server)OAuth2Settings(nested inArmadaSettings): endpoints, client id/secret, scopes, PKCE, claim mapping,RequireVerifiedEmail,AllowAutoProvision,DefaultTenantId.OAuth2Service+IOAuth2Service: PKCE (RFC 7636), single-use CSRF state store, token/userinfo exchange, email-based user resolve/provision, session-token minting.OAuthRoutes:config/authorize/callback(allNoAuthRequired); wired intoArmadaServer.Dashboard (React)
LoginFlow.tsx;#oauth_token/#oauth_errorfragment handling inAuthContext.tsx(reuses the existinglogin()path).Config (Authentik example) — see
docs/REST_API.md:Security review (self-reviewed; fixes applied in this PR)
email_verifiedclaim must betruebefore the email is trusted for identity mapping (RequireVerifiedEmail, default on).Locationfragment verbatim).Tests
OAuth2ServiceTests— PKCE RFC vector, state single-use/unknown, settings gating, authorize-URL params, user resolve + auto-provision + rejection paths.OAuthApiTests—configreturns disabled by default;authorize302s with an error when disabled.Scope / notes
dotnet build/test suites and the dashboardtscbuild were not executed locally. Please run the standard build +test/Armada.Test.Unit/Armada.Test.Automatedbefore merge.🤖 Generated with Claude Code