Skip to content

feat(auth): Auth0 SSO sign-in - #26

Merged
mathewdunne merged 5 commits into
mainfrom
24-auth0-sso
Oct 1, 2026
Merged

mathewdunne merged 5 commits into
mainfrom
24-auth0-sso

Conversation

@mathewdunne

Copy link
Copy Markdown
Owner

Closes #24.

What

Adds Auth0 as a third sign-in option alongside GitHub and Google. Auth0 users don't need to be on the CodeRunner allowlist. Instead, their CodeRunner role comes from their Auth0 roles:

Auth0 roles Result
Admin role (AUTH0_ADMIN_ROLE_NAME, default admin) Admin
User role (AUTH0_USER_ROLE_NAME, default user) Student
Neither Denied
  • Roles are re-read at every Auth0 sign-in, so a change in Auth0 applies the next time the user signs in.
  • CODERUNNER_ADMIN_EMAIL still grants admin.
  • Each configured provider gets its own login button. Auth0 shows up once AUTH0_DOMAIN, AUTH0_CLIENT_ID and AUTH0_CLIENT_SECRET are all set.
  • The roles claim name (AUTH0_ROLES_CLAIM, default https://coderunner/roles) and both role names are configurable, so they can match an existing tenant.

How

  • Auth0 goes through Better Auth's genericOAuth plugin, using its auth0() helper. The callback is /api/auth/oauth2/callback/auth0.
  • New users without a role are rejected in user.create.before, so no user row is created.
  • Returning users without a role are rejected in session.create.before, so no session or cookie is issued. The after-login hook doesn't work for this: Better Auth keeps the callback's 302 redirect and its cookies when that hook throws, and its 5-minute session cookie cache keeps the user signed in after the session row is deleted. Details in decision 044.
  • mapProfileToUser writes the role on every sign-in, so the session cookie carries the current role.

Setup

See the new "Set up Auth0" section in docs/deploying/oauth-credentials.md. It covers the Regular Web Application, the callback URL, the two roles, and the Post-Login Action that puts the roles in the ID token.

Testing

  • bun run verify passes: 463 control-plane tests, 141 web, 76 E2E and 12 security.
  • The new auth0.test.ts runs the real Better Auth sign-in and callback against a fake Auth0 (discovery and token endpoints stubbed via fetch). It covers:
    • first sign-in as student and as admin
    • no role → denied, with no user row created
    • the admin-email override
    • promotion and demotion on later sign-ins
    • a returning user with no role → /login?error=… and no session
  • Checked in a browser that the login page shows the Auth0 button and that clicking it sends the right sign-in request.
  • Not yet tested against a real Auth0 tenant.

🤖 Generated with Claude Code

mathewdunne and others added 5 commits September 30, 2026 22:54
Add Auth0 as a third login provider through Better Auth's genericOAuth
plugin, enabled by AUTH0_DOMAIN/CLIENT_ID/CLIENT_SECRET. Auth0 users skip
the allowlist; a roles claim in the ID token (AUTH0_ROLES_CLAIM) maps
AUTH0_ADMIN_ROLE_NAME to admin and AUTH0_USER_ROLE_NAME to student and is
re-read at every sign-in. No role means denied: new users in
user.create.before, returning users in session.create.before so no
session or cookie is issued. CODERUNNER_ADMIN_EMAIL still grants admin.

Decision 044; setup guide in docs/deploying/oauth-credentials.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	AGENTS.md
#	apps/control/src/auth/auth.ts
…s on GCE

- seedBootstrapAdmins no longer promotes users with an auth0 account. Auth0
  sets the role at every sign-in (admin only for a verified address), and a
  matching row may be an unverified self-sign-up that a later GitHub sign-in
  links onto, so promoting it at startup bypassed the email_verified check.
- render-env.sh reads optional coderunner-auth0-{domain,client-id,
  client-secret} secrets and writes AUTH0_* when all three are set, so Auth0
  config survives reboots on the GCE deployment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mathewdunne
mathewdunne marked this pull request as ready for review October 1, 2026 17:27
@mathewdunne
mathewdunne merged commit 7e21a3a into main Oct 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Auth0 SSO support

1 participant