From 9f5b36fb3b35b474f8225acaf507d1c57a319a28 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 20 Jul 2026 14:35:19 +0200 Subject: [PATCH] docs(dart): OAuth server, custom providers, realtime heartbeat & explain reference updates (#47728) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > **Stacked on #47994** (`docs/dart-reference-categories-in-yaml`). Review/merge that first; GitHub will retarget this to `master` once it lands. ## What Adds Dart client library reference entries (`apps/docs/spec/supabase_dart_v2.yml`) for features shipped in [`supabase/supabase-flutter`](https://github.com/supabase/supabase-flutter) (parity with `supabase-js`). Rebuilt on the **new reference pipeline** (#47224 / #47994): each method's section comes from `category` / `subcategory` fields on its own YAML entry, with subcategory overviews as committed partials under `spec/reference/dart/v2/partials/`. As a result this PR no longer touches `common-client-libs-sections.json` or `supabase_js_v2.yml` (the earlier shared-nav id rename is unnecessary now that Dart no longer reads that file). ## Changes **Auth (OAuth 2.1 server)** — new **OAuth Server** section - `oauth.getAuthorizationDetails()`, `oauth.approveAuthorization()`, `oauth.denyAuthorization()` **Auth admin** — new **Custom Provider Admin** section - `admin.customProviders.listProviders/createProvider/getProvider/updateProvider/deleteProvider`, including `customClaimsAllowlist` **Realtime** - `onHeartbeat` - `onPostgresChanges` examples for the new pattern/negated filter operators, multiple filters, and column selection **Postgrest** - `explain()` `format` option (`ExplainFormat.text` / `.json`) ## Pipeline plumbing - New partials: `oauth-server.json`, `custom-provider-admin.json` - `generate-dart-reference.ts`: registers `oauth-server-api` and `admin-custom-providers-api` group-header ids in `HEADER_IDS` ## Source PRs supabase-flutter: #1499, #1516, #1517, #1519, #1526 ## Verification `pnpm codegen:references:new` builds cleanly and the nav renders the new **OAuth Server**, **Custom Provider Admin**, and Realtime **onHeartbeat** entries. ## Notes - `RealtimeChannelConfig.replicationReady` (#1526) is omitted since there is no reference slot for channel-config options. - The **OAuth Server** section also appears in #47971 (which adds `listGrants` / `revokeGrant`). Whichever lands second should drop the duplicate section header/partial and keep both sets of methods. ## Summary by CodeRabbit * **New Features** * Added OAuth 2.1 server consent-flow methods for retrieving, approving, and denying authorization requests. * Added admin APIs for managing custom OIDC/OAuth providers. * Added Realtime heartbeat monitoring and advanced Postgres change filters. * Added text and JSON output options for query explanations. * **Documentation** * Expanded Dart API reference coverage across Auth, MFA, Passkeys, Database, Realtime, and Storage. * Added dedicated reference sections for OAuth Server and Custom Provider administration. --- apps/docs/scripts/generate-dart-reference.ts | 2 + .../v2/partials/custom-provider-admin.json | 5 + .../dart/v2/partials/oauth-server.json | 5 + apps/docs/spec/supabase_dart_v2.yml | 265 ++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 apps/docs/spec/reference/dart/v2/partials/custom-provider-admin.json create mode 100644 apps/docs/spec/reference/dart/v2/partials/oauth-server.json diff --git a/apps/docs/scripts/generate-dart-reference.ts b/apps/docs/scripts/generate-dart-reference.ts index df8c063e9c3..ae0d51361fd 100644 --- a/apps/docs/scripts/generate-dart-reference.ts +++ b/apps/docs/scripts/generate-dart-reference.ts @@ -61,6 +61,8 @@ const HEADER_IDS = new Set([ 'passkey-api', 'admin-api', 'admin-passkey-api', + 'oauth-server-api', + 'admin-custom-providers-api', 'functions-api', 'database-api', 'realtime-api', diff --git a/apps/docs/spec/reference/dart/v2/partials/custom-provider-admin.json b/apps/docs/spec/reference/dart/v2/partials/custom-provider-admin.json new file mode 100644 index 00000000000..4a2337aa94c --- /dev/null +++ b/apps/docs/spec/reference/dart/v2/partials/custom-provider-admin.json @@ -0,0 +1,5 @@ +{ + "id": "custom-provider-admin", + "title": "Custom Provider Admin", + "notes": "- Methods under the `supabase.auth.admin.customProviders` namespace manage custom OIDC/OAuth providers programmatically. Requires a `secret` key.\n- These are admin methods and should be called on a trusted server. Never expose your `secret` key in the Flutter app.\n- Custom providers are referenced with a `custom:` prefix when signing in (for example `custom:mycompany`), and are distinct from the OAuth 2.1 server clients managed through `supabase.auth.admin.oauth`.\n" +} diff --git a/apps/docs/spec/reference/dart/v2/partials/oauth-server.json b/apps/docs/spec/reference/dart/v2/partials/oauth-server.json new file mode 100644 index 00000000000..4d8ea90b2b2 --- /dev/null +++ b/apps/docs/spec/reference/dart/v2/partials/oauth-server.json @@ -0,0 +1,5 @@ +{ + "id": "oauth-server", + "title": "OAuth Server", + "notes": "Methods under the `supabase.auth.oauth` namespace are used when your Supabase project acts as an OAuth 2.1 server. They drive the user-facing consent flow and require a signed-in user. The OAuth 2.1 server feature must be enabled in your Supabase Auth configuration.\n" +} diff --git a/apps/docs/spec/supabase_dart_v2.yml b/apps/docs/spec/supabase_dart_v2.yml index 67e2df89ca6..33db2972de4 100644 --- a/apps/docs/spec/supabase_dart_v2.yml +++ b/apps/docs/spec/supabase_dart_v2.yml @@ -2335,6 +2335,81 @@ functions: final Session? session = res.session; final User? user = res.user; ``` + - id: oauth-server-api + title: 'OAuth Server' + category: Auth + subcategory: OAuth Server + notes: | + Methods under the `supabase.auth.oauth` namespace are used when your Supabase project acts as an OAuth 2.1 server. They drive the user-facing consent flow and require a signed-in user. The OAuth 2.1 server feature must be enabled in your Supabase Auth configuration. + - id: oauth-get-authorization-details + title: 'oauth.getAuthorizationDetails()' + notes: | + Retrieves details about a pending OAuth authorization request so you can render a consent screen. The `authorizationId` is provided as a query parameter on the redirect URL that starts the flow. + - Returns a sealed `OAuthAuthorizationResponse`. Handle both variants: `OAuthAuthorizationDetailsResponse` carries the requesting `client` and requested `scope` for the consent screen, while `OAuthAuthorizationRedirectResponse` is returned when the user has already granted consent and only carries a `redirectUrl` to forward to. + params: + - name: authorizationId + isOptional: false + type: String + description: The unique identifier of the pending authorization request. + examples: + - id: get-authorization-details + name: Get authorization details + isSpotlight: true + code: | + ```dart + final authorizationId = + Uri.parse(currentUrl).queryParameters['authorization_id']!; + + final response = + await supabase.auth.oauth.getAuthorizationDetails(authorizationId); + + switch (response) { + case OAuthAuthorizationRedirectResponse(:final redirectUrl): + // The user already consented; forward them without a consent screen. + break; + case OAuthAuthorizationDetailsResponse(:final client, :final scope): + // Render a consent screen for `client` requesting `scope`. + break; + } + ``` + - id: oauth-approve-authorization + title: 'oauth.approveAuthorization()' + notes: | + Approves a pending OAuth authorization request on behalf of the signed-in user. The response contains the redirect URL the user should be sent to. + params: + - name: authorizationId + isOptional: false + type: String + description: The unique identifier of the pending authorization request. + examples: + - id: approve-authorization + name: Approve authorization + isSpotlight: true + code: | + ```dart + final consent = + await supabase.auth.oauth.approveAuthorization(authorizationId); + // Redirect the user to consent.redirectUrl + ``` + - id: oauth-deny-authorization + title: 'oauth.denyAuthorization()' + notes: | + Denies a pending OAuth authorization request on behalf of the signed-in user. The response contains the redirect URL the user should be sent to. + params: + - name: authorizationId + isOptional: false + type: String + description: The unique identifier of the pending authorization request. + examples: + - id: deny-authorization + name: Deny authorization + isSpotlight: true + code: | + ```dart + final consent = + await supabase.auth.oauth.denyAuthorization(authorizationId); + // Redirect the user to consent.redirectUrl + ``` - id: admin-api title: 'Overview' category: Auth @@ -2886,6 +2961,122 @@ functions: passkeyId: '34e770dd-9ff9-416c-87fa-43b31d7ef225', ); ``` + - id: admin-custom-providers-api + title: 'Custom OIDC/OAuth Provider Admin API' + category: Auth + subcategory: Custom Provider Admin + notes: | + - Methods under the `supabase.auth.admin.customProviders` namespace manage custom OIDC/OAuth providers programmatically. Requires a `secret` key. + - These are admin methods and should be called on a trusted server. Never expose your `secret` key in the Flutter app. + - Custom providers are referenced with a `custom:` prefix when signing in (for example `custom:mycompany`), and are distinct from the OAuth 2.1 server clients managed through `supabase.auth.admin.oauth`. + - id: admin-custom-providers-list + title: 'admin.customProviders.listProviders()' + notes: | + Lists all custom providers, optionally filtered by provider type. + params: + - name: type + isOptional: true + type: CustomProviderType + description: When set, only providers of this type are returned. Either `CustomProviderType.oauth2` or `CustomProviderType.oidc`. + examples: + - id: list-custom-providers + name: List custom providers + isSpotlight: true + code: | + ```dart + final List providers = + await supabase.auth.admin.customProviders.listProviders(); + ``` + - id: admin-custom-providers-create + title: 'admin.customProviders.createProvider()' + notes: | + Creates a new custom OIDC/OAuth provider. For OIDC providers, the server fetches and validates the discovery document at creation time and throws an `AuthException` with code `validation_failed` if it is unreachable or invalid. + params: + - name: params + isOptional: false + type: CreateCustomProviderParams + description: The provider configuration, including `providerType`, `identifier`, `name`, `clientId`, `clientSecret`, and optional fields such as `customClaimsAllowlist`. + examples: + - id: create-custom-provider + name: Create a custom provider + isSpotlight: true + code: | + ```dart + final CustomOAuthProvider provider = + await supabase.auth.admin.customProviders.createProvider( + CreateCustomProviderParams( + providerType: CustomProviderType.oidc, + identifier: 'custom:mycompany', + name: 'My Company', + clientId: 'client-id', + clientSecret: 'client-secret', + issuer: 'https://auth.mycompany.com', + customClaimsAllowlist: ['groups', 'org_id'], + ), + ); + ``` + - id: admin-custom-providers-get + title: 'admin.customProviders.getProvider()' + notes: | + Gets details of a specific custom provider by its identifier. + params: + - name: identifier + isOptional: false + type: String + description: The provider identifier, for example `custom:mycompany`. + examples: + - id: get-custom-provider + name: Get a custom provider + isSpotlight: true + code: | + ```dart + final CustomOAuthProvider provider = + await supabase.auth.admin.customProviders.getProvider('custom:mycompany'); + ``` + - id: admin-custom-providers-update + title: 'admin.customProviders.updateProvider()' + notes: | + Updates an existing custom provider. When `issuer` or `discoveryUrl` changes on an OIDC provider, the server re-fetches and validates the discovery document before persisting. + params: + - name: identifier + isOptional: false + type: String + description: The provider identifier, for example `custom:mycompany`. + - name: params + isOptional: false + type: UpdateCustomProviderParams + description: The fields to update on the provider. + examples: + - id: update-custom-provider + name: Update a custom provider + isSpotlight: true + code: | + ```dart + final CustomOAuthProvider provider = + await supabase.auth.admin.customProviders.updateProvider( + 'custom:mycompany', + UpdateCustomProviderParams( + customClaimsAllowlist: ['groups', 'org_id', 'mail'], + ), + ); + ``` + - id: admin-custom-providers-delete + title: 'admin.customProviders.deleteProvider()' + notes: | + Deletes a custom provider by its identifier. + params: + - name: identifier + isOptional: false + type: String + description: The provider identifier, for example `custom:mycompany`. + examples: + - id: delete-custom-provider + name: Delete a custom provider + isSpotlight: true + code: | + ```dart + await supabase.auth.admin.customProviders.deleteProvider('custom:mycompany'); + ``` - id: functions-api title: 'Edge Functions' category: Edge Functions @@ -4076,6 +4267,58 @@ functions: }) .subscribe(); ``` + - id: listen-with-pattern-and-negated-filters + name: Listen with pattern and negated filters + description: | + Besides equality, `PostgresChangeFilterType` supports `neq`, `lt`, `lte`, `gt`, `gte`, `inFilter`, `like`, `ilike`, `isFilter`, `match`, `imatch`, and `isDistinct`. Set `negate: true` to prefix the operator with `not.`. + code: | + ```dart + supabase + .channel('public:countries') + .onPostgresChanges( + event: PostgresChangeEvent.all, + schema: 'public', + table: 'countries', + filter: PostgresChangeFilter( + type: PostgresChangeFilterType.ilike, + column: 'name', + value: '%land%', + negate: true, + ), + callback: (payload) { + print('Change received: ${payload.toString()}'); + }) + .subscribe(); + ``` + - id: listen-with-multiple-filters + name: Listen with multiple filters + description: Pass a list of filters to `filters` to combine several conditions with `AND`. Use `select` to limit the change payload to a subset of columns. + code: | + ```dart + supabase + .channel('public:countries') + .onPostgresChanges( + event: PostgresChangeEvent.update, + schema: 'public', + table: 'countries', + filters: [ + PostgresChangeFilter( + type: PostgresChangeFilterType.gte, + column: 'population', + value: 1000000, + ), + PostgresChangeFilter( + type: PostgresChangeFilterType.eq, + column: 'continent', + value: 'Europe', + ), + ], + select: ['id', 'name', 'population'], + callback: (payload) { + print('Change received: ${payload.toString()}'); + }) + .subscribe(); + ``` - id: listen-to-broadcast name: Listen to broadcast messages code: | @@ -4150,6 +4393,24 @@ functions: final channels = supabase.getChannels(); ``` + - id: on-heartbeat + description: | + A `Stream` that emits a status every time the Realtime client sends a heartbeat, receives an acknowledgement, or when a heartbeat goes unanswered. + title: 'onHeartbeat()' + notes: | + - Each event is a `RealtimeHeartbeatStatus`: `sent` when a heartbeat is pushed, `ok` or `error` when the server acknowledges it, and `timeout` when a prior heartbeat is not answered in time. + - Useful for observing connection health, for example to surface a reconnecting indicator in your UI. + examples: + - id: listen-to-heartbeat + name: Listen to heartbeat status + isSpotlight: true + code: | + ```dart + final subscription = supabase.realtime.onHeartbeat.listen((status) { + print('Heartbeat status: $status'); + }); + ``` + - id: stream description: | Returns real-time data from your table as a `Stream`. @@ -5621,6 +5882,10 @@ functions: isOptional: true type: bool description: If `true`, include information on WAL record generation. + - name: format + isOptional: true + type: ExplainFormat + description: The output format of the execution plan. Either `ExplainFormat.text` (default) or `ExplainFormat.json`, in which case the plan is returned as a JSON string. examples: - id: get-execution-plan name: Get the execution plan