Я пытаюсь реализовать вход через Apple в нашем веб-приложении, он работает на AppleTV и IOS, но, похоже, тот же поток не работает с токеном от Apple JS, и я получаю INVALID_CLIENT
.
Кажется, что нужно несколько дополнительных шагов, поэтому я добавил KID и тому подобное, и теперь я застрял на INVALID_GRANT
(что лучше, насколько я понимаю, по крайней мере, клиент в порядке).
Со стороны Js
я делаю следующее: (Поток кажется правильным с соответствующим ответом в AppleIDSignInOnSuccess
)
AppleID.auth.init({
clientId: 'com.#########.weblogin',//service Id created
scope: 'name email',
state: state,
redirectURI: location.href,
usePopup: true //or false defaults to false
});
document.addEventListener('AppleIDSignInOnSuccess', (data) => {
//handle successful response
var deviceKey = document.cookie.replace(/(?:(?:^|.*;\s*)deviceKey\s*\=\s*([^;]*).*$)|^.*$/, "$1");
requestLogin({
AppleToken: data.detail.authorization.id_token,
AppleAuthorizationCode: btoa(data.detail.authorization.code),//our api requires base64urlsafe strings to be symetric with native apps
FirstName: data.detail.user?.firstName,
LastName: data.detail.user?.LastName,
DeviceKey: deviceKey
});
});
Следует отметить, что, в отличие от документации, [состояние] не не возвращается, это может быть необязательно в случае всплывающего окна, но это может быть признаком того, что что-то не так.
Теперь я обрабатываю код авторизации с помощью моего .NET
бэкэнда.
Hclient.DefaultRequestHeaders.Add("User-Agent", "Microsoft ASP.NET Core OpenIdConnect handler");
var datas = new Dictionary<string, string="">()
{
{ "client_id" , request.DeviceKey.StartsWith("WEB_") ? "com.########.weblogin" : "com.########.app" }, //use the serviceId and not the main appId for web
{ "code" , request.AppleAuthorizationCode.FromBase64UrlSafe().FromUtf8Bytes() },
{ "grant_type" , "authorization_code" },
{ "redirect_uri", "https://login.#######.com/signin-apple" },
{"client_secret",request.DeviceKey.StartsWith("WEB_") ? TokenGenerator2.CreateNewToken() : TokenGenerator.CreateNewToken() }//generate the client_secret differently for web
};
var formdata = new FormUrlEncodedContent(datas);
using (HttpResponseMessage res = Hclient.PostAsync("https://appleid.apple.com/auth/token",formdata ).Result)
Это вызов, который отвечает "INVALID_GRANT". И вот как я генерирую свой токен jwt client_secret.
public static class TokenGenerator2
{
public static string CreateNewToken()
{
const string iss = "7#######G"; // team ID
const string aud = "https://appleid.apple.com";
const string sub = "com.#######.weblogin"; // service Id
const string keyId = "G######W";//key Idassociated with the p8 file
const string privateKey = "MIGT####...#####"; // contents of AuthKey_[keyId].p8 file
var d = DateTime.UtcNow.AddDays(-5);//I was worried the date time was the issue so I took a laaaaarge one ...
var cngKey = CngKey.Import(
Convert.FromBase64String(privateKey),
CngKeyBlobFormat.Pkcs8PrivateBlob);
var handler = new JwtSecurityTokenHandler();
var securityKey = new ECDsaSecurityKey(new ECDsaCng(cngKey) { KeySize = 256 , HashAlgorithm = CngAlgorithm.ECDsaP256});
securityKey.KeyId = keyId;
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha256);
return handler.CreateEncodedJwt(iss, aud, new ClaimsIdentity(new List { new Claim("sub", sub) }),d, expires: d.AddMonths(3),d, signingCredentials: signingCredentials);
}
}
Для справки это то, как я создаю его для собственных приложений (поток, который работает)
public static class TokenGenerator
{
public static string CreateNewToken()
{
const string iss = "7#######G"; // your account's team ID found in the dev portal
const string aud = "https://appleid.apple.com";
const string sub = "com.######.app";
const string privateKey = "MIGTAg###...####"; // contents of .p8 file
var cngKey = CngKey.Import(
Convert.FromBase64String(privateKey),
CngKeyBlobFormat.Pkcs8PrivateBlob);
var d = DateTime.UtcNow.AddDays(-5);
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(
issuer: iss,
audience: aud,
subject: new ClaimsIdentity(new List { new Claim("sub", sub) }),
expires: d.AddMonths(3), // expiry can be a maximum of 6 months
issuedAt: d,
notBefore: d,
signingCredentials: new SigningCredentials(
new ECDsaSecurityKey(new ECDsaCng(cngKey)), SecurityAlgorithms.EcdsaSha256));
return handler.WriteToken(token);
}
}