Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
1af1a3b
feat(backend): add SSO auth endpoints replacing NextAuth
nimish-ks Apr 10, 2026
07b5940
fix(backend): use PyJWKClient for OIDC id_token validation
nimish-ks Apr 10, 2026
34535e6
feat(frontend): add UserContext and middleware replacing NextAuth
nimish-ks Apr 10, 2026
42ba535
refactor(frontend): migrate all components from next-auth to UserContext
nimish-ks Apr 10, 2026
71c7dc5
feat(frontend): add legacy OAuth callback redirect for backward compat
nimish-ks Apr 10, 2026
d39341e
chore(frontend): remove next-auth dependency and related files
nimish-ks Apr 10, 2026
594dcb1
test(frontend): add UserContext tests and update jest config
nimish-ks Apr 10, 2026
4f180a2
fix(backend): address CodeQL open redirect warnings
nimish-ks Apr 11, 2026
f324639
Merge remote-tracking branch 'origin/main' into feat/eliminate-nextauth
nimish-ks Apr 11, 2026
688051c
fix(backend): fix test failures in CI
nimish-ks Apr 11, 2026
d9e8e50
fix(backend): fix remaining CI test failures
nimish-ks Apr 11, 2026
637635e
refactor(backend): rename credentials_auth.py to sso.py
nimish-ks Apr 11, 2026
1ae0ab6
fix: fix CI failures from merge
nimish-ks Apr 11, 2026
a0e4bb6
fix: fix remaining CI failures
nimish-ks Apr 11, 2026
9b5b9bc
fix: clean login URL and expanded redirect tests
nimish-ks Apr 11, 2026
faaede2
fix: use organisation
nimish-ks Apr 11, 2026
fc567fa
fix: removed unused paths
nimish-ks Apr 11, 2026
7f7d6c8
feat: removed legacy provider paths
nimish-ks Apr 11, 2026
17fffa2
chore: clean up unused sso classes
nimish-ks Apr 11, 2026
0da2fde
feat: extend tests of sso providers
nimish-ks Apr 11, 2026
8d78d68
fix: package name
nimish-ks Apr 11, 2026
4075ba7
chore: drop jsonwebtoken
nimish-ks Apr 11, 2026
2eaf80e
feat: add EmailVerification model and Argon2 password hashing
nimish-ks Apr 13, 2026
430eb47
feat: add password auth endpoints and email verification
nimish-ks Apr 13, 2026
fde67ae
test: add backend tests for password auth endpoints
nimish-ks Apr 13, 2026
fc1f5a7
feat: add client-side password key derivation functions
nimish-ks Apr 13, 2026
c662070
feat: implement email-first unified login flow
nimish-ks Apr 13, 2026
1914399
feat: add password signup page with email verification
nimish-ks Apr 13, 2026
2c83a08
feat: add 3-step onboarding flow for all users
nimish-ks Apr 13, 2026
dfdfb73
feat: add password change section in account settings
nimish-ks Apr 13, 2026
f6d6f7b
feat: update account recovery to reset password via mnemonic
nimish-ks Apr 13, 2026
c4e0543
fix: filter custom props from Input component to prevent React warnings
nimish-ks Apr 13, 2026
02348af
chore: update auth references and remove stale /register path
nimish-ks Apr 13, 2026
da1247b
fix: use in-memory cache for tests to avoid Redis dependency in CI
nimish-ks Apr 13, 2026
a178f3f
feat: add full_name field to CustomUser model
nimish-ks Apr 14, 2026
5dd216f
fix: store full_name during password registration
nimish-ks Apr 14, 2026
1f593d8
fix: resolve full_name from user model for password-only accounts
nimish-ks Apr 14, 2026
92f2aa9
fix: resolve full_name in serializer for password-only accounts
nimish-ks Apr 14, 2026
3fb4dff
fix: use full_name in Slack signup notification for password users
nimish-ks Apr 14, 2026
66351b0
test: verify full_name is stored during registration and returned on …
nimish-ks Apr 14, 2026
8c0390d
test: verify auth_me returns user.full_name when no social account
nimish-ks Apr 14, 2026
7538bb2
fix: mock _smtp_configured in tests that expect email verification
nimish-ks Apr 14, 2026
3b74d6d
feat(backend): add org-level SSO data model and provider registry
nimish-ks Apr 17, 2026
7b55063
feat(backend): expose org SSO providers and enforcement via GraphQL
nimish-ks Apr 17, 2026
9a481df
feat(backend): add org-level SSO authorize view + session markers
nimish-ks Apr 17, 2026
25c015a
feat(backend): run EE OIDC adapters on cloud + add login email coverage
nimish-ks Apr 17, 2026
f70795c
feat(backend): return org SSO providers from email_check + preserve s…
nimish-ks Apr 17, 2026
634f9f6
feat(backend): enforce org SSO on GraphQL queries via middleware
nimish-ks Apr 17, 2026
2277e0c
feat(backend): add disable_org_sso management command
nimish-ks Apr 17, 2026
e6de5ec
test(backend): tests for org SSO feature
nimish-ks Apr 17, 2026
2893f9b
feat(frontend): add GraphQL queries, mutations, and generated types f…
nimish-ks Apr 17, 2026
d6e2053
feat(frontend): add SSO settings page with provider setup dialogs
nimish-ks Apr 17, 2026
0b34aae
feat(frontend): surface SSO in login, lobby, and global error handling
nimish-ks Apr 17, 2026
a1cec6a
chore: ignore .env.dev.* variants
nimish-ks Apr 18, 2026
2144437
fix(auth): correct email_check reverse accessor and surface org SSO o…
rohan-chaturvedi Apr 21, 2026
bad5e17
fix(sso): set SSO provider audit FKs to SET_NULL so config survives a…
rohan-chaturvedi Apr 21, 2026
353c40c
fix(sso): block SSRF in OIDC discovery and token exchange on cloud de…
rohan-chaturvedi Apr 21, 2026
4e3539a
fix(sso): scope org-level SocialApp rows by client_id to prevent cros…
rohan-chaturvedi Apr 21, 2026
cba08ed
fix(sso): anchor org SSO email trust to members/invites and reject un…
rohan-chaturvedi Apr 21, 2026
380259a
fix(sso): validate Entra ID token signature/issuer/audience and enfor…
rohan-chaturvedi Apr 21, 2026
47b21e3
fix(sso): close enforcement-middleware bypass on resource-ID paths
rohan-chaturvedi Apr 21, 2026
f03c693
fix(frontend): restore large OAuth provider buttons on login page
rohan-chaturvedi Apr 21, 2026
e23b7eb
fix(sso): resolve SSO identity by (provider, uid) before email to pre…
rohan-chaturvedi Apr 21, 2026
8e33c1a
fix(org): verify caller email matches invitee_email on invite acceptance
rohan-chaturvedi Apr 21, 2026
91505d4
refactor(sso): auto-discover org resolution and cache decisions in Redis
rohan-chaturvedi Apr 23, 2026
4c9fc0f
fix(org): reject invite acceptance when org_id does not match invite.…
rohan-chaturvedi Apr 23, 2026
90837dd
feat(auth): unified login + sudo password via parallel Argon2id deriv…
rohan-chaturvedi Apr 25, 2026
5fcf87a
fix(auth): require identity proof on keyring rewrap; reject invite si…
rohan-chaturvedi Apr 25, 2026
9c2b1dd
fix(sso): resolve UserToken.user as OrganisationMember PK in middlewa…
rohan-chaturvedi Apr 26, 2026
82a66a2
fix(sso): pass expected_nonce to id_token verification in EE OIDC ada…
rohan-chaturvedi Apr 26, 2026
bc5602f
fix(sso): clear stale sso_org_config_id on instance-level authorize
rohan-chaturvedi Apr 26, 2026
2bab850
fix(sso): route testOrganisationSsoProvider discovery through _safe_o…
rohan-chaturvedi Apr 26, 2026
69c09c8
fix(auth): reject protocol-relative callbackUrl after login
rohan-chaturvedi Apr 26, 2026
47a2b8c
fix(auth): verify password before active-state check to prevent email…
rohan-chaturvedi Apr 26, 2026
4f10f86
fix(sso): scope email_check SSO to invite org and disambiguate duplic…
rohan-chaturvedi Apr 26, 2026
5f5296a
fix: misc styling and ux polish to signup and onboarding screens
rohan-chaturvedi Apr 26, 2026
4c40396
feat(auth): add verifyPassword query and prompt password users for au…
rohan-chaturvedi Apr 27, 2026
e18eb17
fix(auth): always cache deviceKey for password users (hide opt-out to…
rohan-chaturvedi Apr 27, 2026
97c0de8
fix(auth): clear cached deviceKey for password users on logout
rohan-chaturvedi Apr 27, 2026
a6bee0d
feat(auth): skip password step in recovery flow when deviceKey is cached
rohan-chaturvedi Apr 27, 2026
1df7926
refactor(auth): rename ResetAccountPasswordViaRecoveryMutation to Cha…
rohan-chaturvedi Apr 27, 2026
e9ce499
fix(ui): hide stepper timeline when there is only one step
rohan-chaturvedi Apr 27, 2026
427f7b3
refactor(auth): rename ChangeAccountPasswordMutation to RecoverAccoun…
rohan-chaturvedi Apr 27, 2026
5ab6292
feat(auth): add ChangeAccountPasswordMutation for in-session password…
rohan-chaturvedi Apr 27, 2026
78ed6a5
refactor(auth): migrate change-password UI to GraphQL and GenericDial…
rohan-chaturvedi Apr 27, 2026
5df031c
fix(ui): prevent Stepper crash when steps shrink mid-flow during reco…
rohan-chaturvedi Apr 27, 2026
4d8c754
refactor(auth): authHash to Argon2id-MODERATE with versioned distinct…
nimish-ks Apr 27, 2026
812429c
Merge pull request #862 from phasehq/auth/strengthen-authhash-derivation
rohan-chaturvedi Apr 27, 2026
26eb5e0
chore: revert gitignore
nimish-ks Apr 28, 2026
07f6338
chore: update self-hosting and dev env files
nimish-ks Apr 28, 2026
4fa9722
chore: updated empty state title
nimish-ks Apr 28, 2026
63c5172
chore: comment cleanup
nimish-ks Apr 28, 2026
55b944d
chore: comment cleanup
nimish-ks Apr 28, 2026
0e064f4
fix(sso): close enforcement bypass on bare id/ids/*_data mutation kwargs
rohan-chaturvedi Apr 28, 2026
b77a408
fix(sso): refuse silent SocialAccount linking to existing email accounts
rohan-chaturvedi Apr 28, 2026
24282c2
fix(sso): pin Entra ID id_token tenant claim to configured tenant_id
rohan-chaturvedi Apr 28, 2026
3dc4c6b
fix(auth): use synthetic username for emails exceeding the 64-char co…
rohan-chaturvedi Apr 28, 2026
40de8e8
fix(sso): refuse OIDC userinfo fallback when nonce verification was e…
rohan-chaturvedi Apr 28, 2026
b423c15
fix(sso): truncate SocialApp.name to varchar(40) when constructing or…
rohan-chaturvedi Apr 28, 2026
47c87b0
fix(sso): redirect to invite wizard when org-SSO login completes with…
rohan-chaturvedi Apr 28, 2026
f243301
fix(sso): exempt SSO admin mutations from enforcement to allow lockou…
rohan-chaturvedi Apr 28, 2026
3fc2fe7
feat(auth): add ALLOW_SIGNUPS env var to gate self-service sign-ups
nimish-ks Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 33 additions & 27 deletions .env.dev.example
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
# Securely manage and sync environment variables with Phase.
# phase.dev - Keep secrets.
#
# /$$
# | $$
# /$$$$$$ | $$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$
# /$$__ $$| $$__ $$ |____ $$ /$$_____/ /$$__ $$
# | $$ \ $$| $$ \ $$ /$$$$$$$| $$$$$$ | $$$$$$$$
# | $$ | $$| $$ | $$ /$$__ $$ \____ $$| $$_____/
# | $$$$$$$/| $$ | $$| $$$$$$$ /$$$$$$$/| $$$$$$$
# | $$____/ |__/ |__/ \_______/|_______/ \_______/
# | $$
# |__/
#
# For the complete list of secrets and deployment configuration options, see:
# https://docs.phase.dev/self-hosting/configuration/envars
# Warning: For production deployments, use a more secure method than a .env file to store secrets.

# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠔⠋⣳⣖⠚⣲⢖⠙⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⡴⠉⢀⡼⠃⢘⣞⠁⠙⡆⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⢀⡜⠁⢠⠞⠀⢠⠞⠸⡆⠀⠹⡄⠀⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⢀⠞⠀⢠⠏⠀⣠⠏⠀⠀⢳⠀⠀⢳⠀⠀⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⢠⠎⠀⣠⠏⠀⣰⠃⠀⠀⠀⠈⣇⠀⠘⡇⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⢠⠏⠀⣰⠇⠀⣰⠃⠀⠀⠀⠀⠀⢺⡀⠀⢹⠀⠀⢽⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⢠⠏⠀⣰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⣇⠀⠈⣇⠀⠘⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⢠⠏⠀⢰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⢸⡀⠀⢹⡀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⢠⠏⠀⢰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠈⣇⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠛⠒⠚⠛⠒⠓⠚⠒⠒⠓⠒⠓⠚⠒⠓⠚⠒⠓⢻⡒⠒⢻⡒⠒⢻⡒⠒⠒⠒⠒⠒⠒⠒⠒⠒⣲⠒⠒⣲⠒⠒⡲⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⢧⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⣰⠃⠀⣰⠃⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠘⡆⠀⠸⡄⠀⠀⠀⠀⠀⠀⣠⠇⠀⣰⠃⠀⣴⠃⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡄⠀⠹⡄⠀⠹⡄⠀⠀⠀⠀⡴⠃⢀⡼⠁⢀⡼⠁⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣆⠀⠙⣆⠀⠹⣄⠀⣠⠎⠁⣠⠞⠀⡤⠏⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⢤⣈⣳⣤⣼⣹⢥⣰⣋⡥⡴⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀

# Replace with your domain or host
# Replace with your domain or host.
# Use the domain or IP address where users will access Phase.
HOST=localhost
HTTP_PROTOCOL=https://

# Whitelist email domains that users are allowed to sign-in with, as a comma separated list.
# Leave commented to allow all email domains
# Whitelist email domains that users are allowed to sign in with, as a comma-separated list.
# Leave commented to allow all email domains.
#USER_EMAIL_DOMAIN_WHITELIST=mydomain.com,subdomain.mydomain.com

# Frontend dev
NEXTAUTH_URL=https://localhost
OAUTH_REDIRECT_URI=https://localhost
BACKEND_API_BASE=http://backend:8000
NEXT_PUBLIC_BACKEND_API_BASE=https://localhost/service
SSO_PROVIDERS=google,github,gitlab

# WARNING: Replace these with a cryptographically strong random values. You can use `openssl rand -hex 32` to generate these.
# OAuth providers to enable on the sign-in page (optional).
# Leave empty for password-only local development.
# Example: SSO_PROVIDERS=google,github,gitlab
SSO_PROVIDERS=

# Allow new users to sign themselves up (default: true). Set to "false"
# to require an invite for any new account.
#ALLOW_SIGNUPS=true

# WARNING: Replace these with cryptographically strong random values. You can use `openssl rand -hex 32` to generate them.
NEXTAUTH_SECRET=82031b3760ac58352bb2d48fd9f32e9f72a0614343b669038139f18652ed1447
SECRET_KEY=92d44efc4f9a4c0556cc67d2d033d3217829c263d5ab7d1954cf4b5bfd533e58
SERVER_SECRET=9e760539415af07b22249b5878593bd4deb9b8961c7dd0570117549f2c4f32a2

# OAuth provider credentials. Add your own credentials here for each provider you wish to use
# OAuth provider credentials. Add credentials for each provider you enable in SSO_PROVIDERS.
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

Expand All @@ -48,6 +56,7 @@ GITLAB_CLIENT_SECRET=


# Integrations
# GitHub secret sync integration OAuth credentials.
GITHUB_INTEGRATION_CLIENT_ID=
NEXT_PUBLIC_GITHUB_INTEGRATION_CLIENT_ID=
GITHUB_INTEGRATION_CLIENT_SECRET=
Expand All @@ -57,7 +66,7 @@ ALLOWED_HOSTS=localhost,backend
ALLOWED_ORIGINS=https://localhost
SESSION_COOKIE_DOMAIN=localhost

# Database credentials. Change all these values if required, but you may need to update the healthcheck in dev-docker-compose.yml services.postgres as well
# Database credentials. Change these values as needed, but update the postgres healthcheck in dev-docker-compose.yml as well.
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=postgres-db-name
Expand All @@ -67,6 +76,3 @@ DATABASE_PASSWORD=postgres-password
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=

# Disable NextJs telemtry
NEXT_TELEMETRY_DISABLED=1
66 changes: 35 additions & 31 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
# Securely manage and sync environment variables with Phase.
# phase.dev - Keep secrets.
#
# /$$
# | $$
# /$$$$$$ | $$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$
# /$$__ $$| $$__ $$ |____ $$ /$$_____/ /$$__ $$
# | $$ \ $$| $$ \ $$ /$$$$$$$| $$$$$$ | $$$$$$$$
# | $$ | $$| $$ | $$ /$$__ $$ \____ $$| $$_____/
# | $$$$$$$/| $$ | $$| $$$$$$$ /$$$$$$$/| $$$$$$$
# | $$____/ |__/ |__/ \_______/|_______/ \_______/
# | $$
# |__/
#
# For the complete list of secrets and deployment configuration options, see:
# https://docs.phase.dev/self-hosting/configuration/envars
# Warning: For production deployments, use a more secure method than a .env file to store secrets.

# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠔⠋⣳⣖⠚⣲⢖⠙⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⡴⠉⢀⡼⠃⢘⣞⠁⠙⡆⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⢀⡜⠁⢠⠞⠀⢠⠞⠸⡆⠀⠹⡄⠀⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⢀⠞⠀⢠⠏⠀⣠⠏⠀⠀⢳⠀⠀⢳⠀⠀⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⢠⠎⠀⣠⠏⠀⣰⠃⠀⠀⠀⠈⣇⠀⠘⡇⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⢠⠏⠀⣰⠇⠀⣰⠃⠀⠀⠀⠀⠀⢺⡀⠀⢹⠀⠀⢽⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⢠⠏⠀⣰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⣇⠀⠈⣇⠀⠘⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⢠⠏⠀⢰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⢸⡀⠀⢹⡀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⢠⠏⠀⢰⠃⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠈⣇⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# ⠛⠒⠚⠛⠒⠓⠚⠒⠒⠓⠒⠓⠚⠒⠓⠚⠒⠓⢻⡒⠒⢻⡒⠒⢻⡒⠒⠒⠒⠒⠒⠒⠒⠒⠒⣲⠒⠒⣲⠒⠒⡲⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⢧⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⣰⠃⠀⣰⠃⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠘⡆⠀⠸⡄⠀⠀⠀⠀⠀⠀⣠⠇⠀⣰⠃⠀⣴⠃⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡄⠀⠹⡄⠀⠹⡄⠀⠀⠀⠀⡴⠃⢀⡼⠁⢀⡼⠁⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣆⠀⠙⣆⠀⠹⣄⠀⣠⠎⠁⣠⠞⠀⡤⠏⠀⠀⠀⠀⠀⠀⠀⠀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⢤⣈⣳⣤⣼⣹⢥⣰⣋⡥⡴⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀


# Replace with your domain or host
# Replace with your domain or host.
# Use the domain or IP address where users will access Phase.
HOST=localhost
HTTP_PROTOCOL=https://

# Whitelist email domains that users are allowed to sign-in with, as a comma separated list.
# Leave commented to allow all email domains
# Whitelist email domains that users are allowed to sign in with, as a comma-separated list.
# Leave commented to allow all email domains.
#USER_EMAIL_DOMAIN_WHITELIST=mydomain.com,subdomain.mydomain.com

# WARNING: Replace these with a cryptographically strong random values. You can use `openssl rand -hex 32` to generate these.
# WARNING: Replace these with cryptographically strong random values. You can use `openssl rand -hex 32` to generate them.
NEXTAUTH_SECRET=82031b3760ac58352bb2d48fd9f32e9f72a0614343b669038139f18652ed1447
SECRET_KEY=92d44efc4f9a4c0556cc67d2d033d3217829c263d5ab7d1954cf4b5bfd533e58
SERVER_SECRET=9e760539415af07b22249b5878593bd4deb9b8961c7dd0570117549f2c4f32a2

# OAuth providers to enable on sign-in page (remove any that aren't required)
# OAuth providers to enable on the sign-in page (optional).
# Leave empty for password-only signup and login.
# Example: SSO_PROVIDERS=google,github,gitlab
SSO_PROVIDERS=google,github,gitlab
SSO_PROVIDERS=

# Allow new users to sign themselves up (default: true). Set to "false" to
# require an invite for any new account — existing users keep signing in
# normally, and invited emails always pass through. Useful once your team
# is fully onboarded and you want to close the door on strangers.
#ALLOW_SIGNUPS=true

# OAuth provider credentials. Add your own credentials here for each provider you wish to use
# OAuth provider credentials. Add credentials for each provider you enable in SSO_PROVIDERS.
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

Expand All @@ -45,11 +51,13 @@ GITLAB_CLIENT_ID=
GITLAB_CLIENT_SECRET=

# Integrations
# GitHub secret sync integration OAuth credentials.
GITHUB_INTEGRATION_CLIENT_ID=
GITHUB_INTEGRATION_CLIENT_SECRET=

# Database credentials. Change all these values as required, except DATABASE_HOST
DATABASE_HOST=postgres # don't change this
# Database credentials. Change these values as needed, except DATABASE_HOST.
# Do not change DATABASE_HOST for the default Docker Compose setup.
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=postgres-db-name
DATABASE_USER=postgres-user
Expand All @@ -58,7 +66,3 @@ DATABASE_PASSWORD=a765b221799be364c53c8a32acccf5dd90d5fc832607bdd14fccaaaa0062ad
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=


# Disable NextJs telemtry
NEXT_TELEMETRY_DISABLED=1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ yarn-error.log*
# local env files
.env*.local
.env.dev

# vercel
.vercel

Expand Down
4 changes: 4 additions & 0 deletions backend/api/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api'

def ready(self):
from api.utils.access.org_resolution import register_invalidation_signals
register_invalidation_signals()
81 changes: 69 additions & 12 deletions backend/api/authentication/adapters/generic/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import logging
from allauth.socialaccount.providers.oauth2.views import OAuth2Adapter, OAuth2Error
from allauth.socialaccount.models import SocialAccount
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError

from api.utils.network import validate_url_is_safe


logger = logging.getLogger(__name__)
Expand All @@ -28,27 +32,58 @@ def _fetch_oidc_config(self):
if not self.oidc_config_url:
raise ValueError("OIDC configuration URL not set")

oidc_config_response = requests.get(self.oidc_config_url)
self._assert_url_safe(self.oidc_config_url)
oidc_config_response = requests.get(
self.oidc_config_url, allow_redirects=False
)
oidc_config = oidc_config_response.json()

self.access_token_url = oidc_config["token_endpoint"]
token_endpoint = oidc_config["token_endpoint"]
jwks_uri = oidc_config["jwks_uri"]
userinfo_endpoint = oidc_config["userinfo_endpoint"]
# Endpoints returned by discovery flow back out to server-side
# fetches (token exchange, userinfo, JWKS). Validate before
# trusting any of them.
self._assert_url_safe(token_endpoint)
self._assert_url_safe(jwks_uri)
self._assert_url_safe(userinfo_endpoint)

self.access_token_url = token_endpoint
self.authorize_url = oidc_config["authorization_endpoint"]
self.profile_url = oidc_config["userinfo_endpoint"]
self.jwks_url = oidc_config["jwks_uri"]
self.profile_url = userinfo_endpoint
self.jwks_url = jwks_uri
self.issuer = oidc_config["issuer"]
except Exception as e:
logger.error(f"Failed to fetch OIDC configuration: {e}")
if not self.default_config:
raise
self._set_default_config()

@staticmethod
def _assert_url_safe(url):
"""Reject URLs that resolve to private networks on cloud.
Self-hosted deployments may legitimately use internal OIDC."""
if settings.APP_HOST != "cloud":
return
try:
validate_url_is_safe(url)
except ValidationError:
raise OAuth2Error(f"URL rejected by safety check: {url}")

def _set_default_config(self):
for key, value in self.default_config.items():
setattr(self, key, value)

def _get_user_data(self, token, id_token, app):
def _get_user_data(self, token, id_token, app, expected_nonce=None):
if id_token:
return self._process_id_token(id_token, app)
return self._process_id_token(id_token, app, expected_nonce=expected_nonce)
# Userinfo fallback can't bind to the auth request — refuse if
# a nonce was issued (replay would be undetectable).
if expected_nonce is not None:
raise OAuth2Error(
"id_token required for nonce verification; refusing to "
"accept userinfo claims without it."
)
return self._fetch_user_info(token)

def _fetch_user_info(self, token):
Expand All @@ -57,13 +92,13 @@ def _fetch_user_info(self, token):
resp.raise_for_status()
return resp.json()

def _process_id_token(self, id_token, app):
jwks_response = requests.get(self.jwks_url)
jwks = jwks_response.json()
def _process_id_token(self, id_token, app, expected_nonce=None):
try:
return jwt.decode(
jwk_client = jwt.PyJWKClient(self.jwks_url)
signing_key = jwk_client.get_signing_key_from_jwt(id_token)
claims = jwt.decode(
id_token,
key=jwks,
key=signing_key.key,
algorithms=["RS256"],
audience=app.client_id,
issuer=self.issuer,
Expand All @@ -72,6 +107,19 @@ def _process_id_token(self, id_token, app):
logger.error(f"ID token validation failed: {e}")
raise OAuth2Error(f"Invalid ID token: {e}")

# OIDC nonce: anchors the ID token to the specific authorize
# request initiated by this session. Without it, a stolen token
# could be replayed. When a nonce was sent, it MUST match.
if expected_nonce is not None:
if claims.get("nonce") != expected_nonce:
logger.error(
"ID token nonce mismatch — possible replay or "
"cross-session attack"
)
raise OAuth2Error("Invalid ID token: nonce mismatch")

return claims

def pre_social_login(self, request, sociallogin):
User = get_user_model()

Expand Down Expand Up @@ -111,7 +159,14 @@ def complete_login(self, request, app, token, **kwargs):
if not id_token and isinstance(kwargs.get("response"), dict):
id_token = kwargs["response"].get("id_token")

extra_data = self._get_user_data(token, id_token, app)
expected_nonce = (
request.session.get("sso_nonce")
if hasattr(request, "session")
else None
)
extra_data = self._get_user_data(
token, id_token, app, expected_nonce=expected_nonce
)
logger.debug(
f"User authentication data received for email: {extra_data.get('email')}"
)
Expand All @@ -121,6 +176,8 @@ def complete_login(self, request, app, token, **kwargs):

return login

except OAuth2Error:
raise
except Exception as e:
logger.error(f"OIDC login failed: {str(e)}")
raise OAuth2Error(str(e))
Loading