Another thing to note. You can only attempt to validate the AuthorizationCode once. Attempting to send it twice will result in the "invalid_grant" error.
Also, the way I discovered that c# was broken and nothing else was that I attempted to execute the call using curl.
curl -i POST "https://appleid.apple.com/auth/token" \
-H "content-type: application/x-www-form-urlencoded" \
-d "client_id=com.appname.appname" \
-d "client_secret=JWT_GENERATED_IN_C#" \
-d "code=AUTHORIZATION_CODE_FROM_APP" \
-d "grant_type=authorization_code" \
-d "redirect_uri=https://myredirecturi.com"
Post
Replies
Boosts
Views
Activity
So, I finally solved this. Hopefully it helps someone in the future. I think the issue was my lack of understanding how to properly set the header in an HttpClient.
My new method to validate the Token is
public async Task<AppleVerifySignInTokenResponse> ValidateSignInToken(...)
{
try
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = "https://appleid.apple.com/auth/token";
var dictionary = new Dictionary<string, string>
{
{ "client_id", "com.appname.appname" },
{ "client_secret", GenerateAppleClientSecret() },
{ "code", authorizationCode },
{ "grant_type", "authorization_code" },
{ "redirect_uri", "https://myredirecturi.com" },
});
using (var content = new FormUrlEncodedContent(dictionary))
{
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var response = await httpClient.PostAsync("", content).ConfigureAwait(false);
if (response.IsSuccessStatusCode == true && response.Content != null)
{
var json = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<AppleVerifySignInTokenResponse>(json);
}
return null;
}
}
}
catch (Exception ex)
{
return null;
}
}
Also, note that I was struggling to figure out what my client_id should be. I have my app (com.appname.appname) and my service (com.appname.appnameserice). I did not use the service one anywhere.
This is what I use to generate the client_secret
public string GenerateAppleClientSecret()
{
// Content of the .p8 file (without -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----).
string privateKey = "MIG...";
// The 10-character key identifier from the portal (Also the name of the .p8 file. If file is AuthKey_12345.p8, put 12345)
string keyId = _KeyId;
// The Bundle Id of the iOS app (found in app store connect -> app -> App Information)
string clientId = "com.appname.appname";
// Found in developer portal
string teamId = "SLT12345";
var now = DateTimeOffset.UtcNow;
//Import the key using a Pkcs8PrivateBlob.
var cngKey = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
//Create new ECDsaCng object with the imported key.
var ecDsaCng = new ECDsaCng(cngKey);
ecDsaCng.HashAlgorithm = CngAlgorithm.ECDsaP256;
//Create new SigningCredentials instance which will be used for signing the token.
var signingCredentials = new SigningCredentials(new ECDsaSecurityKey(ecDsaCng), SecurityAlgorithms.EcdsaSha256);
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = teamId,
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("iss", teamId),
new Claim("iat", now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new Claim("exp", now.AddDays(7).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new Claim("aud", "https://appleid.apple.com"),
new Claim("sub", clientId)
}),
Expires = DateTime.UtcNow.AddMinutes(5),
SigningCredentials = signingCredentials,
};
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
if (commandModel.Kid.IsNullOrWhiteSpace() == false)
{
token.Header.Add("kid", keyId);
}
return tokenHandler.WriteToken(token);
}
I just followed this guide to the T and it didn't help either.
https://fluffy.es/how-to-solve-invalid_client-error-in-sign-in-with-apple/
I also verified the JWT at https://jwt.io/#debugger
I still get { "error": "invalid_client"}
Here are some various tutorials I have followed:
https://accedia.com/blog/dotnetifying-sign-in-with-apple/
https://www.scottbrady91.com/OpenID-Connect/Implementing-Sign-In-with-Apple-in-ASPNET-Core
Did you solve this?