`invalid_grant` when exchanging authorization code obtained from `ASAuthorizationAppleIDCredential` on the server-side

Hi,


I'm trying to implement Sign in with Apple where the authorization code is obtained from the

ASAuthorizationAppleIDCredential
native API, then sent to our server for verification via the REST API.


func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {      guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {           return      }       let authorizationCode = String(data: credential.authorizationCode!, encoding: .utf8) }

This is entirely successful. It uses an App ID with the correct entitlement.


I'm then attempting to exchange this on the server-side:

curl "https://appleid.apple.com/auth/token" \      -d "client_id=***" \      -d "client_secret=***" \      -d "grant_type=authorization_code" \      -d "code=***" \      -d "redirect_uri=***"

The client ID and secret are set up from a Services ID. The client secret JWT is generated correctly (this is validated, because I do not receive an

invalid_client
error).


Unfortunately, this fails with an

invalid_grant
error.


Of course the identifier of the Services ID (and therefore the client ID) and the identifier of the App ID are entirely different. This is because it's not possible to make a Services ID with the same identifier as the App ID. It is unclear what client ID should be used, or how to make a Services ID for use with data from the native API.


The redirect URI parameter is also very unclear - since there is no redirect in the process. However I have found that the result is always the same, regardless of what the parameter is set to (including if it is omitted entirely).


I suspect this process is failing because the authorisation code simply wasn't created for the client ID of the Services ID. However there is no way to indicate the intended use on the native API, as far as I can see.


I have scoured all the documentation at length and have still not gotten anywhere. What is the intended approach to take an authorization code from

ASAuthorizationAppleIDCredential
and exchange it with the REST API?


Thanks!

Answered by billinghamj in 367928022

Thanks. That makes sense.


For anyone else having trouble with this, this is how you can generate the client secret JWTs for different client IDs from the same key (Node JS example):


const key = `
-----BEGIN PRIVATE KEY-----
***
-----END PRIVATE KEY-----
`;

const teamId = '***';
const keyId = '***';
const webClientId = 'com.example.backend-auth-system'; // the Services ID
const appClientId = 'com.example.MyApp'; // the App ID

const jsonwebtoken = require('jsonwebtoken');

// for web use
jsonwebtoken.sign({}, key, {
  algorithm: 'ES256',
  expiresIn: '1d',
  audience: 'https://appleid.apple.com',
  subject: webClientId,
  issuer: teamId,
  keyid: keyId,
});

// for native use
jsonwebtoken.sign({}, key, {
  algorithm: 'ES256',
  expiresIn: '1d',
  audience: 'https://appleid.apple.com',
  subject: appClientId,
  issuer: teamId,
  keyid: keyId,
});


Presumably the Services IDs/App IDs all need to be associated with the same primary App ID. The key is then associated to that group via the primary App ID too.

hi everyone,
i had run, i use for verify use on service when i login apple account below app

curl --location --request POST 'https://appleid.apple.com/auth/token' \
  • -header 'content-type: application/x-www-form-urlencoded' \

  • -data-urlencode 'clientid="..."' \

  • -data-urlencode 'client

  • secret="...."' \
  • -data-urlencode 'granttype=authorizationcode' \

  • -data-urlencode 'code="...."'

with:
clientid: "App ID" -> Bundle ID
client
secret: "App Token" -> I use code code nodeJS of billinghamj, i run online on web page 'repl.it/languages/nodejs'
granttype: "authorizationcode"
code: "sent from the app"

if error = "InvalidClient" -> Bundle id or clientsecret wrong
error = "invalid_grant" = please get new code sent from the app.
thanks.

Guys, I finally understood.

In my case, my test was failed because I´m trying validate the authentication code more than one time.

You have only one chance to validate the authentication code!

Remember that authentication expires in a few minutes.

Then, you need to login each test and get new authentication code for each validate test.
`invalid_grant` when exchanging authorization code obtained from `ASAuthorizationAppleIDCredential` on the server-side
 
 
Q