feat: auth flow via same-origin ory elements#438
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
the oryHandler existed for the following case:
- token refresh happens one step before (inside the middleware)
- if the middleware refreshed the token, it appended a Set-Cookie, which resulted in procedures that have been called after the refresh but before the Set-Cookie reached the client, to use an already invalidated access token.
does the new architecture account for this?
There was a problem hiding this comment.
this was a known issue with authjs and they added helpers for this, which we were using. it might be that we don't have this case anymore now, just want to make sure we have this tracked
There was a problem hiding this comment.
yes, I was addressing this explicitly during the implementation. worth double check
There was a problem hiding this comment.
up to exploration: is there certain libraries we should consider using around the oauth state management ?
There was a problem hiding this comment.
we're using oauth4webapi lib for oauth flow, the rest is handled by ory's useFlow()
| // Kratos owns the session. Identity (the gate) comes from `whoami`; the Hydra | ||
| // access token (API access only) comes from the e2b_session cookie, which the | ||
| // middleware keeps fresh. For E2B the OAuth subject equals the Kratos identity | ||
| // id, so kratos.identity.id is both the dashboard user id and the Kratos id used | ||
| // for admin operations. This module never refreshes — it is a pure reader. |
There was a problem hiding this comment.
the AuthUser.id we are using inside the dashboard state management should ideally map to E2B internal user id (public.users.id).
do we still account for that?
There was a problem hiding this comment.
we haven't matched it before afaik, and we shouldn't need this since kratos is source of truth for session, and API token has (iss, sub) pair included
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b769ea78d
ℹ️ 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".
AuthUser.id is now always public.users.id (the Kratos identity's external_id) rather than the Kratos identity id, and a new identityId field carries the Kratos id for admin/Kratos operations. This fixes PostHog identify, tRPC telemetry, and team resolution, which all expect public.users.id. - identity.ts mappers require external_id (no silent fallback to the Kratos id) and set identityId. - getAuthContext refuses a session whose identity has no external_id; the edge gate (isKratosSessionActive) rejects it too, so the user is routed to /sign-in instead of looping, where a fresh login re-runs bootstrap and backfills external_id. - Drop the updateUser override that re-stamped the Kratos id over id. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Summary
Replaces the NextAuth (Auth.js) layer with a first-party, same-origin auth flow built directly on Ory. The dashboard now acts as Hydra's OAuth2 client and its consent/logout provider, drives an authorization-code + PKCE flow, and carries the resulting OIDC tokens in a single JWE-encrypted
e2b_sessioncookie. Kratos owns the session; the cookie is just a token cache, never the auth gate.This removes
next-auth/@auth/coreentirely and the Auth.js request-wrapping that had been fighting our sign-out and tRPC code paths.Why
authjs-boundary,authjs-session-boundary,authjs-callbacks).What changed
Auth flow (new routes)
GET /api/auth/oauth/start— builds the Hydra authorization request (PKCE S256, state, nonce), stashes the verifier/state/nonce in a short-lived httpOnly flow cookie, and 307-redirects to/oauth2/auth.GET /api/auth/oauth/callback/ory— validates state/nonce/PKCE, exchanges the code, bootstraps the dashboard user from the id_token, seals tokens intoe2b_session. Failures route to the one-shotrecoverguard so a stale callback can't loop.GET /consent— dashboard is Hydra's consent provider; accepts the consent challenge and folds the identity's profile traits into the id_token (without this, tokens carrysub/issbut no email/name and bootstrap rejects the login).GET /logout— dashboard is Hydra's logout provider; accepts the logout challenge, routes through Kratos' own logout first soory_kratos_sessionis cleared before Hydra finalizes.sign-outroute now reads the id_token frome2b_sessionto build the Hydra RP-logout URL and clears the cookie on the redirect.Session / tokens
e2b_sessioncookie: JWE (dir+A256GCM) viajose, carrying access/refresh/id tokens. Keyed by newE2B_SESSION_SECRET(replacesAUTH_SECRET); domain-scoped so it works across the cluster's subdomains.token-refreshmodule refreshes the Hydra access token edge-side (direct token-endpoint call, refresh skew, dead/unchanged/refreshed states).kratos-session-edgehelper does an edge-safe Kratoswhoamifor the middleware redirect gate; authoritative enforcement stays ingetAuthContext.UI
/login,/registration,/recovery,/verification) are now always same-origin — theNEXT_PUBLIC_ORY_CUSTOM_UIflag and the legacy redirect fallbacks are gone.next/linkto plain<a>so the cross-origin start redirect stays a top-level document navigation (a soft nav would turn it into a CORS request Hydra rejects).Plumbing
auth(); auth is resolved per-procedure by the auth middleware (Kratos whoami +e2b_session).src/auth.ts, the Auth.js boundary/callback shims,next-auth.d.ts, and the[...nextauth]/bootstrap-failedroutes.Dependencies
next-auth(+@auth/coreand its transitive@panva/hkdf,preact*).joseandoauth4webapi(direct, version-pinned).Env
E2B_SESSION_SECRET, optionalORY_HYDRA_PUBLIC_URL(self-hosted Hydra issuer).AUTH_SECRET,AUTH_TRUST_HOST,NEXT_PUBLIC_ORY_CUSTOM_UI.Testing
New unit + integration coverage for the rewritten flow:
oauth-client,session-cookie,token-refresh,kratos-session-edge(unit) andauth-ory-callback,auth-ory-consent,auth-ory-logout, plus the retained entrypoints/account-security suites (integration). The Auth.js session-boundary test was removed with its module.Notes for reviewers
E2B_SESSION_SECRET(openssl rand -hex 32) in every environment before merge —AUTH_SECRETis no longer read.