/// <summary> /// Invoked when a token is to be validated, before final processing. /// </summary> public virtual Task ValidateToken(ValidateTokenContext context) => OnValidateToken(context);
protected override async Task <AuthenticateResult> HandleAuthenticateAsync() { var context = new RetrieveTokenContext(Context, Scheme, Options); await Events.RetrieveToken(context); if (context.Result != null) { Logger.LogInformation("The default authentication handling was skipped from user code."); return(context.Result); } var token = context.Token; if (string.IsNullOrEmpty(token)) { // Try to retrieve the access token from the authorization header. string header = Request.Headers[HeaderNames.Authorization]; if (string.IsNullOrEmpty(header)) { Logger.LogDebug("Authentication was skipped because no bearer token was received."); return(AuthenticateResult.NoResult()); } // Ensure that the authorization header contains the mandatory "Bearer" scheme. // See https://tools.ietf.org/html/rfc6750#section-2.1 if (!header.StartsWith(OAuthIntrospectionConstants.Schemes.Bearer + ' ', StringComparison.OrdinalIgnoreCase)) { Logger.LogDebug("Authentication was skipped because an incompatible " + "scheme was used in the 'Authorization' header."); return(AuthenticateResult.NoResult()); } // Extract the token from the authorization header. token = header.Substring(OAuthIntrospectionConstants.Schemes.Bearer.Length + 1).Trim(); if (string.IsNullOrEmpty(token)) { Logger.LogDebug("Authentication was skipped because the bearer token " + "was missing from the 'Authorization' header."); return(AuthenticateResult.NoResult()); } } // Try to resolve the authentication ticket from the distributed cache. If none // can be found, a new introspection request is sent to the authorization server. var ticket = await RetrieveTicketAsync(token); if (ticket == null) { // Return a failed authentication result if the introspection // request failed or if the "active" claim was false. var payload = await GetIntrospectionPayloadAsync(token); if (payload == null || !payload.Value <bool>(OAuthIntrospectionConstants.Claims.Active)) { Context.Features.Set(new OAuthIntrospectionFeature { Error = new OAuthIntrospectionError { Error = OAuthIntrospectionConstants.Errors.InvalidToken, ErrorDescription = "The access token is not valid." } }); return(AuthenticateResult.Fail("Authentication failed because the authorization " + "server rejected the access token.")); } // Create a new authentication ticket from the introspection // response returned by the authorization server. var result = await CreateTicketAsync(token, payload); if (!result.Succeeded) { Context.Features.Set(new OAuthIntrospectionFeature { Error = new OAuthIntrospectionError { Error = OAuthIntrospectionConstants.Errors.InvalidToken, ErrorDescription = "The access token is not valid." } }); return(result); } ticket = result.Ticket; await StoreTicketAsync(token, ticket); } // Ensure that the token can be used as an access token. if (!ValidateTokenUsage(ticket)) { Context.Features.Set(new OAuthIntrospectionFeature { Error = new OAuthIntrospectionError { Error = OAuthIntrospectionConstants.Errors.InvalidToken, ErrorDescription = "The access token is not valid." } }); return(AuthenticateResult.Fail("Authentication failed because the token was not an access token.")); } // Ensure that the authentication ticket is still valid. if (ticket.Properties.ExpiresUtc.HasValue && ticket.Properties.ExpiresUtc.Value < Options.SystemClock.UtcNow) { Context.Features.Set(new OAuthIntrospectionFeature { Error = new OAuthIntrospectionError { Error = OAuthIntrospectionConstants.Errors.InvalidToken, ErrorDescription = "The access token is no longer valid." } }); return(AuthenticateResult.Fail("Authentication failed because the access token was expired.")); } // Ensure that the access token was issued // to be used with this resource server. if (!ValidateAudience(ticket)) { Context.Features.Set(new OAuthIntrospectionFeature { Error = new OAuthIntrospectionError { Error = OAuthIntrospectionConstants.Errors.InvalidToken, ErrorDescription = "The access token is not valid for this resource server." } }); return(AuthenticateResult.Fail("Authentication failed because the access token " + "was not valid for this resource server.")); } var notification = new ValidateTokenContext(Context, Scheme, Options, ticket); await Events.ValidateToken(notification); if (notification.Result != null) { Logger.LogInformation("The default authentication handling was skipped from user code."); return(notification.Result); } // Optimization: avoid allocating a new AuthenticationTicket // if the principal/properties instances were not replaced. if (ReferenceEquals(notification.Principal, ticket.Principal) && ReferenceEquals(notification.Properties, ticket.Properties)) { return(AuthenticateResult.Success(ticket)); } return(AuthenticateResult.Success(new AuthenticationTicket( notification.Principal, notification.Properties, Scheme.Name))); }