feat(dashboard-api): add Ory user profile provider and auth middleware fix#2840
Conversation
…e fix Wire dual Supabase/Ory profile resolution, enrich team member responses, and silence per-scheme auth negotiation noise so Ory mode can deploy independently of OIDC bootstrap provisioning.
PR SummaryMedium Risk Overview In Reviewed by Cursor Bugbot for commit 24c13e8. Bugbot is set up for automated code reviews on this repo. Configure here. |
❌ 4 Tests Failed:
View the full list of 5 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
Code Review
In packages/dashboard-api/internal/userprofile/ory.go, the oidc.Config field in ory.IdentityCredentials is of type interface{} and cannot be indexed directly. Attempting to access oidc.Config["providers"] will cause a compile-time error, so oidc.Config must be type-asserted to map[string]any before retrieving the providers list.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ddbe15f3b9
ℹ️ 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".
Ory's admin ListIdentities rejects an `ids` filter with more than 500 entries (400 Bad Request) and does not paginate id-filtered results, so the previous batch size of 1000 would fail any profile lookup touching more than 500 users in a single request. Chunk lookups at 500 and drop the no-op PageSize call (ignored for the ids filter). Also dedupe the two identity-resolver mappings into a generic userIDsBySubject helper.
Fix the Ory identity id-filter batch size: the admin ListIdentities `ids` filter rejects more than 500 entries (400) and is not paginated, so cap batches at 500 instead of 1000. Cleanups: - dedupe the two identity-resolver mappings into a generic userIDsBySubject - extract uniqueNonEmpty to drop the duplicated provider-dedup closures shared by oryLinkedProviders and supabaseLinkedProviders - promote FirstNonEmpty to shared/pkg/utils and reuse it (removes the handlers -> userprofile coupling for a generic string helper) - reuse utils.Map and Go 1.26 new(expr) in place of hand-rolled loops and pointer temporaries - trim redundant/what-explaining comments
There was a problem hiding this comment.
💡 Codex Review
When USER_PROFILE_PROVIDER=ory and an authenticated Ory-only user calls POST /teams, this path still reads auth.users from the Supabase connection before creating the team. Those users are resolved through public.user_identities and may not have a Supabase auth.users row, so additional team creation fails with get auth user even though the same commit moved bootstrap/member profile lookups to s.userProfiles; fetch the email through the configured profile provider here as well.
ℹ️ 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".
Use the SDK's recommended per-request token mechanism instead of a custom RoundTripper that hand-set the Authorization header. The generated client turns a ContextAccessToken value into the bearer header, so the custom oryBearerTransport is removed and each call passes p.authCtx(ctx). Keep the shallow copy of the injected http.Client so the SDK can never mutate the caller's shared instance; the token now lives only in the request context, never on the client. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 700b3e6a63
ℹ️ 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".
| member.Name = new(profile.Name) | ||
| } | ||
| if profile.ProfilePictureURL != "" { | ||
| member.ProfilePictureUrl = new(profile.ProfilePictureURL) |
There was a problem hiding this comment.
Use addressable locals for optional profile fields
When a team member has a non-empty name or profile picture, these assignments call Go's new builtin with a value expression (profile.Name / profile.ProfilePictureURL), but new only accepts a type operand, so the dashboard-api package does not build. Store the string in a local variable and take its address, or use an existing pointer helper.
Useful? React with 👍 / 👎.
Drop what-restating prose and rely on symbol names; keep only the non-obvious context (external Ory data shapes, the issuer/oidc_iss invariant, the shared-client safety note). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
infra/packages/dashboard-api/internal/handlers/utils_team_provisioning.go
Lines 167 to 169 in a31b5f1
When USER_PROFILE_PROVIDER=ory and an Ory-authenticated user calls POST /teams, this path still requires a matching row in Supabase auth.users, so Ory-only users who can authenticate and bootstrap their default team hit get auth user and cannot create additional teams. This should use s.userProfiles (as the bootstrap path now does) or otherwise avoid making Supabase the source of truth in Ory mode.
ℹ️ 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".
createTeam (POST /teams) read the creator's email straight from Supabase auth.users, so in ory mode an Ory-only user with no auth.users row could authenticate and bootstrap a default team but failed to create additional teams with "get auth user". Resolve the profile through the configured provider, matching the bootstrap path. Extract the shared lookup into resolveProfile so bootstrap and team creation use one provider-backed path, and set userProfiles in the team-creation tests that previously only needed supabaseDB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop `picture` from the identity trait schema so it no longer renders on Ory's self-service registration form, and read it from the identity's metadata_public instead. The OIDC Jsonnet mapper continues to populate it from the provider claims; only its destination moves from traits to metadata_public. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42d5fb6f74
ℹ️ 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".
| Ids(batchIDs). | ||
| IncludeCredential([]string{"oidc"}), |
There was a problem hiding this comment.
Request 500 identities for each Ory id batch
When a team has more than 250 Ory-backed members, this lookup sends up to 500 ids but leaves Ory's page_size at its documented default of 250 (max 500, and the ids filter does not support normal pagination: https://www.ory.com/docs/kratos/reference/api#tag/identity/operation/listIdentities). The remaining requested identities are omitted, so GetTeamsTeamIDMembers logs them as missing profiles and silently drops members from the response; set PageSize(oryListIDsBatchSize) on the id-filtered request.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: caf2a18744
ℹ️ 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".
|
|
||
| // stamp 401 so the ErrorHandler's max(writer, 400) resolves to 401 | ||
| // when every security group fails. without this, auth failures become 400s. | ||
| ginCtx.Status(http.StatusUnauthorized) |
There was a problem hiding this comment.
Defer stamping 401 until all auth alternatives fail
For dashboard routes with two alternative security requirements (for example /builds in spec/openapi-dashboard.yml lists Supabase and AuthProvider as alternatives), a request that uses valid AuthProvider headers still first tries the Supabase group; this line leaves c.Writer.Status() at 401 when the Supabase header is missing. If that later-authenticated request then fails normal request validation, packages/dashboard-api/main.go uses max(c.Writer.Status(), statusCode), so a real 400 is returned as 401 even though auth succeeded. Only stamp 401 when the whole authentication validation fails, or reset the status after a successful alternative.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b088383. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14f9c33dfa
ℹ️ 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".

Summary
userprofile.Providerwith dual Supabase/Ory mode, IaC wiring (ORY_*,USER_PROFILE_PROVIDER), and team member enrichment (name,picture,providers).Stack
This is PR 1 of 2 for the Ory dashboard auth migration. PR 2 (
ory-bootstrap) adds the OIDC admin bootstrap endpoint and stacks on this branch.