Unified authentication: a single sign-in surface for portal and admin#284
Conversation
…hods
- New script set-portal-auth-methods.ts (disable/restore/enable-magic-link)
reads and patches settings.portal_config.oauth in the DB, mirroring the
set-workspace-anon.ts pattern
- Case (d) now disables all portal auth methods before navigating, asserts
the team form still renders, and restores in a finally block
- Case (c) wraps loginViaMagicLink in enable-magic-link/restore so the
portal magic-link hook does not silently drop the send on repeat runs
(existing role='user' principals are blocked by hooks.ts:277-279 when
portal magicLink is off by default)
- Case (c) now passes { role: 'user' } explicitly to loginViaMagicLink
- Added flushMagicLinkRateLimit helper (execFileSync, no shell) + beforeAll
- Added comment to case (e) confirming /auth/login loader does not resolve
the invitation id — only isTeamCallback/isSafeCallbackUrl are called
… the provider list
…config domain normalization, per-provider test/enforcement, onboarding registry read)
…rallelize test stamps
…, single-source button predicate, non-sso enforce UI dead-end)
…-code break-glass link
Move the recoveryLink element outside the emailEntryEnabled guard in Stage 1 so it renders whenever showRecoveryLink is true, regardless of whether password or magic-link is enabled. SSO-only workspaces (password=false, magicLink=false) previously hid the link entirely because the emailEntryEnabled block was skipped. Adds two tests: recovery link absent when callbackUrl is undefined, and recovery link visible in SSO-only Stage 1 with a team callbackUrl.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
- backfill: materialize legacy team magic-link when password is explicitly disabled so passwordless workspaces aren't migrated to zero sign-in methods, while preserving an explicit magicLink:false - backfill: bump authConfigVersion after creating the custom-oidc provider - config-file: accept and ignore deprecated auth/features keys instead of failing the whole watcher; warn and surface the ignored keys in status - idp: extend the SSRF guard and detailsChangedAt restamp to the manual authorization/token/userinfo endpoints, not just discoveryUrl
There was a problem hiding this comment.
💡 Codex Review
When a private portal visitor clicks the gate's Sign in / Register CTA (rather than the auto-open path) and then enters an email routed to SSO, the dialog receives no callback and PortalAuthFormInline defaults the same-tab OAuth callback to /. That path does not use the popup broadcast that would keep the user on the gated page, so routed SSO returns them to the root instead of the private URL; pass the safe/current callback here as the auto-open branch does.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
After sign-in the header goes straight from the Log in / Sign up buttons to the logged-in avatar once the session refetch lands. Drops the signingIn bridge, its render branch, and the now-unused ArrowPathIcon.
…plumbing The auth dialog now handles TOTP challenge and enrollment inline (Task 4); the standalone /auth/two-factor and /auth/two-factor-setup-required pages are obsolete. No-op onTwoFactorRedirect so Better-Auth never navigates away. Delete all related sessionStorage helpers (stash/read/clear/resolve) and their test file. Remove /auth/two-factor-setup-required from isTeamCallback's prefix list and update the routing test accordingly.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57d509e5a7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- idp: reject enabling a provider with no usable OAuth endpoint source (require a discovery URL, or both authorization + token URLs) so a sole-IdP workspace can't publish a broken login flow - credentials: enforce the last-working-method invariant before deleting provider credentials (the low-level updateAuthConfig bypassed the guard) - sso-test: request the provider's configured scopes instead of a hardcoded set, so a test mirrors production and can't unlock enforcement spuriously - instant-sso: fall back to the requested deep link (location.pathname) so a sole-IdP redirect returns the user to the private route they asked for
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26f2277570
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Providers configured with manual endpoints (authorization + token, no discovery document — e.g. migrated custom-OIDC) could sign in but the SSO test rejected them as not-configured, so they could never be tested and therefore never enforced (Codex P2). - add jwks_uri + issuer columns to identity_provider (migration 0117) so a discovery-less provider carries what the test needs to verify the ID token - thread jwksUri/issuer through the service type, zod input, SSRF guard (jwks_uri is server-fetched), and the detailsChangedAt restamp - startSsoTestFn + the handshake now resolve endpoints from the discovery doc OR the stored manual endpoints; discovery providers are unchanged - provider editor gains a collapsible "Manual endpoints" section for the Other kind (authorization/token/userinfo/jwks/issuer)
- _portal loader: optional-chain location.pathname so a missing location (e.g. in unit tests) can't crash the portal-access path - sso-test-callback test: assert the manual-endpoint fields now threaded to runHandshake (tokenEndpoint / jwksUri / issuer)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7d09cd574
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- idp: registrationId is immutable on update — keep the stored id rather than the caller's, so an edit can't orphan the secret/accounts/redirect URI (or slip past the enabled-based last-method guard) - idp: restrict registrationId to the oidc_ namespace (+ legacy sso/custom-oidc) so a provider can't register under a built-in method id like `credential` and bypass a disabled built-in toggle via the registered-OIDC short-circuit - sso-test: capture the provider's detailsChangedAt in the test session and only stamp lastSuccessfulTestAt when it's unchanged at callback time, so a mid-test edit can't let a stale test unlock enforcement for a new config
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 844d1804a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- auth dialog: closing mid-2FA (X / backdrop / Esc) now revokes the session signIn.email created, not just the in-form Cancel button. Without it a required-2FA user who never finishes enrollment kept a valid, un-enrolled session and bypassed the policy on the next navigation. Success closes programmatically (no onOpenChange), so this only fires on a real abandon. - auth-method-count: seed the count with the default-on password (absent oauth.password means on), so the settings UI no longer undercounts methods and wrongly locks the Remove/Off control on the last IdP/social provider.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The CI `test` job intermittently failed with an unhandled "window is not defined": a setState fired after the test env was torn down. - two-factor-enroll-steps: re-check the unmount flag after the QR await so a late-resolving enable/QR chain can't setState on an unmounted component - enroll-steps test: stub the OTP input (as the sibling dialog test does) — the real input-otp schedules effects/RAF that outlive the test and trip the teardown race; a plain controlled input keeps the run deterministic
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Post-review cleanup of the unified-auth changes. No behavior change except the deliberate decision that config-file can no longer lock the auth/feature toggles (that management was already removed upstream). - drop dead markSsoDetailsChanged + its stale test mock - remove 6 orphaned portal.auth.* i18n keys across all locales - remove the unused docUrl field from idp-shortcuts - unify the duplicated settings-oauth JSON parser across both backfills - share DEFAULT_OIDC_SCOPES between production and the SSO test flow - remove vestigial "managed by config" badges/disabled-state on the sign-in providers and Labs tabs (config no longer manages these) - stop the login dispatcher fetching configured types twice - dedupe the 6-slot OTP group, isOnlyMethod, and the _portal auth-prompt parse; inline a single-use redirect helper; hoist a duplicated import
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Replace the private-portal gate's intermediate "Sign in / Register" card and
modal with the shared auth form rendered directly as a dedicated sign-in
screen, so visitors land straight on it instead of clicking through.
- Extract headerForStep into auth-step-header.tsx with a `surface` option.
The private gate uses private-portal framing copy ("Sign in to access X" /
"This portal is private...") instead of the public "vote and comment"
tagline; the public dialog is unchanged.
- The gate seeds the form mode from ?auth=signin|signup and keeps the
post-sign-in broadcast-to-loader-rerun bridge (now swapping the whole form
for a "Signing in..." state).
- Preserve the 2FA-abandon session revoke: the modal does it on close, the
inline form revokes on unmount while mid-2FA (skipped once sign-in succeeds).
- Replace the gate's skeleton backdrop with a blurred faux feedback board
built from hardcoded mock data. No real content reaches the browser and
every element is inert (aria-hidden, nothing focusable).
Tests cover the inline render, mode seeding, the signing-in bridge, the
2FA-abandon revoke, and the backdrop inertness invariant.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Closes #270 — one sign-in surface for admins and regular users (with an "Admin area" entry in the user dropdown), and a single SSO/sign-in config governing both.
What this does
Consolidates authentication onto a single sign-in surface for the portal and the admin team, and tidies up the Security settings that drive it.
/auth/loginand/auth/signuppages. Old login redirects point at it, and the private-portal gate auto-opens it while honouringcallbackUrl.callbackUrlvalidation against open-redirects.Security settings (latest commit)
Testing
bun run typecheck: clean.bun run test apps/web/src/components/admin/settings/security: 42 tests pass across 7 files.eslinton the changed files: clean.