Skip to content

feat(auth): update a custom OAuth/OIDC provider as an admin - #593

Merged
Tr00d merged 1 commit into
feat/auth.admin.list_providersfrom
feat/auth.admin.update_provider
Oct 29, 2026
Merged

Tr00d merged 1 commit into
feat/auth.admin.list_providersfrom
feat/auth.admin.update_provider

Conversation

@Tr00d

@Tr00d Tr00d commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Admins can update a custom OAuth/OIDC provider. Only the settings they set are sent; every other setting keeps its current value.

Capability: auth.admin.update_provider: Admin Update Provider.

Stacked on #592 (auth.admin.list_providers), which is stacked on #591 (get) and #590 (create). This PR's base is feat/auth.admin.list_providers, so the diff shows only the update capability. Retarget it once the PRs below it merge.

Public API

// IGotrueAdminClient<TUser>
Task<CustomProvider> UpdateCustomProviderAsync(string identifier, CustomProviderChanges changes, CancellationToken cancellationToken = default);

New type in Supabase.Gotrue.UpdateCustomProvider:

Type Members
CustomProviderChanges (init-only record, every member optional) Name, ClientId, ClientSecret, Issuer, AuthorizationUrl, TokenUrl, UserInfoUrl, Options (Supabase.Gotrue.Common.CustomProviderOptions, shared with create)
using Supabase.Gotrue.Common;
using Supabase.Gotrue.UpdateCustomProvider;

var admin = new AdminClient(serviceRoleKey, new ClientOptions { Url = authUrl });
var updated = await admin.UpdateCustomProviderAsync("custom:mycompany", new CustomProviderChanges
{
    Name = "My Company",
    Options = new CustomProviderOptions { Enabled = false },
});

IGotrueApi<TUser, TSession> gains the matching UpdateCustomProviderAsync(string jwt, string identifier, CustomProviderChanges, CancellationToken).

Behavior

Operation PUT /admin/custom-providers/{identifier} with the service-role key
Input identifier, required, starting with custom:. changes, required. Null members are left out of the body, so the server keeps those values. Type and identifier cannot change. Settings of the other protocol are ignored by the server. Changing the issuer or discovery URL of an OIDC provider makes the server fetch and validate the discovery document again. If nothing is set, the server returns the provider unchanged.
Output 200 → the updated CustomProvider
Checks before the request ArgumentNullException for null changes. ArgumentException (the same check as get and delete) when the identifier lacks the case-sensitive custom: prefix or contains /, \, ?, # or %.
Failures GotrueException: Reason = CustomProviderNotFound on 404 custom_provider_not_found; ErrorCode = validation_failed on 400 (an invalid setting, a non-HTTPS or loopback URL, an OIDC discovery failure); Reason = AdminTokenRequired on 401/403; ErrorCode = feature_disabled on 404. A null body also throws GotrueException.
State None

CustomProviderChanges.ToString() leaves out the client secret.

Deviations from supabase-js

  • supabase-js puts the identifier into the URL raw. This SDK rejects identifiers that would change the route before sending anything.
  • The optional settings are grouped in the CustomProviderOptions record shared with create, instead of one flat parameter bag.

Tests

Tier Class Pins
E2E UpdateCustomProviderTests An update applies the fields sent and keeps every other setting (read back with GetCustomProviderAsync). Unknown identifier → CustomProviderNotFound. Non-admin key → AdminTokenRequired.
Contract UpdateCustomProviderContractTests Route, method and service-role credentials. Response mapping. 404 → CustomProviderNotFound. null body. Null changes rejected before I/O. A cancelled token sends nothing. The call completes when the caller blocks on a context that never runs continuations.
Contract (approval) UpdateCustomProviderApprovalTests Exact body with only a name changed ({"name":"Renamed"}) and with every member set
Unit UpdateCustomProviderIdentifierTests, CustomProviderChangesTests Identifiers that would change the route are rejected. The secret is kept out of ToString().

Changed-line coverage: 100% (36 lines on top of #592). Mutation: 5/5 detected on changed lines (4 killed, 1 timeout from the deadlock test), 0 survivors (1 compile error, 2 ignored).

Layout

packages/Gotrue/Gotrue/UpdateCustomProvider/
  CustomProviderChanges.cs
  UpdateCustomProviderBody.cs   internal: update's wire body, built on Common/CustomProviderOptionsBody from #590
packages/Gotrue/Gotrue.Tests/UpdateCustomProvider/   E2E, contract, approval, unit tests, Data/*.verified.txt

Update reuses Common/ (CustomProviderOptions, CustomProviderOptionsBody, CustomProvider) as it is and moves nothing.

Notes

@Tr00d
Tr00d force-pushed the feat/auth.admin.list_providers branch from 796f94f to 071053b Compare October 29, 2026 14:28
@Tr00d
Tr00d force-pushed the feat/auth.admin.update_provider branch from fa61ee8 to db15b90 Compare October 29, 2026 14:28
@Tr00d
Tr00d marked this pull request as ready for review October 29, 2026 14:42
@Tr00d
Tr00d merged commit 6b3fa67 into feat/auth.admin.list_providers Oct 29, 2026
1 check passed
@Tr00d
Tr00d deleted the feat/auth.admin.update_provider branch October 29, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant