public override async Task ValidateAsync([NotNull] AppleValidateIdTokenContext context) { if (!_tokenHandler.CanValidateToken) { throw new NotSupportedException($"The configured {nameof(JwtSecurityTokenHandler)} cannot validate tokens."); } byte[] keysJson = await _keyStore.LoadPublicKeysAsync(context); string json = Encoding.UTF8.GetString(keysJson); var keySet = JsonWebKeySet.Create(json); var parameters = new TokenValidationParameters() { ValidAudience = context.Options.ClientId, ValidIssuer = context.Options.TokenAudience, IssuerSigningKeys = keySet.Keys, }; try { _tokenHandler.ValidateToken(context.IdToken, parameters, out var _); } catch (Exception ex) { _logger.LogError( ex, "Apple ID token validation failed for issuer {TokenIssuer} and audience {TokenAudience}. ID Token: {IdToken}", parameters.ValidAudience, parameters.ValidIssuer, context.IdToken); throw; } }
/// <inheritdoc /> public override async Task <byte[]> LoadPublicKeysAsync([NotNull] AppleValidateIdTokenContext context) { if (_publicKey == null) { _publicKey = await LoadApplePublicKeysAsync(context); } return(_publicKey); }
/// <inheritdoc /> protected override async Task <AuthenticationTicket> CreateTicketAsync( [NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens) { string?idToken = tokens.Response !.RootElement.GetString("id_token"); Log.CreatingTicket(Logger); if (Logger.IsEnabled(LogLevel.Trace)) { Log.LogAccessToken(Logger, tokens.AccessToken); Log.LogRefreshToken(Logger, tokens.RefreshToken); Log.LogTokenType(Logger, tokens.TokenType); Log.LogExpiresIn(Logger, tokens.ExpiresIn); Log.LogTokenResponse(Logger, tokens.Response.RootElement); Log.LogIdToken(Logger, idToken); } if (string.IsNullOrWhiteSpace(idToken)) { throw new InvalidOperationException("No Apple ID token was returned in the OAuth token response."); } if (Options.ValidateTokens) { var validateIdContext = new AppleValidateIdTokenContext(Context, Scheme, Options, idToken); await Events.ValidateIdToken(validateIdContext); } var tokenClaims = ExtractClaimsFromToken(idToken); foreach (var claim in tokenClaims) { identity.AddClaim(claim); } var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, tokens.Response.RootElement); context.RunClaimActions(); await Events.CreatingTicket(context); return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name)); }
private async Task <byte[]> LoadApplePublicKeysAsync([NotNull] AppleValidateIdTokenContext context) { var response = await context.Options.Backchannel.GetAsync(context.Options.PublicKeyEndpoint, context.HttpContext.RequestAborted); if (!response.IsSuccessStatusCode) { _logger.LogError("An error occurred while retrieving the public keys from Apple: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync(context.HttpContext.RequestAborted)); throw new HttpRequestException("An error occurred while retrieving the public keys from Apple."); } return(await response.Content.ReadAsByteArrayAsync(context.HttpContext.RequestAborted)); }
public override async Task ValidateAsync([NotNull] AppleValidateIdTokenContext context) { if (context.Options.SecurityTokenHandler is null) { throw new InvalidOperationException("The options SecurityTokenHandler is null."); } if (!context.Options.SecurityTokenHandler.CanValidateToken) { throw new NotSupportedException($"The configured {nameof(JsonWebTokenHandler)} cannot validate tokens."); } if (context.Options.ConfigurationManager is null) { throw new InvalidOperationException($"An OpenID Connect configuration manager has not been set on the {nameof(AppleAuthenticationOptions)} instance."); } if (context.Options.TokenValidationParameters is null) { throw new InvalidOperationException($"Token validation parameters have not been set on the {nameof(AppleAuthenticationOptions)} instance."); } var configuration = await context.Options.ConfigurationManager.GetConfigurationAsync(context.HttpContext.RequestAborted); var validationParameters = context.Options.TokenValidationParameters.Clone(); validationParameters.IssuerSigningKeys = configuration.JsonWebKeySet.Keys; try { var result = context.Options.SecurityTokenHandler.ValidateToken(context.IdToken, validationParameters); if (result.Exception is not null || !result.IsValid) { throw new SecurityTokenValidationException("Apple ID token validation failed.", result.Exception); } } catch (Exception ex) { Log.TokenValidationFailed(_logger, ex, validationParameters.ValidIssuer, validationParameters.ValidAudience); Log.TokenInvalid(_logger, ex, context.IdToken); throw; } }
/// <inheritdoc /> public override async Task <byte[]> LoadPublicKeysAsync([NotNull] AppleValidateIdTokenContext context) { var utcNow = _clock.UtcNow; if (_publicKey == null || _reloadKeysAfter < utcNow) { _logger.LogInformation("Loading Apple public keys from {PublicKeyEndpoint}.", context.Options.PublicKeyEndpoint); _publicKey = await LoadApplePublicKeysAsync(context); _reloadKeysAfter = utcNow.Add(context.Options.PublicKeyCacheLifetime); _logger.LogInformation( "Loaded Apple public keys from {PublicKeyEndpoint}. Keys will be reloaded at or after {ReloadKeysAfter}.", context.Options.PublicKeyEndpoint, _reloadKeysAfter); } return(_publicKey); }
/// <summary> /// Invoked whenever the ID token needs to be validated. /// </summary> /// <param name="context">Contains information about the ID token to validate.</param> /// <returns> /// A <see cref="Task"/> representing the completed operation. /// </returns> public virtual async Task ValidateIdToken([NotNull] AppleValidateIdTokenContext context) => await OnValidateIdToken(context);
/// <summary> /// Validates the Apple ID token associated with the specified context as an asynchronous operation. /// </summary> /// <param name="context">The context to validate the ID token for.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation to validate the ID token. /// </returns> public abstract Task ValidateAsync(AppleValidateIdTokenContext context);