我正在尝试通过iOS应用和后端实施使用Apple登录。目标是这样:
authorizationCode
authorizationCode
。在这里我很困惑。为了进行此调用,后端需要提供一堆参数:
URL
https://appleid.apple.com/auth/token
查询参数
client_id = com.mycompany.appname
client_secret = ...
code = ... // `authorizationCode` from the signin in the iOS app
grant_type = authorization_code
我为JWT
生成了client_secret
:
JWT属性
header:
kid: <key id, created on Apple Dev Portal>
claims:
iss: <team id>
iat: <current timestamp>
iat: <current timestamp + 180 days>
aud: "https://appleid.apple.com"
sub: "com.mycompany.appname"
昨天,我在开发门户网站上为两个应用程序(A和B)创建了两个密钥,并用它来生成秘密,而今天应用程序A起作用了,我得到了积极的回应:
积极回应
{
"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仍然不起作用。今天,我撤消了A的密钥并创建了一个新密钥,现在即使在开发门户网站上将其删除,它也不再适用于新密钥,但仍然不能与旧密钥一起使用。我很困惑。
响应错误:
{
"error": "invalid_client"
}
我想知道苹果是否需要一些时间索引或类似的东西。我只想了解它的工作原理。
答案 0 :(得分:6)
我在如何通过服务器端iOS应用验证与Apple的登录方面花了很多力气,却找不到太多有关它的文档。
如果对任何人有帮助,我将在这里留在nodeJS中。我实际上遵循了Curtis Herbert概述的here方法
{
"keys": [
{
"kty": "RSA",
"kid": "86D88Kf",
"use": "sig",
"alg": "RS256",
"n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ",
"e": "AQAB"
},
{
"kty": "RSA",
"kid": "eXaunmL",
"use": "sig",
"alg": "RS256",
"n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
"e": "AQAB"
}
]
}
const axios = require('axios').default;
const jose = require('jose')
const {
JWKS, // JSON Web Key Set (JWKS)
JWT, // JSON Web Token (JWT)
errors // errors utilized by jose
} = jose
axios.get('https://appleid.apple.com/auth/keys')
.then(function (response) {
// handle success
const key = jose.JWKS.asKeyStore(response.data);
const verified = jose.JWT.verify(identityToken, key);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
最后,您得到一个具有与解码JWT(identityToken)相同结构的对象,您可以在其中验证该对象;
{
"iss": "https://appleid.apple.com",
"aud": "BundleID",
"sub": "credential.user",
"email": "credential.email",
}
答案 1 :(得分:2)
发生这种情况的原因有很多:
client_id
应为服务ID 。对于应用,它应该是应用捆绑包ID 。 JWT调用中的sub
应该与client_id
相同。
See the answer there Apple forum 在调用令牌API时指定User-Agent标头也很重要。还值得一提的是curl会引发此错误,而当您从Web服务器调用它时,它将正常工作。
答案 2 :(得分:0)
您的本机APP ID是您的捆绑包ID,上面带有您的团队ID,以逗号分隔。
“ Apple App ID是一个两部分的字符串,用于标识一个或多个应用程序。具体地说,Apple App ID是您的团队ID和捆绑包ID,并带有句点,用于example:1A234H7ABC.com。 yourdomain.YourApp。”
我遇到了同样的问题,但是要使其正常工作。
答案 3 :(得分:0)
当您的密钥存在问题时,我们将获得“无效的客户端”,而在通过ES256验证授权代码时,将获得“子”。使用下面的代码,因为我已经解决了我的问题,所以您的问题将得到解决。
代码:
private static CngKey GetPrivateKey()
{
using (var reader = File.OpenText(ConfigurationManager.AppSettings["key2"].ToString())) //put your key's path like C:\ABC.p8
{
var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();
var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
return EccKey.New(x, y, d);
}
}
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.Now;
var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;
var payload = new Dictionary<string, object>()
{
{ "sub", "com.xyzttt" }, // you registered app
{ "aud", "https://appleid.apple.com"},
{ "iss", "ABCDEFGHttt" }, //Team id
{ "exp", exp }, }, //current time + @@@@
{ "iat", iat } } // current time
};
var extraHeader = new Dictionary<string, object>()
{
{ "alg", "ES256" },
{ "kid", "5ABCDEFGH123tt21"}, //key id
};
var sb = GetPrivateKey();
var ecdsa = new ECDsaCng(sb);
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(extraHeader, Formatting.None));
byte[] claimsBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
var Rpayload = Base64UrlEncode(headerBytes) + "." + Base64UrlEncode(claimsBytes);
var signature = ecdsa.SignData(Encoding.UTF8.GetBytes(Rpayload), HashAlgorithmName.SHA256);
var data = Base64UrlEncode(signature);
string token = Rpayload + "." + data;
return token;
答案 4 :(得分:0)
对于仍在为此苦苦挣扎的人:在创建我的第一个密钥后,我不得不等待 48 小时,以便 Apple 服务器接收它。在过去的两天里,我一直在尝试我可以在网上找到的每一个解决方案,认为我在某个地方犯了错误。不......什么是修复它的时间(或者苹果公司的某个人在他们的最后修改了一些东西)。我在 10 分钟前和现在调用了完全相同的 curl 请求,从苹果的 api 获得了两种不同的响应。完全相同的请求(包括所有参数)。
请耐心等待。在 https://developer.apple.com/account/resources/authkeys/list
创建第一个密钥后等待 48 小时答案 5 :(得分:0)
如果有人想在java后端使用它,这里是nimbus-jose-jwt java lib的有效代码
var publicKeys = JWKSet.load(new URL("https://appleid.apple.com/auth/keys"));
publicKeys.getKeys().forEach(jwk -> {
try {
System.out.println(JWSObject.parse(appleIdentityToken)
.verify(new RSASSAVerifier(jwk.toRSAKey())));
} catch (JOSEException | ParseException joseException) {
joseException.printStackTrace();
}
});