I'm writing a server application that handles Apple ID authorization from the iOS device. The server is written in C# .net ASP.NET framework.
To authenticate I'm using this data:
_config.ClientId
=com.organization.ios.app_name
,_config.PrivateKeyId
- .p8 file key id_config._DevelopmentTeam
- code located before client id name ,X.com.organization.ios.app_name
that X,_config.PrivateKey
- private key from .p8 file
From request from an iOS device I'm getting:
request.AuthCode
- authorization code,request.IdentityToken
- token.
Code is really simple:
public Task<ResponseDTO> AuthenticateWithApple(RequestDTO request)
{
var contentData = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("client_id", _config.ClientId),
new KeyValuePair<string, string>("client_secret", GenerateAppleJwtToken()),
new KeyValuePair<string, string>("code", passData.AuthCode);
new KeyValuePair<string, string>("grand_type", "authorization_code"),
new KeyValuePair<string, string>("refresh_token", "");
new KeyValuePair<string, string>("redirect_uri", "");
};
try
{
var content = new FormUrlEncodedContent(contentData);
var response = await httpClient.PostAsync(_requestUrl, content);
var rawResponseData = await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.OK)
{
var responseData = JsonConvert.DeserializeObject<ResponseDTO>(rawResponseData);
return responseData;
}
else
{
var errorMessage = $"Status code {response.StatusCode}: \"{rawResponseData}\"";
throw new ArgumentException(errorMessage);
}
}
catch (Exception exc)
{
var error = new ErrorResponseViewModel();
var errorMessage = $"Problem encountered during authorization: {exc.Message}";
error.AddError(ErrorTypes.UNAUTHORIZED, nameof(AppleAuthenticationProvider), errorMessage);
return new ResponseDTO()
{
Errors = error
};
}
}
And for creating JWT client secret code is:
private string GenerateAppleJwtToken()
{
var issueTime = DateTime.Now;
var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var iat = (int) issueTime.Subtract(zeroTime).TotalSeconds;
var exp = (int) issueTime.AddMinutes(5).Subtract(zeroTime).TotalSeconds;
var headers = new Dictionary<string, object>()
{
{ "alg", "ES256" },
{ "typ", "JWT" },
{ "kid", _config.PrivateKeyId },
};
var payload = new Dictionary<string, object>()
{
{ "sub", _config.ClientId },
{ "aud", "https://appleid.apple.com" },
{ "iss", _config.DevelopmentTeam },
{ "exp", exp },
{ "iat", iat }
};
var cngKey = CngKey.Import(Convert.FromBase64String(_config.PrivateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
var ecdsa = new ECDsaCng(cngKey);
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(headers, Formatting.None));
byte[] claimsBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
var base64Payload = Base64UrlEncoder.Encode(headerBytes) + "." + Base64UrlEncoder.Encode(claimsBytes);
var signature = ecdsa.SignData(Encoding.UTF8.GetBytes(base64Payload), HashAlgorithmName.SHA256);
return base64Payload + "." + Base64UrlEncoder.Encode(signature);
}
According to many code implementations of Apple ID authorization, this should work, but it doesn't. Error message is that i recive is: "unsupported_grant_type"
You are sending a parameter
grand_type
, butgrant_type
is expected. Mind the typo in grand.