Sign in with Apple: Better Auth token exchange returns invalid_client while equivalent direct request returns invalid_grant

Hi, Feedback ID - FB24176019

I am investigating a Sign in with Apple issue affecting my production application and would appreciate guidance on what Apple may be rejecting during the token exchange.

The authorization stage succeeds and Apple returns an authorization code to our callback. The failure occurs when the authorization code is exchanged at:

https://appleid.apple.com/auth/token

The production OAuth implementation uses Better Auth 1.6.13.

Apple configuration:

Primary App ID / Bundle ID: com.whatsupplier.uk

Services ID / client_id: com.whatsupplier.uk.signin

Registered return URL: https://www.whatsupp.uk/api/auth/callback/apple

We have checked the client secret JWT and confirmed:

  • alg = ES256
  • kid matches the active Apple key
  • iss matches our Apple Team ID
  • sub = com.whatsupplier.uk.signin
  • aud = https://appleid.apple.com
  • iat/exp are valid
  • the JWT signature verifies locally against the configured Apple private key
  • the signature is P1363/raw r||s rather than ASN.1 DER
  • the private key is EC / prime256v1
  • the redirect_uri exactly matches the registered return URL
  • the token request uses application/x-www-form-urlencoded

The unusual behaviour is reproducible:

  1. The real Better Auth token exchange reaches Apple's /auth/token endpoint but Apple responds:

HTTP 400 error: invalid_client

  1. We created a diagnostic request directly to the same Apple token endpoint using the same configured credentials and a deliberately invalid authorization code.

Apple responds:

HTTP 400 error: invalid_grant "The code has expired or has been revoked."

This indicates Apple accepts the client credentials/JWT in the direct request and proceeds as far as validating the authorization code.

We also performed an A/B test using the exact same generated client-secret JWT:

Test A: grant_type code redirect_uri client_id client_secret

Result: invalid_grant

Test B: grant_type code redirect_uri client_id client_secret code_verifier

Result: invalid_grant

Therefore code_verifier alone does not change Apple's classification.

We then captured the observable shape of the real Better Auth token request and compared it with a direct request. The client_id, redirect_uri, grant_type, client-secret JWT claims, URLSearchParams/form encoding and absence of an Authorization header were consistent.

Despite this, the real framework-generated exchange returns invalid_client while the direct diagnostic request is accepted at the client-authentication stage and returns invalid_grant.

Could Apple advise what additional property of the token request could cause this difference in classification?

In particular, is there any server-side state or request characteristic used by Sign in with Apple that could cause an otherwise valid client_secret to be classified as invalid_client only during the real authorization-code exchange?

I have also prepared detailed diagnostic evidence and can submit the requested sensitive request information through Feedback Assistant rather than posting JWTs, authorization codes, or other credentials publicly.

Thank you.

Sign in with Apple: Better Auth token exchange returns invalid_client while equivalent direct request returns invalid_grant
 
 
Q