feat(auth): add ALLOW_SIGNUPS env var to gate self-service sign-ups#865
Open
feat(auth): add ALLOW_SIGNUPS env var to gate self-service sign-ups#865
Conversation
- GET /auth/me/ — returns user info from Django session - GET /auth/sso/<provider>/authorize/ — initiates OAuth redirect - GET /auth/sso/<provider>/callback/ — handles OAuth callback - Provider registry built from SOCIALACCOUNT_PROVIDERS settings - OIDC discovery with TTL cache - Email domain whitelist enforcement - Support for client_secret_post and client_secret_basic - Preserves callbackUrl through SSO flow for deep link support - Tests for all new endpoints
The generic OIDC adapter passed raw JWKS JSON to jwt.decode() which expects a PEM key. This was masked by NextAuth decoding the id_token client-side. Now uses PyJWKClient for proper key resolution.
- UserContext fetches /auth/me/ for user info (email, name, avatar) - useSession() compatibility shim with memoized references - Middleware checks Django sessionid cookie instead of NextAuth JWT - Redirects to /login with callbackUrl preserving path + query string - Handles stale sessions by redirecting only on 401/403, not transient errors - handleSignout() calls /logout/ and redirects to /login
Replace all useSession/signIn/signOut imports from next-auth/react with the new UserContext compatibility shim. SSO buttons now redirect to backend /auth/sso/<provider>/authorize/ endpoints.
Self-hosted instances have old NextAuth callback URLs registered with their OAuth providers. This route 302-redirects /api/auth/callback/<provider> to the new backend callback endpoint, preserving all query params. Zero config changes needed for existing deployments.
- Delete pages/api/auth/[...nextauth].ts (338 lines) - Delete 5 OIDC provider utility files (264 lines) - Remove next-auth from package.json
Tests for useSession compatibility shim and UserProvider behavior.
- Validate authorize URL scheme is HTTPS before redirecting - Use quote(safe='') for IdP error descriptions in redirect
# Conflicts: # frontend/ee/authentication/sso/oidc/util/githubEnterpriseProvider.ts # frontend/pages/api/auth/[...nextauth].ts
- Update Authelia tests to mock PyJWKClient instead of raw JWKS dict - Convert credentials_auth tests from django.test.TestCase to unittest.TestCase (no Postgres dependency in CI) - Fix CallbackUrlTest to patch module-level FRONTEND_URL
- Update 2 more Authelia TestCompleteLogin tests to mock PyJWKClient - Add SECRET_KEY and SERVER_SECRET defaults to conftest.py for CI
Rename api/views/credentials_auth.py → api/views/sso.py and tests/test_credentials_auth.py → tests/test_sso.py. Update all import references.
- Fix f-string nested quotes in entraid/views.py (Python <3.12 compat) - Delete frontend autheliaProvider test that imports deleted OIDC utils
- Fix nested f-string quotes in github_enterprise/views.py (Python <3.12) - Update userContext test assertion for callbackUrl redirect
- Middleware and UserProvider skip callbackUrl for root path (clean /login) - Only add callbackUrl for actual deep links (/team/settings, etc.) - Fix nested f-string in github_enterprise/views.py (Python <3.12) - Expanded tests: root redirect, deep link redirect, query string preservation, public path exclusion
- Add EmailVerification model with token, verified flag, and expiry - Add migration 0120_email_verification - Configure Argon2PasswordHasher as the sole password hasher - Add django-argon2-hasher dependency
- Add password register, login, change, and reset-via-recovery endpoints - Add email verification flow with token generation and validation - Add resend verification endpoint with rate limiting - Add email-check endpoint for email-first login flow - Add email verification HTML template - Register all new URL routes
Tests for register, login, email verification, password change, recovery reset, email check, and domain whitelist enforcement.
- Add deviceVaultKey (Argon2id) and passwordAuthHash (BLAKE2b) to crypto utils - Add corresponding unit tests
Replace separate SSO and password login paths with a single email-first flow: enter email, then the server resolves the auth method (password or SSO provider) and the UI adapts. Remove unused SSO-only dead code path.
New signup page with full name, email, password fields. Handles client-side key derivation, shows email verification screen on success, and supports resend with single-use disable.
All users (password and SSO) go through the same onboard: Organisation Name → Sudo Password → Account Recovery. No sessionStorage password caching.
- Add ChangePasswordSection component for password users - Re-wraps all org keyrings with the new password - Hidden for SSO users (no usable password)
…ngeAccountPasswordMutation
…og, delete REST endpoint
… salt Bump passwordAuthHash from INTERACTIVE (~64MiB / ~100ms) to MODERATE (~256MiB / ~1s) and prefix its salt input with "auth-v1:". Symmetric work factor with deviceVaultKey, explicit domain separation, no change to deviceKey derivation. Tests pass (32/32).
refactor(auth): authHash to Argon2id-MODERATE with versioned distinct salt
Self-service sign-up is on by default. Setting ALLOW_SIGNUPS=false requires an invite for any new account — closing the door on strangers without affecting existing users, password recovery, or org-level SSO. The gate fires in two places: password_register and the instance- level SSO new-user creation path. Invitees with a pending OrganisationMemberInvite always pass through, so closed instances can still onboard people. Supersedes #863, which gated the entire password-auth subsystem (login, recovery, change-password) instead of just sign-ups, and defaulted to off — locking out existing password users on upgrade.
Base automatically changed from
feat/password-auth-pr
to
feat/eliminate-nextauth
April 29, 2026 07:38
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
Self-service sign-up is on by default. Setting
ALLOW_SIGNUPS=falserequires an invite for any new account — closing the door on strangers without affecting existing users, password recovery, or org-level SSO.The gate fires at:
password_register(REST)Invitees with a pending
OrganisationMemberInvitealways pass through, so closed instances can still onboard people. Org-level SSO is unaffected — its existing membership-or-invite check already does the right thing.Why not #863
Supersedes #863. Two key differences:
For org-level "this team must use SSO" enforcement, the existing per-org SSO gate on the base branch is the right primitive —
ALLOW_SIGNUPSis intentionally orthogonal.Docs: phasehq/docs#228
Test plan
test_auth_password.py(gate scenarios +_signups_allowedtruthiness incl. fail-open on typo)ALLOW_SIGNUPS=false: stranger 403 toast, existing user sign-in, invitee end-to-end, GitHub SSO new-user blocked without invite, GitHub SSO new-user passes with invite