/// <summary>
 /// Represents an event called when deserializing an access token.
 /// </summary>
 /// <param name="context">The context instance associated with this event.</param>
 /// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
 public virtual Task DeserializeAccessToken(DeserializeAccessTokenContext context)
 => OnDeserializeAccessToken(context);
        private async Task <AuthenticationTicket> DeserializeAccessTokenAsync(string token, OpenIdConnectMessage request)
        {
            var notification = new DeserializeAccessTokenContext(Context, Options, request, token)
            {
                DataFormat           = Options.AccessTokenFormat,
                Issuer               = Context.GetIssuer(Options),
                SecurityTokenHandler = Options.AccessTokenHandler,
                SigningCredentials   = Options.SigningCredentials.FirstOrDefault()
            };

            await Options.Provider.DeserializeAccessToken(notification);

            // Directly return the authentication ticket if one
            // has been provided by DeserializeAccessToken.
            if (notification.Ticket != null)
            {
                return(notification.Ticket);
            }

            var handler = notification.SecurityTokenHandler as ISecurityTokenValidator;

            if (handler == null)
            {
                return(notification.DataFormat?.Unprotect(token));
            }

            // Create new validation parameters to validate the security token.
            // ValidateAudience and ValidateLifetime are always set to false:
            // if necessary, the audience and the expiration can be validated
            // in InvokeIntrospectionEndpointAsync or InvokeTokenEndpointAsync.
            var parameters = new TokenValidationParameters {
                IssuerSigningKey = notification.SigningCredentials.SigningKey,
                ValidIssuer      = notification.Issuer,
                ValidateAudience = false,
                ValidateLifetime = false
            };

            SecurityToken   securityToken;
            ClaimsPrincipal principal;

            try {
                principal = handler.ValidateToken(token, parameters, out securityToken);
            }

            catch (Exception exception) {
                Options.Logger.LogInformation("An exception occured when deserializing an access token: {Message}", exception.Message);

                return(null);
            }

            // Parameters stored in AuthenticationProperties are lost
            // when the identity token is serialized using a security token handler.
            // To mitigate that, they are inferred from the claims or the security token.
            var properties = new AuthenticationProperties {
                ExpiresUtc = securityToken.ValidTo,
                IssuedUtc  = securityToken.ValidFrom
            };

            var ticket = new AuthenticationTicket((ClaimsIdentity)principal.Identity, properties);

            var audiences = principal.FindAll(OpenIdConnectConstants.Claims.Audience);

            if (audiences.Any())
            {
                ticket.SetAudiences(audiences.Select(claim => claim.Value));
            }

            var presenters = principal.FindAll(OpenIdConnectConstants.Claims.AuthorizedParty);

            if (presenters.Any())
            {
                ticket.SetPresenters(presenters.Select(claim => claim.Value));
            }

            var scopes = principal.FindAll(OpenIdConnectConstants.Claims.Scope);

            if (scopes.Any())
            {
                ticket.SetScopes(scopes.Select(claim => claim.Value));
            }

            // Note: the token identifier may be stored in either the token_id claim
            // or in the jti claim, which is the standard name used by JWT tokens.
            var identifier = principal.FindFirst(OpenIdConnectConstants.Claims.JwtId) ??
                             principal.FindFirst(OpenIdConnectConstants.Claims.TokenId);

            if (identifier != null)
            {
                ticket.SetTicketId(identifier.Value);
            }

            var usage = principal.FindFirst(OpenIdConnectConstants.Claims.Usage);

            if (usage != null)
            {
                ticket.SetUsage(usage.Value);
            }

            var confidential = principal.FindFirst(OpenIdConnectConstants.Claims.Confidential);

            if (confidential != null && string.Equals(confidential.Value, "true", StringComparison.OrdinalIgnoreCase))
            {
                ticket.Properties.Dictionary[OpenIdConnectConstants.Properties.Confidential] = "true";
            }

            // Ensure the received ticket is an access token.
            if (!ticket.IsAccessToken())
            {
                Options.Logger.LogWarning("The received token was not an access token: {Token}.", token);

                return(null);
            }

            return(ticket);
        }
예제 #3
0
        private async Task <AuthenticationTicket> DeserializeAccessTokenAsync(string token, OpenIdConnectRequest request)
        {
            var notification = new DeserializeAccessTokenContext(Context, Options, request, token)
            {
                DataFormat           = Options.AccessTokenFormat,
                SecurityTokenHandler = Options.AccessTokenHandler
            };

            // Note: ValidateAudience and ValidateLifetime are always set to false:
            // if necessary, the audience and the expiration can be validated
            // in InvokeIntrospectionEndpointAsync or InvokeTokenEndpointAsync.
            notification.TokenValidationParameters = new TokenValidationParameters
            {
                IssuerSigningKeys = Options.SigningCredentials.Select(credentials => credentials.SigningKey),
                NameClaimType     = OpenIdConnectConstants.Claims.Name,
                RoleClaimType     = OpenIdConnectConstants.Claims.Role,
                ValidIssuer       = Context.GetIssuer(Options),
                ValidateAudience  = false,
                ValidateLifetime  = false
            };

            await Options.Provider.DeserializeAccessToken(notification);

            if (notification.HandledResponse || notification.Ticket != null)
            {
                notification.Ticket.SetUsage(OpenIdConnectConstants.Usages.AccessToken);

                return(notification.Ticket);
            }

            else if (notification.Skipped)
            {
                return(null);
            }

            var handler = notification.SecurityTokenHandler as ISecurityTokenValidator;

            if (handler == null)
            {
                return(notification.DataFormat?.Unprotect(token));
            }

            SecurityToken   securityToken;
            ClaimsPrincipal principal;

            try
            {
                if (!handler.CanReadToken(token))
                {
                    Logger.LogDebug("The access token handler refused to read the token: {Token}", token);

                    return(null);
                }

                principal = handler.ValidateToken(token, notification.TokenValidationParameters, out securityToken);
            }

            catch (Exception exception)
            {
                Logger.LogDebug("An exception occured when deserializing an access token: {Message}", exception.Message);

                return(null);
            }

            // Parameters stored in AuthenticationProperties are lost
            // when the identity token is serialized using a security token handler.
            // To mitigate that, they are inferred from the claims or the security token.
            var properties = new AuthenticationProperties
            {
                ExpiresUtc = securityToken.ValidTo,
                IssuedUtc  = securityToken.ValidFrom
            };

            var ticket = new AuthenticationTicket((ClaimsIdentity)principal.Identity, properties);

            var audiences = principal.FindAll(OpenIdConnectConstants.Claims.Audience);

            if (audiences.Any())
            {
                ticket.SetAudiences(audiences.Select(claim => claim.Value));
            }

            var presenters = principal.FindAll(OpenIdConnectConstants.Claims.AuthorizedParty);

            if (presenters.Any())
            {
                ticket.SetPresenters(presenters.Select(claim => claim.Value));
            }

            var scopes = principal.FindAll(OpenIdConnectConstants.Claims.Scope);

            if (scopes.Any())
            {
                ticket.SetScopes(scopes.Select(claim => claim.Value));
            }

            var identifier = principal.FindFirst(OpenIdConnectConstants.Claims.JwtId);

            if (identifier != null)
            {
                ticket.SetTicketId(identifier.Value);
            }

            var usage = principal.FindFirst(OpenIdConnectConstants.Claims.Usage);

            if (usage != null)
            {
                ticket.SetUsage(usage.Value);
            }

            var confidentiality = principal.FindFirst(OpenIdConnectConstants.Claims.ConfidentialityLevel);

            if (confidentiality != null)
            {
                ticket.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel, confidentiality.Value);
            }

            // Ensure the received ticket is an access token.
            if (!ticket.IsAccessToken())
            {
                Logger.LogDebug("The received token was not an access token: {Token}.", token);

                return(null);
            }

            return(ticket);
        }
        private async Task <AuthenticationTicket> DeserializeAccessTokenAsync(string token, OpenIdConnectRequest request)
        {
            var notification = new DeserializeAccessTokenContext(Context, Options, request, token)
            {
                DataFormat           = Options.AccessTokenFormat,
                SecurityTokenHandler = Options.AccessTokenHandler
            };

            // Note: ValidateAudience and ValidateLifetime are always set to false:
            // if necessary, the audience and the expiration can be validated
            // in InvokeIntrospectionEndpointAsync or InvokeTokenEndpointAsync.
            notification.TokenValidationParameters = new TokenValidationParameters
            {
                IssuerSigningKeys = Options.SigningCredentials.Select(credentials => credentials.SigningKey),
                NameClaimType     = OpenIdConnectConstants.Claims.Name,
                RoleClaimType     = OpenIdConnectConstants.Claims.Role,
                ValidIssuer       = Context.GetIssuer(Options),
                ValidateAudience  = false,
                ValidateLifetime  = false
            };

            await Options.Provider.DeserializeAccessToken(notification);

            if (notification.HandledResponse || notification.Ticket != null)
            {
                notification.Ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken);

                return(notification.Ticket);
            }

            else if (notification.Skipped)
            {
                return(null);
            }

            var handler = notification.SecurityTokenHandler as ISecurityTokenValidator;

            if (handler == null)
            {
                if (notification.DataFormat == null)
                {
                    throw new InvalidOperationException("A security token handler or data formatter must be provided.");
                }

                var value = notification.DataFormat.Unprotect(token);
                if (value == null)
                {
                    Logger.LogTrace("The received token was invalid or malformed: {Token}.", token);

                    return(null);
                }

                // Note: since the data formatter relies on a data protector using different "purposes" strings
                // per token type, the ticket returned by Unprotect() is guaranteed to be an access token.
                value.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken);

                Logger.LogTrace("The access token '{Token}' was successfully validated using " +
                                "the specified token data format: {Claims} ; {Properties}.",
                                token, value.Identity.Claims, value.Properties.Dictionary);

                return(value);
            }

            SecurityToken   securityToken;
            ClaimsPrincipal principal;

            try
            {
                if (!handler.CanReadToken(token))
                {
                    Logger.LogTrace("The access token '{Token}' was rejected by the security token handler.", token);

                    return(null);
                }

                principal = handler.ValidateToken(token, notification.TokenValidationParameters, out securityToken);
            }

            catch (Exception exception)
            {
                Logger.LogDebug("An exception occured while deserializing an access token: {Exception}.", exception);

                return(null);
            }

            // Parameters stored in AuthenticationProperties are lost
            // when the identity token is serialized using a security token handler.
            // To mitigate that, they are inferred from the claims or the security token.
            var properties = new AuthenticationProperties
            {
                ExpiresUtc = securityToken.ValidTo,
                IssuedUtc  = securityToken.ValidFrom
            };

            var ticket = new AuthenticationTicket((ClaimsIdentity)principal.Identity, properties)
                         .SetAudiences(principal.FindAll(OpenIdConnectConstants.Claims.Audience).Select(claim => claim.Value))
                         .SetConfidentialityLevel(principal.GetClaim(OpenIdConnectConstants.Claims.ConfidentialityLevel))
                         .SetPresenters(principal.FindAll(OpenIdConnectConstants.Claims.AuthorizedParty).Select(claim => claim.Value))
                         .SetScopes(principal.FindAll(OpenIdConnectConstants.Claims.Scope).Select(claim => claim.Value))
                         .SetTokenId(principal.GetClaim(OpenIdConnectConstants.Claims.JwtId))
                         .SetTokenUsage(principal.GetClaim(OpenIdConnectConstants.Claims.TokenUsage));

            // Ensure that the received ticket is an access token.
            if (!ticket.IsAccessToken())
            {
                Logger.LogTrace("The received token was not an access token: {Token}.", token);

                return(null);
            }

            Logger.LogTrace("The access token '{Token}' was successfully validated using " +
                            "the specified security token handler: {Claims} ; {Properties}.",
                            token, ticket.Identity.Claims, ticket.Properties.Dictionary);

            return(ticket);
        }