Add single sign-on (OIDC) - #684
Merged
Merged
Conversation
A narrow OIDC authorization-code flow (PKCE, one-use state, nonce, issuer/audience and RS256 verification), off by default and enabled only once all four UTOPIA_OIDC_* variables are set. Accounts must be explicitly linked by an administrator on a new Administration > Single sign-on page; a subject that has never been linked is refused at callback rather than silently provisioned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0136c4gq3BDppPhcWcRdy5MJ Signed-off-by: Nafeeur Rahman <hello@nafeeur.nyc>
Contributor
Author
Signed-off-by: WaylandYang <wayland0916@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
Contributor
|
@nafeeur thanks for this. The protocol work is careful, and it held up against every token and state attack I threw at it. Before merging for the release I pushed ec126ae to your branch with one design change and a set of hardening fixes, listed in the description. The design change: identities are now linked by their owner from the account page, not by an administrator. An admin binding any subject to any account (other admins included) gave admins a way to sign in as someone else that survives a password change, so admins now keep only the list and unlink. 🤖 Generated with Claude Code |
Merged
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 a narrow OIDC authorization-code flow for single sign-on, off by default:
crates/utopia-server/src/api/oidc_routes.rs): PKCE, one-usestate,nonce, issuer/audience checks, and RS256 verification against the provider's JWKS. Enabled only when all fourUTOPIA_OIDC_ISSUER/UTOPIA_OIDC_CLIENT_ID/UTOPIA_OIDC_CLIENT_SECRET(optional) /UTOPIA_OIDC_REDIRECT_URIenv vars are set —statusreportsfalseotherwise and the login button stays hidden./auth/oidc/callbackunless an administrator has already bound it to an account (oidc_identities, migration0056). Binding/unbinding is admin-only and audited (auth.oidc_link/auth.oidc_unlink/auth.oidc_login).status.enabled), and a new Administration → Single sign-on page for admins to link/unlink identities — built from the existingui/shell (Table,Dialog,Field,SearchSelect) perweb/DESIGN.md, with full English + Chinese strings.azp).Test plan
cargo check -p utopia-server/cargo test -p utopia-server oidc_routes(2/2 passing)pnpm build(style guard +tsc+vite build) andpnpm test(51/51) inweb/0056applies cleanly,/api/v1/auth/oidc/statusresponds correctly when unconfiguredcargo fmt --all --check/cargo clippy --workspace --all-targets -- -D warnings— both clean, no errors or warningsChanges before merging (maintainer)
A security review before the release found the protocol handling sound: PKCE S256, one-use DB-backed state bound to an HttpOnly cookie, RS256 pinned,
iss/aud/azp/nonce/exp/nbf/iatall enforced. It also found one design problem and several smaller defects. All are fixed on this branch in ec126ae.An identity is now linked by its owner, not by an administrator. The admin
POST /admin/oidc/identitieslet an admin bind any subject to any account in the org, other admins included, without the owner taking part or seeing it. That gave admins a new way to sign in as someone else. The link survived a password change and attributed later actions to the victim (decision 0014: identity comes from the person). Now:GET /auth/oidc/start?link=1from their account page, and the flow records their id (oidc_flows.link_user_id);GET/DELETE /auth/oidc/melet the owner see and remove their link;taken), and so is a second link on the same account (already_linked), instead of a 500.Smaller fixes
startis bounded. Discovery and JWKS are cached for 10 minutes, with a forced JWKS refresh when akidis unknown. In-flight flows are capped at 1,000./loginor/accountwith?sso_error=<code>, including cancel at the provider (error=access_denied), and shows a localized message. The reason goes to awarnlog and to the audit ledger (auth.oidc_login_failed/auth.oidc_link_failed).__Host--prefixed (Secure, Path=/) when behind TLS.kidis accepted only when the JWKS holds exactly one RSA key.UTOPIA_OIDC_ALLOW_LOOPBACK_HTTP=1lets the issuer be a localhost IdP for development; public hosts still require HTTPS..env.exampleand hints now say three variables are required and the secret is optional.End-to-end, against a local fake IdP
A scripted IdP signs RS256 tokens with the test key, and the server is built from this branch. 29/29 checks pass:
aud, wrongnonce, an expired token andalg: noneare refused;__Host-, Secure, Path=/;cargo clippy --workspace --all-targets -D warningsis clean.utopia-servertests: 276 passed on a migrated database. Web: style guard,tsc, and Vitest 51/51. Login, account and admin pages were checked in the browser.