Post not yet marked as solved
Post marked as unsolved with 0 replies, 4,041 views
Hello, I'm trying to implement Sign in with apple on our Web App, (We already have it on The Iphone App and AppleTv App).I've been trying to follow the documentation (unsucessfully) and I have tried a bunch of stuff to make it works. If I try to validate the auth token the sameway I do for the native apps I get INVALID_CLIENT response.So I added the KID and such and now I'm stuck on INVALID_GRANT (which is better according to what I understand).From the Js side I do the following (and all the flow seems correct as I get a response)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),//api requires base64urlsafe strings
FirstName: data.detail.user?.firstName,
LastName: data.detail.user?.LastName,
DeviceKey: deviceKey
});
});First thing that I note, unlike the documentation the state isn't returned, it might not be necessary in case of popup but that could be a sign that I'm doing something wrong.So now that I sent the Authorization code to my server I need to handle, I send it to appleid.apple.com/auth/token as a form data (and I don't forget to add a User-Agent)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)Finally my tokenGenerator , which slightly differs from the one user for the nativeApps token: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);
}
}My decoded token looks like the following :HEADER:ALGORITHM & TOKEN TYPE{ "alg": "ES256", "kid": "G*******W", "typ": "JWT"}PAYLOAD:DATA{ "sub": "com.********.weblogin", "nbf": 1583316961, "exp": 1591265761, "iat": 1583316961, "iss": "7********G", "aud": "https://appleid.apple.com"}I would take any hint as I've already lost a day trying to figure out what's going wrong ... Here is TokenGenerator for the nativeApp apple sign in that works perfectly fine : 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);
}
}