DEV Community

Tori TIC
Tori TIC

Posted on

Supabase Sign in with Apple keeps throwing invalid_client: the checklist that actually finds it

You rotate a key for Sign in with Apple, and suddenly every login throws invalid_client. It was supposed to be a five minute change. Three hours later you are still staring at the same error.

This one bites hard because invalid_client is Apple's answer to about six different mistakes, and the error text never tells you which one you made. Here is the checklist that finds it, in the order of most likely to least.

What is actually being checked

When Supabase (or any backend) talks to Apple, it presents a client secret that is not a static string: it is a JWT you generate, signed with your .p8 key. Apple validates five things about it, and a mismatch in any one of them returns invalid_client:

  1. the JWT is signed by the key whose Key ID is in the JWT header
  2. that key belongs to your Apple team
  3. the sub claim matches your Services ID (not your App ID)
  4. the iss claim is your Team ID
  5. the JWT is not expired

The checklist

1. Did you regenerate the client-secret JWT after rotating the .p8?

This is the classic trap. Rotating the key in the Apple Developer portal does nothing to the JWT you generated from the old key. That JWT still carries the old Key ID in its header, so Apple rejects it. After any key rotation you must generate a fresh client-secret JWT from the new .p8 and paste that into your provider config. The old secret does not "refresh".

2. Is the sub claim your Services ID?

The sub in the client-secret JWT must be the Services ID (the identifier you created for web auth, usually something like com.yourapp.web), not the App ID and not the bundle id. If you copied the wrong identifier when generating the secret, Apple sees a client it does not know.

3. Does the Key ID in the JWT header match the uploaded key?

Decode your client secret at any JWT debugger (it is not sensitive to decode, it is public claims plus a signature). The header's kid must be exactly the Key ID shown next to your Sign in with Apple key in the developer portal. A stale kid means you signed with a key Apple no longer associates with that configuration.

4. Is the JWT expired?

Apple caps client-secret JWTs at 6 months (exp at most 15777000 seconds after iat). If you generated the secret long ago and it worked until today, this is your answer: it aged out, which people then misdiagnose as a rotation problem while rotating everything except the one thing that expired.

5. Is the ES256 algorithm actually used?

The secret must be signed with ES256 (the .p8 is an EC key). A generator defaulting to HS256 or RS256 produces a structurally valid JWT that Apple rejects. The decoded header should read "alg": "ES256".

6. Are the domain and return URL registered on the Services ID?

If the identifiers all match but auth still fails at the redirect step, check that the Services ID has your auth domain (for Supabase: <project-ref>.supabase.co) and the exact callback URL registered under its Sign in with Apple configuration.

The five minute reset, when you would rather stop debugging

When the state is too tangled, a clean rebuild is faster than archaeology:

  1. In the Apple portal: create a fresh key with Sign in with Apple enabled, download the new .p8, note the Key ID.
  2. Confirm the Services ID, Team ID, and domain/return URL config.
  3. Generate a brand new client-secret JWT (ES256, iss = Team ID, sub = Services ID, kid = the new Key ID, exp under 6 months).
  4. Paste it into your provider config (in Supabase: Authentication, then Providers, then Apple) and save.
  5. Test in a private window.

Every value is freshly derived, so any stale-state mismatch is gone.

If you would rather hand it over

We fix broken AI-built and Supabase apps for a living, auth is one of the three things we repair most, and the diagnosis is free: rescue.ticassociation.com. You pay only after the fix is verified working.

And if your app is Supabase and you have never audited its Row Level Security, our free read-only auditor finds the common holes in a minute: ticassociation.com/supabase-rls-audit.

Top comments (0)