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!
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.