Skip to content

feat(auth): add ALLOW_SIGNUPS env var to gate self-service sign-ups#865

Open
nimish-ks wants to merge 102 commits intomainfrom
feat/allow-signups
Open

feat(auth): add ALLOW_SIGNUPS env var to gate self-service sign-ups#865
nimish-ks wants to merge 102 commits intomainfrom
feat/allow-signups

Conversation

@nimish-ks
Copy link
Copy Markdown
Member

Summary

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 at:

  • password_register (REST)
  • The instance-level SSO new-user creation path

Invitees with a pending OrganisationMemberInvite always 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:

  • Default ON instead of OFF. Off-by-default would silently lock out existing password users on upgrade and forces a helm-chart / compose / docs fan-out we don't otherwise need.
  • Gates the sign-up event, not the whole password-auth subsystem. Login, recovery, and change-password keep working for existing users.

For org-level "this team must use SSO" enforcement, the existing per-org SSO gate on the base branch is the right primitive — ALLOW_SIGNUPS is intentionally orthogonal.

Docs: phasehq/docs#228

Test plan

  • 7 new unit tests in test_auth_password.py (gate scenarios + _signups_allowed truthiness incl. fail-open on typo)
  • Manual QA in browser with 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

- 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)
rohan-chaturvedi and others added 25 commits April 27, 2026 17:03
… 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/org-sso-gating to feat/password-auth-pr April 29, 2026 07:36
Base automatically changed from feat/password-auth-pr to feat/eliminate-nextauth April 29, 2026 07:38
Base automatically changed from feat/eliminate-nextauth to main April 29, 2026 07:47
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.

2 participants