Skip to content

Commit 4de7bd6

Browse files
authored
Add Facebook Limited Login Support for iOS (#2046)
## What kind of change does this PR introduce? This PR adds support for [Facebook Limited Login](https://developers.facebook.com/docs/facebook-login/limited-login/) JWT (iOS only) to the `/token?grant_type=id_token` endpoint. This enables iOS apps using Facebook's Limited Login feature to authenticate with Supabase without requiring web browser redirects. ## What is the current behavior? Currently, the `/token?grant_type=id_token` endpoint does not support Facebook as a provider. When iOS apps using Facebook Limited Login try to authenticate with their JWT, they receive a `Bad ID token` error because Facebook's JWT structure is not recognized by the generic OIDC parser. This is already raised by users in #1522 as well. ## What is the new behavior? - iOS apps can now authenticate using Facebook Limited Login JWT via `signInWithIdToken()` function on the client side - Facebook JWT are properly parsed and validated - End users can authenticate on iOS even if they dont allow tracking ([ATT](https://developer.apple.com/documentation/apptrackingtransparency)) ## Additional context Important: Android Platform Limitations This implementation only supports iOS Facebook Limited Login. Android developers must continue using the standard OAuth flow (`signInWithOAuth()`) with web browser redirects. Why Android is not supported in this PR: 1. Fundamental Token Differences: - iOS: Facebook Limited Login provides self-contained JWT ID tokens that follow OIDC standards - Android: Facebook SDK only provides opaque access tokens (random strings, not JWTs) 2. Validation Requirements: - iOS JW: Can be validated using standard OIDC/JWKS (already handled by our infrastructure) - Android access tokens: Require calling Facebook Graph API for validation 3. Architectural Considerations: - The /token?grant_type=id_token endpoint is designed specifically for OIDC-compliant JWT - Adding Graph API validation for Android access tokens would be out of scope and violate the endpoint's single responsibility - It would essentially make this an "id_token OR access_token" endpoint, which breaks the grant type semantics WIP: Tests in `oidc_test.go` will be added.
1 parent 96469bd commit 4de7bd6

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

example.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ GOTRUE_EXTERNAL_FACEBOOK_ENABLED="false"
8383
GOTRUE_EXTERNAL_FACEBOOK_CLIENT_ID=""
8484
GOTRUE_EXTERNAL_FACEBOOK_SECRET=""
8585
GOTRUE_EXTERNAL_FACEBOOK_REDIRECT_URI="https://localhost:9999/callback"
86+
GOTRUE_EXTERNAL_FACEBOOK_SKIP_NONCE_CHECK=true
8687

8788
# Figma OAuth config
8889
GOTRUE_EXTERNAL_FIGMA_ENABLED="false"

internal/api/provider/oidc.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ func ParseIDToken(ctx context.Context, provider *oidc.Provider, config *oidc.Con
6161
token, data, err = parseKakaoIDToken(token)
6262
case IssuerVercelMarketplace:
6363
token, data, err = parseVercelMarketplaceIDToken(token)
64+
case IssuerFacebook:
65+
// Handle only Facebook Limited Login JWT, NOT Facebook Access Token
66+
token, data, err = parseFacebookIDToken(token)
6467
default:
6568
if IsAzureIssuer(token.Issuer) {
6669
token, data, err = parseAzureIDToken(token)
@@ -121,6 +124,44 @@ func parseGoogleIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData,
121124
return token, &data, nil
122125
}
123126

127+
// FacebookIDTokenClaims represents the claims in a Facebook Limited Login ID token
128+
type FacebookIDTokenClaims struct {
129+
jwt.RegisteredClaims
130+
Email string `json:"email"`
131+
Name string `json:"name"`
132+
Picture string `json:"picture"`
133+
Nonce string `json:"nonce,omitempty"`
134+
}
135+
136+
func parseFacebookIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, error) {
137+
var claims FacebookIDTokenClaims
138+
if err := token.Claims(&claims); err != nil {
139+
return nil, nil, err
140+
}
141+
142+
var data UserProvidedData
143+
144+
if claims.Email != "" {
145+
data.Emails = append(data.Emails, Email{
146+
Email: claims.Email,
147+
Verified: true, // Facebook Limited Login emails are always verified
148+
Primary: true,
149+
})
150+
}
151+
152+
data.Metadata = &Claims{
153+
Issuer: token.Issuer,
154+
Subject: token.Subject,
155+
Name: claims.Name,
156+
Picture: claims.Picture,
157+
ProviderId: token.Subject,
158+
Email: claims.Email,
159+
EmailVerified: claims.Email != "",
160+
}
161+
162+
return token, &data, nil
163+
}
164+
124165
type AppleIDTokenClaims struct {
125166
jwt.RegisteredClaims
126167

0 commit comments

Comments
 (0)