Custom OIDC Providers for Self-Hosted Supabase: Complete Integration Guide

Configure custom OpenID Connect identity providers like Keycloak, Auth0, and enterprise IDPs for self-hosted Supabase authentication.

Cover Image for Custom OIDC Providers for Self-Hosted Supabase: Complete Integration Guide

Beyond the standard OAuth providers like Google and GitHub, many organizations need to connect self-hosted Supabase to their own identity infrastructure. Whether you're running Keycloak, Auth0, Authentik, or an industry-specific identity provider, OpenID Connect (OIDC) makes this possible.

Supabase recently released custom OIDC provider support, allowing any standards-compliant identity provider to integrate with your authentication flow. For self-hosted deployments, this requires configuration through Docker environment variables—there's no dashboard toggle to flip.

This guide walks you through integrating custom OIDC providers with self-hosted Supabase, from basic setup to production hardening.

Why Custom OIDC Providers Matter for Self-Hosting

Organizations choose custom identity providers for several reasons:

  • Self-hosted identity management: Running Keycloak, Authentik, or Dex alongside Supabase keeps your entire auth stack on your infrastructure
  • Existing enterprise identity: Connecting to an organization's established SSO system without paying for Supabase's enterprise tier
  • Industry-specific providers: Healthcare, finance, and government often require specialized identity networks
  • Multi-tenant architectures: Each tenant uses their own IdP for authentication

OIDC (OpenID Connect) sits on top of OAuth 2.0 and adds a standardized identity layer. The key advantage over raw OAuth 2.0 is automatic endpoint discovery—you provide an issuer URL, and Supabase resolves everything else from the well-known configuration document.

Prerequisites

Before configuring custom OIDC providers, ensure you have:

  1. A running self-hosted Supabase instance with HTTPS configured
  2. Access to your identity provider's admin console to create OAuth applications
  3. The ability to modify Docker Compose environment variables
  4. Your Supabase instance's external URL (e.g., https://supabase.yourdomain.com)

If you haven't set up HTTPS yet, see our reverse proxy setup guide. OIDC providers require HTTPS—HTTP callback URLs are rejected by most providers.

Understanding OIDC vs OAuth2 Configuration

Supabase supports two configuration modes for custom providers:

OIDC Mode (recommended when available):

  • Supply only the issuer URL
  • Endpoints are resolved automatically via /.well-known/openid-configuration
  • ID tokens are verified against the provider's JWKS
  • The openid scope is automatically included

OAuth2 Mode (fallback for non-OIDC providers):

  • You must supply authorization, token, and userinfo endpoints manually
  • No automatic endpoint discovery
  • Suitable for legacy OAuth2 providers without OpenID Connect support

Most modern identity providers support OIDC. Use OAuth2 mode only when your provider doesn't publish a discovery document.

Step 1: Configure Your Identity Provider

The first step happens in your identity provider, not Supabase. You'll create an application/client and collect the necessary credentials.

Creating an Application in Keycloak

If you're running Keycloak:

  1. Navigate to your realm and select ClientsCreate client
  2. Set Client type to OpenID Connect
  3. Enter a Client ID (e.g., supabase)
  4. Enable Client authentication (this generates a client secret)
  5. Add your callback URL as a Valid redirect URI:
https://supabase.yourdomain.com/auth/v1/callback
  1. Save and copy the Client secret from the Credentials tab

The issuer URL for Keycloak follows this pattern:

https://keycloak.yourdomain.com/realms/your-realm

Creating an Application in Auth0

For Auth0:

  1. Go to ApplicationsCreate Application
  2. Choose Regular Web Application
  3. Under Settings, add the callback URL:
https://supabase.yourdomain.com/auth/v1/callback
  1. Copy the Domain, Client ID, and Client Secret

The issuer URL for Auth0:

https://your-tenant.auth0.com

Collecting Required Information

Regardless of your provider, you'll need:

ValueDescriptionExample
Issuer URLBase URL for OIDC discoveryhttps://keycloak.yourdomain.com/realms/main
Client IDPublic identifier for your appsupabase
Client SecretPrivate credentialyour-secret-key
Callback URLWhere users return after authhttps://supabase.yourdomain.com/auth/v1/callback

Verify your provider's discovery document is accessible:

curl https://your-issuer/.well-known/openid-configuration | jq

You should see JSON containing authorization_endpoint, token_endpoint, userinfo_endpoint, and jwks_uri.

Step 2: Configure GoTrue Environment Variables

GoTrue, Supabase's auth service, handles custom OIDC providers through environment variables. Add these to your Docker Compose configuration.

For a provider named "keycloak":

# docker-compose.yml - auth service section
auth:
  environment:
    # Enable the custom provider
    GOTRUE_EXTERNAL_KEYCLOAK_ENABLED: "true"
    
    # Your client credentials
    GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID: "supabase"
    GOTRUE_EXTERNAL_KEYCLOAK_SECRET: "${KEYCLOAK_CLIENT_SECRET}"
    
    # OIDC issuer URL - endpoints are discovered automatically
    GOTRUE_EXTERNAL_KEYCLOAK_URL: "https://keycloak.yourdomain.com/realms/main"
    
    # Redirect URI must match what you configured in Keycloak
    GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: "https://supabase.yourdomain.com/auth/v1/callback"
    
    # Scopes to request (openid is added automatically for OIDC)
    GOTRUE_EXTERNAL_KEYCLOAK_SCOPES: "email profile"

Store the client secret in your .env file rather than hardcoding it:

# .env
KEYCLOAK_CLIENT_SECRET=your-secret-key-here

Naming Convention

The provider name (e.g., KEYCLOAK) becomes part of your sign-in URL. Choose something URL-friendly and consistent:

  • Sign-in URL: https://supabase.yourdomain.com/auth/v1/authorize?provider=keycloak
  • The name appears in user.app_metadata.provider

Step 3: Update Kong Route Configuration

By default, Kong may not route the new provider's callback correctly. Verify your Kong configuration includes the auth callback route:

# kong.yml
services:
  - name: auth-v1
    url: http://auth:9999/
    routes:
      - name: auth-v1
        strip_path: true
        paths:
          - /auth/v1/

The callback path /auth/v1/callback should be handled by the auth service. If you're seeing 404 errors on callback, ensure the route isn't being stripped incorrectly.

Step 4: Restart and Test

After configuration changes, restart the affected services:

docker compose down auth kong
docker compose up -d auth kong

Test the flow by navigating directly to the authorization URL:

https://supabase.yourdomain.com/auth/v1/authorize?provider=keycloak

You should be redirected to your identity provider's login page. After authenticating, you'll return to your callback URL with a session.

Testing with the Supabase Client

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://supabase.yourdomain.com',
  'your-anon-key'
)

// Sign in with your custom provider
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'keycloak',
  options: {
    redirectTo: 'https://your-app.com/auth/callback'
  }
})

Configuring Multiple Custom Providers

You can add multiple custom OIDC providers by repeating the environment variable pattern with different names:

auth:
  environment:
    # First provider - Keycloak for internal users
    GOTRUE_EXTERNAL_KEYCLOAK_ENABLED: "true"
    GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID: "supabase"
    GOTRUE_EXTERNAL_KEYCLOAK_SECRET: "${KEYCLOAK_SECRET}"
    GOTRUE_EXTERNAL_KEYCLOAK_URL: "https://keycloak.internal.com/realms/main"
    
    # Second provider - Auth0 for external customers
    GOTRUE_EXTERNAL_AUTH0_ENABLED: "true"
    GOTRUE_EXTERNAL_AUTH0_CLIENT_ID: "xyz123"
    GOTRUE_EXTERNAL_AUTH0_SECRET: "${AUTH0_SECRET}"
    GOTRUE_EXTERNAL_AUTH0_URL: "https://your-tenant.auth0.com"

Each provider gets its own sign-in endpoint:

  • ?provider=keycloak
  • ?provider=auth0

Handling User Data and Claims

OIDC providers return user information through claims. Supabase maps standard claims to user metadata:

OIDC ClaimSupabase Field
subuser.id (linked via identity)
emailuser.email
email_verifieduser.email_confirmed_at
nameuser.user_metadata.full_name
pictureuser.user_metadata.avatar_url

Custom claims from your provider appear in user.user_metadata or can be accessed via auth hooks for advanced processing.

Example: Mapping Keycloak Roles to Custom Claims

If your Keycloak returns roles in the ID token, you can use an auth hook to map them:

CREATE OR REPLACE FUNCTION custom_access_token_hook(event jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
  claims jsonb;
  user_roles text[];
BEGIN
  claims := event->'claims';
  
  -- Extract roles from identity provider data
  SELECT ARRAY(
    SELECT jsonb_array_elements_text(
      event->'user'->'user_metadata'->'realm_access'->'roles'
    )
  ) INTO user_roles;
  
  -- Add roles to claims
  claims := jsonb_set(claims, '{user_roles}', to_jsonb(user_roles));
  
  RETURN jsonb_set(event, '{claims}', claims);
END;
$$;

OAuth2 Mode for Non-OIDC Providers

Some legacy providers don't support OIDC discovery. For these, configure endpoints manually:

auth:
  environment:
    GOTRUE_EXTERNAL_LEGACY_ENABLED: "true"
    GOTRUE_EXTERNAL_LEGACY_CLIENT_ID: "your-client-id"
    GOTRUE_EXTERNAL_LEGACY_SECRET: "${LEGACY_SECRET}"
    
    # Manual endpoint configuration (no discovery)
    GOTRUE_EXTERNAL_LEGACY_AUTHORIZE_URL: "https://legacy.provider.com/oauth/authorize"
    GOTRUE_EXTERNAL_LEGACY_TOKEN_URL: "https://legacy.provider.com/oauth/token"
    GOTRUE_EXTERNAL_LEGACY_USERINFO_URL: "https://legacy.provider.com/oauth/userinfo"

Without OIDC, there's no automatic ID token verification. Ensure you trust the userinfo endpoint response.

Troubleshooting Common Issues

Callback URL Mismatch

Symptom: "Invalid redirect URI" error from identity provider

Fix: The redirect URI configured in your IdP must exactly match GOTRUE_EXTERNAL_{PROVIDER}_REDIRECT_URI. Pay attention to:

  • HTTP vs HTTPS
  • Trailing slashes
  • Case sensitivity (some providers are case-sensitive)

OIDC Discovery Fails

Symptom: Auth service fails to start with OIDC errors

Debug:

# Test if discovery document is accessible from within Docker
docker compose exec auth curl -v https://your-issuer/.well-known/openid-configuration

Common causes:

  • Network connectivity between containers
  • Self-signed certificates not trusted (add CA to auth container)
  • Incorrect issuer URL

User Not Created After Authentication

Symptom: Auth succeeds but no user appears in auth.users

Check: Ensure email scope is included and the IdP returns an email claim. Supabase requires an email to create users by default.

Sessions Not Persisting

Symptom: User appears logged in momentarily then loses session

Likely cause: Cookie domain mismatch. Ensure GOTRUE_SITE_URL matches your application's domain and cookies can be set correctly.

Security Considerations

Validate the Issuer

Only connect to identity providers you control or trust. The auth service will accept any claims from a configured issuer. A compromised IdP means compromised users.

Use Unique Client Secrets

Generate strong, unique client secrets for each environment:

openssl rand -base64 32

Never reuse client secrets across development, staging, and production.

Consider Token Lifetimes

OIDC ID tokens and access tokens have expiration times. Configure session lifetimes in GoTrue to align with your security requirements:

auth:
  environment:
    GOTRUE_JWT_EXP: 3600  # 1 hour
    GOTRUE_SECURITY_REFRESH_TOKEN_ROTATION_ENABLED: "true"

Network Isolation

If your identity provider is internal-only, ensure proper network segmentation. The auth container needs outbound access to the IdP, but the IdP shouldn't be exposed publicly.

How Supascale Simplifies Provider Management

Setting up custom OIDC providers requires modifying Docker Compose files, managing secrets, and restarting services. With Supascale, you can:

  • Configure OAuth and OIDC providers through a visual interface
  • Manage secrets without editing environment files
  • Apply configuration changes without manual Docker restarts
  • Test provider connections before going live

While the underlying GoTrue configuration remains the same, Supascale removes the friction of editing YAML files and coordinating service restarts. The one-time purchase pricing makes it accessible for teams running multiple self-hosted Supabase projects with complex authentication requirements.

Conclusion

Custom OIDC providers unlock enterprise authentication scenarios that standard OAuth providers can't address. Whether you're integrating with Keycloak for internal SSO, connecting to Auth0 for customer-facing auth, or plugging into industry-specific identity networks, self-hosted Supabase provides the flexibility you need.

The key steps:

  1. Create an OAuth application in your identity provider
  2. Configure GoTrue environment variables with client credentials and issuer URL
  3. Test the authentication flow
  4. Handle claims and roles appropriately in your application

For most OIDC-compliant providers, configuration takes under an hour. The investment pays off in unified identity management across your self-hosted infrastructure.

Further Reading