Skip to content

feat(auth): add local Hydra login/consent/logout provider#344

Open
tvi wants to merge 3 commits into
mainfrom
t/auth
Open

feat(auth): add local Hydra login/consent/logout provider#344
tvi wants to merge 3 commits into
mainfrom
t/auth

Conversation

@tvi
Copy link
Copy Markdown

@tvi tvi commented May 25, 2026

No description provided.

ben-fornefeld and others added 2 commits May 24, 2026 18:31
Wire the dashboard to Ory through Auth.js while preserving Supabase mode behind the auth provider switch.
@tvi tvi requested review from ben-fornefeld and drankou as code owners May 25, 2026 23:58
@cla-bot cla-bot Bot added the cla-signed label May 25, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
web Ready Ready Preview, Comment May 26, 2026 12:11am
web-juliett Ready Ready Preview, Comment May 26, 2026 12:11am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 25, 2026

PR Summary

High Risk
Touches authentication, session middleware, sign-out/OAuth flows, and how all backend calls attach credentials—misconfiguration can lock users out or break API access across the app.

Overview
When AUTH_PROVIDER=ory, the dashboard switches from Supabase sessions to Auth.js (NextAuth v5) with the Ory Hydra provider at /api/auth/oauth, including JWT refresh and a post-sign-in bootstrap to POST /admin/users/bootstrap using OIDC claims.

API contract: OpenAPI adds AuthProviderBearerAuth + X-Team-ID alongside existing Supabase security; team/build/sandbox routes accept either scheme. New admin routes resolve/bootstrap Ory-linked users. Client/server code uses authHeaders() (Authorization + team header in Ory mode) everywhere infra/dashboard APIs are called.

UX & middleware: Sign-in/up/forgot-password pages redirect to hosted Ory via OryHostedAuthRedirect; sign-out goes through /api/auth/oauth/signout-flow (Hydra RP logout + signed state). Middleware runs Auth.js when Ory is enabled. oryAuthAdmin uses Ory Identity API for user/email lookups (replacing the prior fail-closed stub).

Local Hydra: Optional /oauth/login, /oauth/consent, /oauth/logout handlers auto-accept challenges (dev-oriented; ORY_LOCAL_LOGIN_* env fallbacks for bootstrap without consent-injected claims).

Deps/config: next-auth, @ory/client-fetch; expanded .env.example / lib/env validation for Ory and Auth.js secrets.

Reviewed by Cursor Bugbot for commit b20766d. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

loginChallenge: challenge,
})

const { redirect_to } = await hydra.acceptOAuth2LoginRequest({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Agentic Security Review
Severity: HIGH

The login provider route accepts any login_challenge and unconditionally authenticates it as a fixed ORY_LOCAL_LOGIN_SUBJECT, but there is no runtime enforcement that this path can only run in local/dev mode.

If this route is ever wired outside an isolated dev setup, an attacker can start an OAuth flow and receive tokens as that fixed subject, resulting in authentication bypass/account impersonation.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 95f62c7. Configure here.

Comment thread package.json Outdated
className="text-fg underline underline-offset-[3px]"
>
Continue if you are not redirected automatically
</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw button element instead of Button primitive

Low Severity

A new raw <button> element with manual Tailwind styling (text-fg underline underline-offset-[3px]) is used instead of the Button component from @/ui/primitives/button. This is new feature code that violates the rule requiring use of the Button primitive for consistent focus rings, disabled states, and accessibility.

Fix in Cursor Fix in Web

Triggered by learned rule: Use Button primitive instead of raw button elements

Reviewed by Cursor Bugbot for commit 95f62c7. Configure here.

name: keyof OryTokenClaims
): string | null {
return readStringClaim(claims, name)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readRequiredStringClaim is identical to readStringClaim

Low Severity

readRequiredStringClaim is a wrapper that does nothing beyond calling readStringClaim — it returns string | null without enforcing any "required" semantics. The name is misleading: callers reading the code at line 29 (readRequiredStringClaim(accessClaims, 'sub')) would expect it to throw or assert on missing claims, but it silently returns null just like the non-required variant.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 95f62c7. Configure here.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95f62c79a9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/configs/api.ts
Comment on lines +15 to +17
const isOry = isOryAuthEnabled()
const headers: Record<string, string> = isOry
? { Authorization: `Bearer ${token}` }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid server-only env checks in shared auth header helper

authHeaders now switches behavior via isOryAuthEnabled(), which reads process.env.AUTH_PROVIDER, but this helper is also consumed by client-side code paths introduced in this commit (for example terminal/inspect browser SDK calls). In Next.js, non-NEXT_PUBLIC_ env vars are not available in browser bundles, so client code will not reliably select the Ory branch and can keep emitting Supabase-style headers; in AUTH_PROVIDER=ory deployments that causes sandbox API calls from the browser to be unauthorized.

Useful? React with 👍 / 👎.

Wire the dashboard as Hydra's login provider so the OIDC flow can complete
end-to-end against a self-hosted Hydra (e.g. ../infra devenv) without
requiring a separate IdP UI.

- src/app/oauth/login: auto-accept login challenges as ORY_LOCAL_LOGIN_SUBJECT.
- src/app/oauth/consent: defensive auto-accept (never hit while the seeded
  client has skip_consent=true; kept for misconfiguration safety).
- src/app/oauth/logout: auto-accept logout challenges.
- src/core/server/auth/ory/hydra-admin.ts: OAuth2Api client that targets
  ORY_HYDRA_ADMIN_URL (self-hosted, no PAT) or ORY_SDK_URL (Ory Network, PAT).
- src/lib/env.ts: new optional ORY_HYDRA_ADMIN_URL and ORY_LOCAL_LOGIN_SUBJECT.
- package.json: pin 'next dev' to :3001 so it doesn't collide with the
  infra api on :3000 and matches the seeded client's redirect_uri.

Modeled on ory/hydra-login-consent-node. Intended for local/dev only;
production deployments delegate login to Ory Network / Kratos.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants