I'm trying to implement Sign In with Apple with an iOS app and a backend. The goal is this:
authorizationCode
authorizationCode
with another call to apple's server.Here I'm confused. In order to make this call, the backend needs to provide a bunch of parameters:
URL
https://appleid.apple.com/auth/token
Query Parameters
client_id = com.mycompany.appname
client_secret = ...
code = ... // `authorizationCode` from the signin in the iOS app
grant_type = authorization_code
I've generated a JWT
for the client_secret
:
JWT Properties
header:
kid: <key id, created on Apple Dev Portal>
claims:
iss: <team id>
iat: <current timestamp>
exp: <current timestamp + 180 days>
aud: "https://appleid.apple.com"
sub: "com.mycompany.appname"
Yesterday I've created two keys for two apps (A and B) on the Dev Portal, used it to generate secrets and today app A worked and I've got a positive response:
Positive response
{
"access_token" : "a1e64327924yt49f5937d643e25a48b81.0.mxwz.GN9TjJIJ5_4dR6WjbZoVNw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "rca76d9ebta644fde9edba269c61eeb41.0.mxwz.sMDUlXnnLLUOu2z0WlABoQ",
"id_token" : "eyJraWQiOiJBSURPUEsxIcccYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiZGUudHJ1ZmZscy5hcHBsZS1zaWduaW4tdGVzdCIsImV4cCI6MTU2NzcwMDI0MiwiaWF0IjoxNTY3Njk5NjQyLCJzdWaaaiIwMDA3NjkuYWY3NDdjMTlmZGRmNDJhNjhhYmFkZjhlNTQ1MmY3NjAuMjIwNSIsImF0X2hhc2giOiJrVThQTkZOUHYxS0RGUEtMT2hIY213IiwiYXV0aF90aW1lIjoxNTY3Njk5NjM5fQ.g3JD2MDGZ6wiVS9VMHpj24ER0XqJlunatmqpE7sRarMkhMHMTk7j8gty1lpqVBC6Z8L5CZuewdzLuJ5Odrd3_c1cX7gparTQE4jCyvyTACCPKHXReTC2hGRIEnAogcxv6HDWrtZgb3ENhoGhZW778d70DUdd-e4KKiAvzLOse-endHr51PaR1gv-cHPcwnm3NQZ144I-xhpU5TD9VQJ9IgLQvZGZ8fi8SOcu6rrk5ZOr0mpt0NbJNGYgH5-8iuSxo18QBWZDXoEGNsa4kS5GDkq5Cekxt7JsJFc_L1Np94giXhpbYHqhcO1pZSGFrJVaMvMMftZfuS_T3sh2yCqkcA"
}
B, however, still doesn't work. Today I revoked the key for A and created a new one and now it doesn't work anymore with the new one, but still with the old one, even though I deleted it on the Dev Portal. I'm so confused.
Response Error:
{
"error": "invalid_client"
}
I wonder if Apple needs some time indexing or something like that. I just want to understand how this works.
There are several reasons why it could happen:
client_id
for web should be Service id. For apps it should be App bundle id. (Even if you use native apple dialog get code
and later pass it to a webserver and then use it to request token API.) sub
in JWT call should be the same as client_id
.
See the answer there Apple forumexpires: DateTime.UtcNow.AddDays(2), // expiry can be a maximum of 6 months issuedAt: DateTime.UtcNow.AddDays(-1), notBefore: DateTime.UtcNow.AddDays(-1),
It failed with invalid_client on my webserver, since Apple considered it as a certificate from the future, when I had:
expires: DateTime.UtcNow.AddMinutes(5), // expiry can be a maximum of 6 months
issuedAt: DateTime.UtcNow,
notBefore: DateTime.UtcNow,
Content-Type: application/x-www-form-urlencoded
header instead of Content-Type: application/json
that some libraries like axios set by default.