Summary
When using the id_token grant for Sign in with Apple (native iOS) with signInWithIdToken({ provider: 'apple', token, nonce }), GoTrue returns "Nonces mismatch" even when the client sends the correct raw nonce. The cause is an encoding mismatch in nonce verification: GoTrue compares a hex-encoded hash to Apple's base64url-encoded hash.
Environment
- Flow: Native Sign in with Apple → Apple returns ID token (JWT) → client sends
id_token + raw nonce to POST /token?grant_type=id_token.
- Provider: Apple.
- GoTrue: Hosted Supabase Auth (and/or self-hosted from
supabase/auth).
- Error:
invalid nonce / Nonces mismatch from the id_token grant.
Root cause
In internal/api/token_oidc.go, nonce verification does:
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(params.Nonce)))
if hash != idToken.Nonce {
return apierrors.NewOAuthError("invalid nonce", "Nonces mismatch")
}
- GoTrue:
hash is the SHA-256 of the request nonce, encoded as lowercase hex (64 characters).
- Apple ID token: The
nonce claim is the SHA-256 of the nonce, encoded as base64url without padding (43 characters), per Apple OIDC and common OIDC practice.
So the comparison is hex string vs base64url string and can never succeed.
Expected behavior
For the Apple provider, nonce verification should compare like-for-like:
- Compute
nonce_hash = SHA-256(params.Nonce).
- Encode
nonce_hash as base64url (no padding) to match Apple’s nonce claim.
- Compare
base64url(nonce_hash) to idToken.Nonce.
Proposed fix (conceptual)
Use base64url for the computed hash when the provider is Apple, and keep existing behavior for other providers:
// In token_oidc.go, where nonce is verified:
sum := sha256.Sum256([]byte(params.Nonce))
var computedNonce string
if providerType == "apple" {
// Apple's ID token nonce claim is base64url (no padding) per OIDC
computedNonce = base64.RawURLEncoding.EncodeToString(sum[:])
} else {
computedNonce = fmt.Sprintf("%x", sum)
}
if computedNonce != idToken.Nonce {
return apierrors.NewOAuthError("invalid nonce", "Nonces mismatch")
}
(Exact branch and provider detection to match your codebase; other providers may need to stay hex or be normalized per their spec.)
Minimal repro
- Native iOS app: Sign in with Apple → obtain
id_token (JWT) and the raw nonce that was hashed and sent to Apple.
POST /auth/v1/token with grant_type=id_token, provider=apple, id_token=<jwt>, nonce=<raw_nonce>.
- Result: 400 with
"Nonces mismatch" even though the raw nonce is correct and the token’s nonce claim is base64url(SHA256(raw_nonce)).
References
- Apple: Sign in with Apple REST API — identity token includes
nonce as hashed value.
- OIDC / JWT: nonce in ID token is typically base64url-encoded hash when sent as hash to the provider.
- Existing issue about nonce/encoding: e.g. #1829 (Google); same class of issue for Apple.
Suggested next steps: Implement provider-specific nonce encoding (base64url for Apple) in the id_token grant so native Sign in with Apple works without disabling nonce verification.
Summary
When using the id_token grant for Sign in with Apple (native iOS) with
signInWithIdToken({ provider: 'apple', token, nonce }), GoTrue returns "Nonces mismatch" even when the client sends the correct raw nonce. The cause is an encoding mismatch in nonce verification: GoTrue compares a hex-encoded hash to Apple's base64url-encoded hash.Environment
id_token+ raw nonce toPOST /token?grant_type=id_token.supabase/auth).invalid nonce / Nonces mismatchfrom the id_token grant.Root cause
In
internal/api/token_oidc.go, nonce verification does:hashis the SHA-256 of the request nonce, encoded as lowercase hex (64 characters).nonceclaim is the SHA-256 of the nonce, encoded as base64url without padding (43 characters), per Apple OIDC and common OIDC practice.So the comparison is hex string vs base64url string and can never succeed.
Expected behavior
For the Apple provider, nonce verification should compare like-for-like:
nonce_hash = SHA-256(params.Nonce).nonce_hashas base64url (no padding) to match Apple’snonceclaim.base64url(nonce_hash)toidToken.Nonce.Proposed fix (conceptual)
Use base64url for the computed hash when the provider is Apple, and keep existing behavior for other providers:
(Exact branch and provider detection to match your codebase; other providers may need to stay hex or be normalized per their spec.)
Minimal repro
id_token(JWT) and the raw nonce that was hashed and sent to Apple.POST /auth/v1/tokenwithgrant_type=id_token,provider=apple,id_token=<jwt>,nonce=<raw_nonce>."Nonces mismatch"even though the raw nonce is correct and the token’snonceclaim isbase64url(SHA256(raw_nonce)).References
nonceas hashed value.Suggested next steps: Implement provider-specific nonce encoding (base64url for Apple) in the id_token grant so native Sign in with Apple works without disabling nonce verification.