Skip to content

Conversation

@cemalkilic
Copy link
Contributor

Summary

This PR adds support for using Supabase Auth as an OAuth 2.1 provider with full PKCE support, enabling Supabase Auth instances to federate authentication to other Supabase Auth instances. This implements the OAuth 2.1 capabilities discussed in https://github.com/orgs/supabase/discussions/38022.

What Changed

1. New Supabase OAuth Provider (internal/api/provider/supabase.go)

  • Implements OAuth 2.1 authorization code flow with PKCE. (PKCE is a must for OAuth2.1 Authorization Code flow)
  • Temporary Implementation: Currently decodes user data from JWT claims instead of calling a /userinfo endpoint
    • TODO: Update to use OpenID Connect Discovery when Supabase Auth supports OIDC
    • TODO: Update to call /userinfo endpoint when available
  • Scopes: Currently placeholder implementation (not yet supported by Supabase Auth OAuth 2.1)
  • Extracts and preserves app_metadata and user_metadata as custom claims

2. Generic PKCE Support for OAuth Providers

Added code to support dual PKCE flows:

  • User PKCE: Code challenge/verifier between end user and this auth instance
  • Provider PKCE: Separate code challenge/verifier between this instance and external provider

Currently, only supabase auth requires PKCE but added the code to make it future proof as OAuth2.1 adoption is increasing. Added RequiresPKCE() method (returns false by default) to all 25 existing providers

Flow Matrix:

User Flow Provider Requires PKCE FlowState Created? Result
PKCE Yes Auth code (both PKCEs)
PKCE No Auth code (user PKCE only)
Implicit Yes Token (provider PKCE internal)
Implicit No Token (traditional implicit)

Configuration

Users can configure the Supabase provider via environment variables:

GOTRUE_EXTERNAL_SUPABASE_ENABLED=true
GOTRUE_EXTERNAL_SUPABASE_CLIENT_ID=your-oauth-client-id
GOTRUE_EXTERNAL_SUPABASE_SECRET=your-oauth-client-secret
GOTRUE_EXTERNAL_SUPABASE_URL=https://other-supabase-project.supabase.co/auth/v1
GOTRUE_EXTERNAL_SUPABASE_REDIRECT_URI=https://your-auth-instance.supabase.co/auth/v1/callback

Technical Highlights

  1. No Supabase-specific conditionals in flow logic. All PKCE handling works for any provider via the RequiresPKCE() interface method.
  2. Uses golang.org/x/oauth2 Built-in PKCE:
    • oauth2.GenerateVerifier() - generates code verifier
    • oauth2.S256ChallengeOption() - adds challenge to auth URL
    • oauth2.VerifierOption() - adds verifier to token exchange
  3. Separation of Concerns:
    • User's PKCE stored in FlowState.CodeChallenge and verified against user's verifier
    • Provider's PKCE stored in FlowState.ProviderCodeVerifier and sent to provider during token exchange
    • Both flows are completely independent

Manual Testing Scenarios:

  1. ✅ User PKCE + Supabase provider (dual PKCE)
  2. ✅ User implicit + Supabase provider (provider PKCE only, return token)
  3. ✅ User PKCE + non-PKCE provider (user PKCE only)
  4. ✅ User implicit + non-PKCE provider (traditional implicit)

@cemalkilic cemalkilic requested a review from a team as a code owner October 29, 2025 09:03
@cemalkilic cemalkilic changed the title feat(oauth): add __Supabase__ OAuth provider feat(oauth): add Supabase OAuth provider Oct 29, 2025
@coveralls
Copy link

coveralls commented Oct 29, 2025

Pull Request Test Coverage Report for Build 19170193324

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 138 of 246 (56.1%) changed or added relevant lines in 36 files are covered.
  • 509 unchanged lines in 16 files lost coverage.
  • Overall coverage increased (+0.6%) to 68.151%

Changes Missing Coverage Covered Lines Changed/Added Lines %
internal/api/provider/twitter.go 3 4 75.0%
internal/api/provider/slack_oidc.go 3 5 60.0%
internal/api/external_oauth.go 8 11 72.73%
internal/api/provider/apple.go 3 7 42.86%
internal/api/provider/oidc.go 0 4 0.0%
internal/api/provider/linkedin_oidc.go 0 5 0.0%
internal/api/provider/slack.go 0 5 0.0%
internal/api/provider/spotify.go 0 5 0.0%
internal/api/provider/vercel_marketplace.go 0 5 0.0%
internal/api/external.go 24 34 70.59%
Files with Coverage Reduction New Missed Lines %
internal/api/provider/apple.go 1 28.97%
internal/api/provider/linkedin_oidc.go 1 0.0%
internal/api/provider/slack.go 1 0.0%
internal/api/provider/slack_oidc.go 1 44.78%
internal/api/provider/spotify.go 1 0.0%
internal/api/provider/vercel_marketplace.go 1 0.0%
internal/api/token_refresh.go 2 93.33%
internal/utilities/request.go 2 94.38%
internal/api/api.go 7 86.6%
internal/models/refresh_token.go 19 70.43%
Totals Coverage Status
Change from base Build 18655410402: 0.6%
Covered Lines: 14093
Relevant Lines: 20679

💛 - Coveralls

Linkedin OAuthProviderConfiguration `json:"linkedin"`
LinkedinOIDC OAuthProviderConfiguration `json:"linkedin_oidc" envconfig:"LINKEDIN_OIDC"`
Spotify OAuthProviderConfiguration `json:"spotify"`
Supabase OAuthProviderConfiguration `json:"supabase"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if Supabase makes sense here. It can imply "supabase.com" which we do not support.

Maybe change this to "SupabaseAuth" or "AuthProject" something?

Copy link
Contributor

@hf hf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM except for the config name.

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.

4 participants