Custom OAuth/OIDC providers let you integrate any standards-compliant identity provider with Zuvo Auth, beyond the ones Zuvo supports out of the box.
Each custom provider uses a custom: prefix in its identifier (for example, custom:my-idp or custom:github-enterprise). This prefix distinguishes custom providers from built-in providers.
There are two provider types:
- OAuth2: for generic OAuth2 providers where you supply the authorization, token, and userinfo endpoints manually.
- OIDC: for providers that support OpenID Connect discovery. You supply only the issuer URL and endpoints are resolved automatically.
Creating a provider
The create form displays a read-only Callback URL. Copy this URL and configure it as the redirect/callback URI in your external identity provider before completing setup.
OAuth2 provider
Use an OAuth2 provider when your identity provider does not support OpenID Connect discovery. You must supply the authorization, token, and userinfo endpoint URLs explicitly.
- Go to Auth Providers in the Dashboard.
- Click New Provider. Select Manual configuration as the configuration method.
- Enter a unique identifier (must start with
custom:, for examplecustom:my-oauth-provider). - Enter the provider's Client ID and Client Secret.
- Enter the Authorization URL, Token URL, and UserInfo URL.
- Click Create and enable provider.
const { data, error } = await supabase.auth.admin.customProviders.createProvider({
provider_type: 'oauth2',
identifier: 'custom:my-oauth-provider',
name: 'My OAuth Provider',
client_id: 'your-client-id',
client_secret: 'your-client-secret',
authorization_url: 'https://provider.example.com/oauth/authorize',
token_url: 'https://provider.example.com/oauth/token',
userinfo_url: 'https://provider.example.com/oauth/userinfo',
scopes: ['profile', 'email'],
})
OIDC provider
Use an OIDC provider when your identity provider supports OpenID Connect. Supply the issuer URL and the discovery document, JWKS, and endpoints are resolved automatically.
- Go to Auth Providers in the Dashboard.
- Click New Provider. Select Auto-discovery (OIDC) as the configuration method.
- Enter a unique identifier (must start with
custom:, for examplecustom:my-regional-provider). - Enter the provider's Client ID and Client Secret.
- Enter the Issuer URL. The discovery document and endpoints are resolved automatically.
- Click Create and enable provider.
const { data, error } = await supabase.auth.admin.customProviders.createProvider({
provider_type: 'oidc',
identifier: 'custom:my-regional-provider',
name: 'Regional Provider',
client_id: 'your-client-id',
client_secret: 'your-client-secret',
issuer: 'https://auth.example.com',
scopes: ['openid', 'profile', 'email'],
})
OIDC providers have the following automatic behavior:
- The discovery document is fetched from
{issuer}/.well-known/openid-configuration(or fromdiscovery_urlif set). - The
openidscope is always included. It is automatically added if missing from thescopesarray. - ID tokens are verified against the provider's JWKS (fetched from the discovery document's
jwks_uri).
Provider identifiers
Every custom provider identifier must start with the custom: prefix. Identifiers are 2–50 characters, lowercase alphanumeric with hyphens and colons allowed. Examples:
custom:my-providercustom:github-enterprise
User sign-in
Once a custom provider is created and enabled, users sign in via the standard OAuth authorize endpoint:
GET https://your-project.supabase.co/auth/v1/authorize?provider=custom:my-provider
Or using the Zuvo client libraries:
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'custom:my-provider',
})
await supabase.auth.signInWithOAuth(
OAuthProvider('custom:my-provider'),
);
try await supabase.auth.signInWithOAuth(
provider: "custom:my-provider",
redirectTo: URL(string: "my-custom-scheme://my-app-host")
)
supabase.auth.signInWith(CustomProvider("custom:my-provider"))
Managing providers
List providers
Go to Auth Providers in the Dashboard. All custom providers are listed under Custom OAuth Providers.
// List all custom providers
const { data, error } = await supabase.auth.admin.customProviders.listProviders()
// Filter by provider type
const { data, error } = await supabase.auth.admin.customProviders.listProviders({
type: 'oidc',
})
Update a provider
Update any provider fields except provider_type and identifier. Only provided fields are changed (partial update). To rotate a client secret, update only the client_secret field.
- Go to Auth Providers in the Dashboard.
- Click the three-dot menu (⋮) next to the provider and select Update.
- Modify the fields you want to change.
- Click Update provider.
const { data, error } = await supabase.auth.admin.customProviders.updateProvider(
'custom:my-provider',
{
name: 'Updated Provider Name',
scopes: ['profile', 'email', 'groups'],
enabled: false,
}
)
Delete a provider
- Go to Auth Providers in the Dashboard.
- Click the three-dot menu (⋮) next to the provider and select Delete.
- Confirm the deletion.
const { data, error } =
await supabase.auth.admin.customProviders.deleteProvider('custom:my-provider')
Advanced configuration
PKCE
PKCE (Proof Key for Code Exchange) is enabled by default (pkce_enabled: true) for all custom providers. The auth server automatically generates a code challenge and verifier during the authorization flow, protecting against authorization code interception attacks. This is handled entirely server-side, no client-side PKCE logic is needed.
To disable PKCE for a specific provider, set pkce_enabled: false when creating or updating it. This is not recommended unless the identity provider does not support PKCE.
Authorization params
Extra query parameters appended to the provider's authorization URL during the OAuth flow. All values must be strings.
{
"prompt": "consent",
"access_type": "offline",
"login_hint": "user@example.com"
}
The following reserved parameters are managed by the auth server and cannot be overridden: client_id, client_secret, redirect_uri, response_type, state, code_challenge, code_challenge_method, code_verifier, nonce.
Multi-platform apps
If your app uses different client IDs for different platforms (for example, web vs mobile), use acceptable_client_ids to list additional client IDs that should be accepted for audience validation in OIDC ID tokens:
const { data, error } = await supabase.auth.admin.customProviders.createProvider({
provider_type: 'oidc',
identifier: 'custom:multi-platform-app',
name: 'Multi-Platform App',
client_id: 'web-client-id',
client_secret: 'your-client-secret',
issuer: 'https://app.example.com',
scopes: ['openid', 'profile', 'email'],
acceptable_client_ids: ['ios-client-id', 'android-client-id'],
})
Email-optional providers
By default, providers must return an email address. Set email_optional to true when creating or updating a provider to allow sign-in without an email. This applies to both OAuth2 and OIDC providers.
OIDC-specific options
| Field | Type | Default | Description |
|---|---|---|---|
discovery_url | string | null | Override the discovery document URL if the provider uses a non-standard location. |
skip_nonce_check | bool | false | Skip nonce validation on ID tokens. Use only for providers that do not support nonce. |
Error reference
| Error code | HTTP status | Description |
|---|---|---|
validation_failed | 400 | Invalid parameters: missing required fields, bad format, reserved params, or invalid URLs. |
conflict | 400 | A provider with the same identifier already exists. |
over_custom_provider_quota | 400 | Maximum number of custom providers reached. |
custom_provider_not_found | 404 | No provider exists with the given identifier. |