I'm trying to set up Sign In With Apple on my .NET 7 Web App (Not sure how many people here use this). I followed the guide by Scott Brady here: https://www.scottbrady91.com/openid-connect/implementing-sign-in-with-apple-in-aspnet-core
It reaches Apple Sign In OK, authenticates, and passes back to my server, but the callback responds with this error.
OpenIdConnectProtocolException: Message contains error: 'invalid_client', error_description: 'error_description is null', error_uri: 'error_uri is null'.
Googling hasn't helped much, other than I saw a post saying to wait 48 hours, which I have now done (not that that makes sense anyway).
Any idea whats been done wrong? Code below, replacing sensitive data.
Startup.cs
.AddOpenIdConnect("apple", async options =>
{
options.Authority = "https://appleid.apple.com"; // disco doc: https://appleid.apple.com/.well-known/openid-configuration
options.ClientId = "com.rackemapp.applelogin"; // Service ID
options.CallbackPath = "/signin-apple"; // corresponding to your redirect URI
options.ResponseType = "code id_token"; // hybrid flow due to lack of PKCE support
options.ResponseMode = "form_post"; // form post due to prevent PII in the URL
options.UsePkce = false; // apple does not currently support PKCE (April 2021)
options.DisableTelemetry = true;
options.Scope.Clear(); // apple does not support the profile scope
options.Scope.Add("openid");
options.Scope.Add("email");
options.Scope.Add("name");
options.Events.OnAuthorizationCodeReceived = context =>
{
context.TokenEndpointRequest.ClientSecret = AppleTokenGenerator.CreateNewToken();
return Task.CompletedTask;
};
});
Apple Token Generator
public static class AppleTokenGenerator
{
public static string CreateNewToken()
{
const string iss = "[MyTeamId]"; // your account's team ID found in the dev portal
const string aud = "https://appleid.apple.com";
const string sub = "com.rackemapp.applelogin"; // same as client_id
var now = DateTime.UtcNow;
// contents of your .p8 file
const string privateKey = "[MyKey]";
var ecdsa = ECDsa.Create();
ecdsa?.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
var handler = new JsonWebTokenHandler();
return handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = iss,
Audience = aud,
Claims = new Dictionary<string, object> { { "sub", sub } },
Expires = now.AddMinutes(5), // expiry can be a maximum of 6 months - generate one per request or re-use until expiration
IssuedAt = now,
NotBefore = now,
SigningCredentials = new SigningCredentials(new ECDsaSecurityKey(ecdsa), SecurityAlgorithms.EcdsaSha256)
});
}
}
Also attached, images of my keys and setp in developer portal