feat(auth): custom Ory Elements login UI#380
Conversation
Replace the stock Kratos self-service UI for the login flow with a custom @ory/elements-react page at /login, styled with the dashboard's own design system (no Ory default theme CSS). - add @ory/elements-react + @ory/nextjs deps - src/configs/ory.ts: OryClientConfiguration (login-only scope) - src/app/login: server page (getLoginFlow + per-request sdk.url = own origin), client <Login> wrapper, layout, and component overrides for card/header/ input/button/label/sso/form-group/message - src/proxy.ts: wire @ory/nextjs createOryMiddleware so /self-service etc. proxy to Kratos same-origin (first-party cookies, no CORS) - env: NEXT_PUBLIC_ORY_SDK_URL (browser-facing Kratos public URL)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Add svgl Google/GitHub logos via shadcn registry (@svgl) - Show provider logo + 'Continue with …' on Ory SSO buttons - GitHub logo toggles light/dark variants via Tailwind dark: variant - Replace bare login divider with an 'or' separator
- Resolve provider via node.attributes.value (stable id) so the logo + 'Continue with …' label render (the provider prop is a display label) - Remove logos/index.ts barrel in favor of direct imports
- Add /register route (page, layout, card) mirroring /login, using @ory/elements-react <Registration> + getRegistrationFlow - Make shared card header/footer/submit-button flow-aware (Sign in vs Sign up; registration footer shows sign-in link + legal copy) - Enable registration in ory config, point registration_ui_url at /register
- Add 'Don't have an account? Sign up' footer to the login card (-> /register) - Move GitHubLogo into oauth-provider-buttons and drop the shared logos/github-logo.tsx; remove GitHub from the Ory SSO provider map
Rename /register route to /registration and update registration_ui_url and the login footer 'Sign up' link to match.
…n-flows # Conflicts: # src/proxy.ts
…to Ory proxy - Drop logos/google-logo.tsx wrapper; oauth-provider-buttons and the Ory SSO button now use the @svgl Google icon directly with sizing - Pass oryConfig.project to createOryMiddleware so Kratos redirect/body URLs are rewritten onto our /login and /registration UIs
- Add isOryCustomUiEnabled() (Ory enabled && VERCEL_ENV !== production) - Gate the proxy Ory-SDK short-circuit and the /login + /registration pages on it; production keeps Ory auth on but uses its existing flow - Drop redundant component-label comments, keeping only core why-notes
proxy.ts now imports createOryMiddleware (@ory/nextjs/middleware), whose ESM bare-imports 'next/server'; inline it like next-auth so vitest resolves Next's exports. Fixes proxy.test.ts and auth-ory-entrypoints.test.ts.
Replace the VERCEL_ENV-based gate with an explicit NEXT_PUBLIC_ORY_CUSTOM_UI flag. isOryCustomUiEnabled() is read in Edge middleware, where VERCEL_ENV/AUTH_PROVIDER are inlined at build time and resolve to undefined in standalone/self-hosted builds (anything but `next dev`) — silently disabling the same-origin Ory proxy. A NEXT_PUBLIC_ flag is readable at request time everywhere and lets the local harness opt in explicitly while production leaves it unset (keeping /sign-in).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 16c3e0b9bb
ℹ️ 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".
| if (isOryCustomUiEnabled() && isOrySdkProxyPath(request.nextUrl.pathname)) { | ||
| return oryProxy(request) |
There was a problem hiding this comment.
Move the Ory proxy check before the public-route bypass
When the custom UI submits or creates flows under /self-service/..., classifyProxyRequest marks those paths as public with needsOryAuthJsSession: false (checked in src/core/server/http/proxy-plan.ts), so the earlier return proxyCore(request, plan) runs before this new Ory proxy branch is reached. In the staging/preview custom-UI context, requests like /self-service/login/browser therefore fall through to Next instead of being forwarded to Kratos, breaking login/registration flow creation and form submission; this branch needs to run before the !plan.needsOryAuthJsSession bypass.
Useful? React with 👍 / 👎.
…pass /self-service/* classifies as public (needsOryAuthJsSession: false), so the short-circuit ran too late and those requests fell through to Next, breaking custom-UI flow creation and form submits. Move the gated Ory-SDK proxy branch to the top of proxy(). Add a regression test covering both enabled/disabled.
Replace the stock Kratos self-service UI for the login flow with a custom @ory/elements-react page at /login, styled with the dashboard's own design system (no Ory default theme CSS).
Gating (opt-in flag)
The custom Elements UI is gated behind
isOryCustomUiEnabled()insrc/configs/flags.ts:Ory auth itself is unchanged in production — only the new custom UI is gated off there. The gate lives in two places:
src/proxy.ts— the Ory-SDK same-origin short-circuit only runs when the flag is on (production proxy behaves exactly as before)./loginand/registrationpages — when the flag is off, theyredirect()to the existing/sign-in//sign-upflow instead of rendering the custom card.The custom UI activates wherever
AUTH_PROVIDER=oryandNEXT_PUBLIC_ORY_CUSTOM_UI=1(set in staging/preview and by the local dev harness); production leaves the flag unset and keeps Ory's existing login.Why an explicit
NEXT_PUBLIC_flag instead ofVERCEL_ENV:isOryCustomUiEnabled()runs in Edge middleware, where non-public env vars likeVERCEL_ENV/AUTH_PROVIDERare inlined at build time and resolve toundefinedin standalone/self-hosted builds (anything butnext dev) — which would silently disable the same-origin proxy. ANEXT_PUBLIC_flag is read at request time in middleware, server, and client alike.Login
Register
NEXT_PUBLIC_ORY_CUSTOM_UI(wasVERCEL_ENV)