private async Task <string> SerializeRefreshTokenAsync(
            ClaimsPrincipal principal, AuthenticationProperties properties,
            OpenIdConnectMessage request, OpenIdConnectMessage response)
        {
            // properties.IssuedUtc and properties.ExpiresUtc
            // should always be preferred when explicitly set.
            if (properties.IssuedUtc == null)
            {
                properties.IssuedUtc = Options.SystemClock.UtcNow;
            }

            if (properties.ExpiresUtc == null)
            {
                properties.ExpiresUtc = properties.IssuedUtc + Options.RefreshTokenLifetime;
            }

            properties.SetUsage(OpenIdConnectConstants.Usages.RefreshToken);

            // Claims in refresh tokens are never filtered as they are supposed to be opaque:
            // SerializeAccessTokenAsync and SerializeIdentityTokenAsync are responsible of ensuring
            // that subsequent access and identity tokens are correctly filtered.
            var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

            var notification = new SerializeRefreshTokenContext(Context, Options, request, response, ticket)
            {
                DataFormat = Options.RefreshTokenFormat
            };

            await Options.Provider.SerializeRefreshToken(notification);

            if (!string.IsNullOrEmpty(notification.RefreshToken))
            {
                return(notification.RefreshToken);
            }

            // Allow the application to change the authentication
            // ticket from the SerializeRefreshTokenAsync event.
            ticket = notification.AuthenticationTicket;
            ticket.Properties.CopyTo(properties);

            return(notification.DataFormat?.Protect(ticket));
        }
        private async Task <AuthenticationTicket> DeserializeIdentityTokenAsync(string token, OpenIdConnectMessage request)
        {
            try {
                var notification = new DeserializeIdentityTokenContext(Context, Options, request, token)
                {
                    Issuer = Context.GetIssuer(Options),
                    SecurityTokenHandler = Options.IdentityTokenHandler,
                    SignatureProvider    = Options.SignatureProvider,
                    SigningKey           = Options.SigningCredentials.Select(credentials => credentials.Key).FirstOrDefault()
                };

                // Sets the default deserializer used to resolve the
                // authentication ticket corresponding to the identity token.
                notification.Deserializer = payload => {
                    if (notification.SecurityTokenHandler == null)
                    {
                        return(Task.FromResult <AuthenticationTicket>(null));
                    }

                    // 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 InvokeValidationEndpointAsync or InvokeTokenEndpointAsync.
                    var parameters = new TokenValidationParameters {
                        IssuerSigningKey = notification.SigningKey,
                        ValidIssuer      = notification.Issuer,
                        ValidateAudience = false,
                        ValidateLifetime = false
                    };

                    SecurityToken securityToken;
                    var           principal = notification.SecurityTokenHandler.ValidateToken(payload, parameters, out securityToken);

                    // 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 audiences = principal.FindAll(JwtRegisteredClaimNames.Aud);
                    if (audiences.Any())
                    {
                        properties.SetAudiences(audiences.Select(claim => claim.Value));
                    }

                    var usage = principal.FindFirst(OpenIdConnectConstants.Extra.Usage);
                    if (usage != null)
                    {
                        properties.SetUsage(usage.Value);
                    }

                    if (principal.Claims.Any(claim => claim.Type == OpenIdConnectConstants.Extra.Confidential))
                    {
                        properties.Items[OpenIdConnectConstants.Extra.Confidential] = "true";
                    }

                    return(Task.FromResult(new AuthenticationTicket(principal, properties, Options.AuthenticationScheme)));
                };

                await Options.Provider.DeserializeIdentityToken(notification);

                // Directly return the authentication ticket if one
                // has been provided by DeserializeIdentityToken.
                // Treat a non-null ticket like an implicit HandleResponse call.
                if (notification.HandledResponse || notification.AuthenticationTicket != null)
                {
                    if (notification.AuthenticationTicket == null)
                    {
                        return(null);
                    }

                    // Ensure the received ticket is an identity token.
                    if (!notification.AuthenticationTicket.IsIdentityToken())
                    {
                        Logger.LogVerbose("The received token was not an identity token: {0}.", token);

                        return(null);
                    }

                    return(notification.AuthenticationTicket);
                }

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

                var ticket = await notification.DeserializeTicketAsync(token);

                if (ticket == null)
                {
                    return(null);
                }

                // Ensure the received ticket is an identity token.
                if (!ticket.IsIdentityToken())
                {
                    Logger.LogVerbose("The received token was not an identity token: {0}.", token);

                    return(null);
                }

                return(ticket);
            }

            catch (Exception exception) {
                Logger.LogWarning("An exception occured when deserializing an identity token.", exception);

                return(null);
            }
        }
        private async Task <string> SerializeRefreshTokenAsync(
            ClaimsPrincipal principal, AuthenticationProperties properties,
            OpenIdConnectMessage request, OpenIdConnectMessage response)
        {
            try {
                // properties.IssuedUtc and properties.ExpiresUtc
                // should always be preferred when explicitly set.
                if (properties.IssuedUtc == null)
                {
                    properties.IssuedUtc = Options.SystemClock.UtcNow;
                }

                if (properties.ExpiresUtc == null)
                {
                    properties.ExpiresUtc = properties.IssuedUtc + Options.RefreshTokenLifetime;
                }

                properties.SetUsage(OpenIdConnectConstants.Usages.RefreshToken);

                // Claims in refresh tokens are never filtered as they are supposed to be opaque:
                // SerializeAccessTokenAsync and SerializeIdentityTokenAsync are responsible of ensuring
                // that subsequent access and identity tokens are correctly filtered.
                var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

                var notification = new SerializeRefreshTokenContext(Context, Options, request, response, ticket)
                {
                    DataFormat = Options.RefreshTokenFormat
                };

                // Sets the default refresh token serializer.
                notification.Serializer = payload => {
                    return(Task.FromResult(notification.DataFormat?.Protect(payload)));
                };

                await Options.Provider.SerializeRefreshToken(notification);

                // Treat a non-null refresh token like an implicit HandleResponse call.
                if (notification.HandledResponse || !string.IsNullOrEmpty(notification.RefreshToken))
                {
                    return(notification.RefreshToken);
                }

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

                // Allow the application to change the authentication
                // ticket from the SerializeRefreshTokenAsync event.
                ticket = notification.AuthenticationTicket;
                ticket.Properties.CopyTo(properties);

                return(await notification.SerializeTicketAsync());
            }

            catch (Exception exception) {
                Logger.LogWarning("An exception occured when serializing a refresh token.", exception);

                return(null);
            }
        }
        private async Task <string> SerializeIdentityTokenAsync(
            ClaimsPrincipal principal, AuthenticationProperties properties,
            OpenIdConnectMessage request, OpenIdConnectMessage response)
        {
            try {
                // properties.IssuedUtc and properties.ExpiresUtc
                // should always be preferred when explicitly set.
                if (properties.IssuedUtc == null)
                {
                    properties.IssuedUtc = Options.SystemClock.UtcNow;
                }

                if (properties.ExpiresUtc == null)
                {
                    properties.ExpiresUtc = properties.IssuedUtc + Options.IdentityTokenLifetime;
                }

                properties.SetUsage(OpenIdConnectConstants.Usages.IdToken);

                // Replace the principal by a new one containing only the filtered claims.
                // Actors identities are also filtered (delegation scenarios).
                principal = principal.Clone(claim => {
                    // ClaimTypes.NameIdentifier and JwtRegisteredClaimNames.Sub are never excluded.
                    if (string.Equals(claim.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal) ||
                        string.Equals(claim.Type, JwtRegisteredClaimNames.Sub, StringComparison.Ordinal))
                    {
                        return(true);
                    }

                    // Claims whose destination is not explicitly referenced or
                    // doesn't contain "id_token" are not included in the identity token.
                    return(claim.HasDestination(OpenIdConnectConstants.ResponseTypes.IdToken));
                });

                var identity = (ClaimsIdentity)principal.Identity;

                // Create a new ticket containing the updated properties and the filtered principal.
                var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

                var notification = new SerializeIdentityTokenContext(Context, Options, request, response, ticket)
                {
                    Confidential         = properties.IsConfidential(),
                    Issuer               = Context.GetIssuer(Options),
                    Nonce                = request.Nonce,
                    SecurityTokenHandler = Options.IdentityTokenHandler,
                    SigningCredentials   = Options.SigningCredentials.FirstOrDefault()
                };

                // If a nonce was present in the authorization request, it MUST
                // be included in the id_token generated by the token endpoint.
                // See http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
                if (request.IsAuthorizationCodeGrantType())
                {
                    // Restore the nonce stored in the authentication
                    // ticket extracted from the authorization code.
                    notification.Nonce = properties.GetNonce();
                }

                // While the 'sub' claim is declared mandatory by the OIDC specs,
                // it is not always issued as-is by the authorization servers.
                // When missing, the name identifier claim is used as a substitute.
                // See http://openid.net/specs/openid-connect-core-1_0.html#IDToken
                notification.Subject = identity.GetClaim(JwtRegisteredClaimNames.Sub) ??
                                       identity.GetClaim(ClaimTypes.NameIdentifier);

                // Only add client_id in the audiences list if it is non-null.
                if (!string.IsNullOrEmpty(request.ClientId))
                {
                    notification.Audiences.Add(request.ClientId);
                }

                if (!string.IsNullOrEmpty(response.Code))
                {
                    using (var algorithm = SHA256.Create()) {
                        // Create the c_hash using the authorization code returned by SerializeAuthorizationCodeAsync.
                        var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(response.Code));

                        // Note: only the left-most half of the hash of the octets is used.
                        // See http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
                        notification.CHash = Base64UrlEncoder.Encode(hash, 0, hash.Length / 2);
                    }
                }

                if (!string.IsNullOrEmpty(response.AccessToken))
                {
                    using (var algorithm = SHA256.Create()) {
                        // Create the at_hash using the access token returned by SerializeAccessTokenAsync.
                        var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(response.AccessToken));

                        // Note: only the left-most half of the hash of the octets is used.
                        // See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
                        notification.AtHash = Base64UrlEncoder.Encode(hash, 0, hash.Length / 2);
                    }
                }

                // Sets the default identity token serializer.
                notification.Serializer = payload => {
                    if (notification.SecurityTokenHandler == null)
                    {
                        return(Task.FromResult <string>(null));
                    }

                    if (string.IsNullOrEmpty(notification.Subject))
                    {
                        throw new InvalidOperationException(
                                  "A unique identifier cannot be found to generate a 'sub' claim. " +
                                  "Make sure to either add a 'sub' or a 'ClaimTypes.NameIdentifier' claim " +
                                  "in the returned ClaimsIdentity before calling SignIn.");
                    }

                    // Extract the main identity from the principal.
                    identity = (ClaimsIdentity)payload.Principal.Identity;

                    // Store the unique subject identifier as a claim.
                    identity.AddClaim(JwtRegisteredClaimNames.Sub, notification.Subject);

                    // Remove the ClaimTypes.NameIdentifier claims to avoid getting duplicate claims.
                    // Note: the "sub" claim is automatically mapped by JwtSecurityTokenHandler
                    // to ClaimTypes.NameIdentifier when validating a JWT token.
                    // Note: make sure to call ToArray() to avoid an InvalidOperationException
                    // on old versions of Mono, where FindAll() is implemented using an iterator.
                    foreach (var claim in identity.FindAll(ClaimTypes.NameIdentifier).ToArray())
                    {
                        identity.RemoveClaim(claim);
                    }

                    // Store the "usage" property as a claim.
                    identity.AddClaim(OpenIdConnectConstants.Extra.Usage, payload.Properties.GetUsage());

                    // Store the audiences as claims.
                    foreach (var audience in notification.Audiences)
                    {
                        identity.AddClaim(JwtRegisteredClaimNames.Aud, audience);
                    }

                    if (!string.IsNullOrEmpty(notification.AtHash))
                    {
                        identity.AddClaim("at_hash", notification.AtHash);
                    }

                    if (!string.IsNullOrEmpty(notification.CHash))
                    {
                        identity.AddClaim(JwtRegisteredClaimNames.CHash, notification.CHash);
                    }

                    if (!string.IsNullOrEmpty(notification.Nonce))
                    {
                        identity.AddClaim(JwtRegisteredClaimNames.Nonce, notification.Nonce);
                    }

                    // If the ticket is marked as confidential, add a new
                    // "confidential" claim in the security token.
                    if (notification.Confidential)
                    {
                        identity.AddClaim(new Claim(OpenIdConnectConstants.Extra.Confidential, "true", ClaimValueTypes.Boolean));
                    }

                    var token = notification.SecurityTokenHandler.CreateToken(
                        subject: identity,
                        issuer: notification.Issuer,
                        signingCredentials: notification.SigningCredentials,
                        issuedAt: payload.Properties.IssuedUtc.Value.UtcDateTime,
                        notBefore: payload.Properties.IssuedUtc.Value.UtcDateTime,
                        expires: payload.Properties.ExpiresUtc.Value.UtcDateTime);

                    if (notification.SigningCredentials != null)
                    {
                        var x509SecurityKey = notification.SigningCredentials.Key as X509SecurityKey;
                        if (x509SecurityKey != null)
                        {
                            // Note: unlike "kid", "x5t" is not automatically added by JwtHeader's constructor in IdentityModel for ASP.NET 5.
                            // Though not required by the specifications, this property is needed for IdentityModel for Katana to work correctly.
                            // See https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/132
                            // and https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/181.
                            token.Header[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SecurityKey.Certificate.GetCertHash());
                        }

                        object identifier;
                        if (!token.Header.TryGetValue(JwtHeaderParameterNames.Kid, out identifier) || identifier == null)
                        {
                            // When the token doesn't contain a "kid" parameter in the header, automatically
                            // add one using the identifier specified in the signing credentials.
                            identifier = notification.SigningCredentials.Kid;

                            if (identifier == null)
                            {
                                // When no key identifier has been explicitly added by the developer, a "kid" is automatically
                                // inferred from the hexadecimal representation of the certificate thumbprint (SHA-1).
                                if (x509SecurityKey != null)
                                {
                                    identifier = x509SecurityKey.Certificate.Thumbprint;
                                }

                                // When no key identifier has been explicitly added by the developer, a "kid"
                                // is automatically inferred from the modulus if the signing key is a RSA key.
                                var rsaSecurityKey = notification.SigningCredentials.Key as RsaSecurityKey;
                                if (rsaSecurityKey != null)
                                {
                                    // Only use the 40 first chars to match the identifier used by the JWKS endpoint.
                                    identifier = Base64UrlEncoder.Encode(rsaSecurityKey.Parameters.Modulus)
                                                 .Substring(0, 40)
                                                 .ToUpperInvariant();
                                }
                            }

                            token.Header[JwtHeaderParameterNames.Kid] = identifier;
                        }
                    }

                    return(Task.FromResult(notification.SecurityTokenHandler.WriteToken(token)));
                };

                await Options.Provider.SerializeIdentityToken(notification);

                // Treat a non-null identity token like an implicit HandleResponse call.
                if (notification.HandledResponse || !string.IsNullOrEmpty(notification.IdentityToken))
                {
                    return(notification.IdentityToken);
                }

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

                // Allow the application to change the authentication
                // ticket from the SerializeIdentityTokenAsync event.
                ticket = notification.AuthenticationTicket;
                ticket.Properties.CopyTo(properties);

                return(await notification.SerializeTicketAsync());
            }

            catch (Exception exception) {
                Logger.LogWarning("An exception occured when serializing an identity token.", exception);

                return(null);
            }
        }
        private async Task <string> SerializeAuthorizationCodeAsync(
            ClaimsPrincipal principal, AuthenticationProperties properties,
            OpenIdConnectMessage request, OpenIdConnectMessage response)
        {
            try {
                // properties.IssuedUtc and properties.ExpiresUtc
                // should always be preferred when explicitly set.
                if (properties.IssuedUtc == null)
                {
                    properties.IssuedUtc = Options.SystemClock.UtcNow;
                }

                if (properties.ExpiresUtc == null)
                {
                    properties.ExpiresUtc = properties.IssuedUtc + Options.AuthorizationCodeLifetime;
                }

                properties.SetUsage(OpenIdConnectConstants.Usages.Code);

                // Claims in authorization codes are never filtered as they are supposed to be opaque:
                // SerializeAccessTokenAsync and SerializeIdentityTokenAsync are responsible of ensuring
                // that subsequent access and identity tokens are correctly filtered.
                var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

                var notification = new SerializeAuthorizationCodeContext(Context, Options, request, response, ticket)
                {
                    DataFormat = Options.AuthorizationCodeFormat
                };

                // Sets the default authorization code serializer.
                notification.Serializer = payload => {
                    return(Task.FromResult(notification.DataFormat?.Protect(payload)));
                };

                await Options.Provider.SerializeAuthorizationCode(notification);

                // Treat a non-null authorization code like an implicit HandleResponse call.
                if (notification.HandledResponse || !string.IsNullOrEmpty(notification.AuthorizationCode))
                {
                    return(notification.AuthorizationCode);
                }

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

                // Allow the application to change the authentication
                // ticket from the SerializeAuthorizationCode event.
                ticket = notification.AuthenticationTicket;
                ticket.Properties.CopyTo(properties);

                var key = GenerateKey(256 / 8);

                using (var stream = new MemoryStream())
                    using (var writter = new StreamWriter(stream)) {
                        writter.Write(await notification.SerializeTicketAsync());
                        writter.Flush();

                        await Options.Cache.SetAsync(key, options => {
                            options.AbsoluteExpiration = ticket.Properties.ExpiresUtc;

                            return(stream.ToArray());
                        });
                    }

                return(key);
            }

            catch (Exception exception) {
                Logger.LogWarning("An exception occured when serializing an authorization code.", exception);

                return(null);
            }
        }
        private async Task <string> SerializeAccessTokenAsync(
            ClaimsPrincipal principal, AuthenticationProperties properties,
            OpenIdConnectMessage request, OpenIdConnectMessage response)
        {
            try {
                // properties.IssuedUtc and properties.ExpiresUtc
                // should always be preferred when explicitly set.
                if (properties.IssuedUtc == null)
                {
                    properties.IssuedUtc = Options.SystemClock.UtcNow;
                }

                if (properties.ExpiresUtc == null)
                {
                    properties.ExpiresUtc = properties.IssuedUtc + Options.AccessTokenLifetime;
                }

                properties.SetUsage(OpenIdConnectConstants.Usages.AccessToken);

                // Create a new principal containing only the filtered claims.
                // Actors identities are also filtered (delegation scenarios).
                principal = principal.Clone(claim => {
                    // ClaimTypes.NameIdentifier and JwtRegisteredClaimNames.Sub are never excluded.
                    if (string.Equals(claim.Type, ClaimTypes.NameIdentifier, StringComparison.Ordinal) ||
                        string.Equals(claim.Type, JwtRegisteredClaimNames.Sub, StringComparison.Ordinal))
                    {
                        return(true);
                    }

                    // Claims whose destination is not explicitly referenced or
                    // doesn't contain "token" are not included in the access token.
                    return(claim.HasDestination(OpenIdConnectConstants.ResponseTypes.Token));
                });

                var identity = (ClaimsIdentity)principal.Identity;

                // Create a new ticket containing the updated properties and the filtered principal.
                var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

                var notification = new SerializeAccessTokenContext(Context, Options, request, response, ticket)
                {
                    Client               = request.ClientId,
                    Confidential         = properties.IsConfidential(),
                    DataFormat           = Options.AccessTokenFormat,
                    Issuer               = Context.GetIssuer(Options),
                    SecurityTokenHandler = Options.AccessTokenHandler,
                    SigningCredentials   = Options.SigningCredentials.FirstOrDefault()
                };

                foreach (var audience in properties.GetResources())
                {
                    notification.Audiences.Add(audience);
                }

                foreach (var scope in properties.GetScopes())
                {
                    notification.Scopes.Add(scope);
                }

                // Sets the default access token serializer.
                notification.Serializer = payload => {
                    if (notification.SecurityTokenHandler == null)
                    {
                        return(Task.FromResult(notification.DataFormat?.Protect(payload)));
                    }

                    // Extract the main identity from the principal.
                    identity = (ClaimsIdentity)payload.Principal.Identity;

                    // Store the "usage" property as a claim.
                    identity.AddClaim(OpenIdConnectConstants.Extra.Usage, payload.Properties.GetUsage());

                    // If the ticket is marked as confidential, add a new
                    // "confidential" claim in the security token.
                    if (notification.Confidential)
                    {
                        identity.AddClaim(new Claim(OpenIdConnectConstants.Extra.Confidential, "true", ClaimValueTypes.Boolean));
                    }

                    // Create a new claim per scope item, that will result
                    // in a "scope" array being added in the access token.
                    foreach (var scope in notification.Scopes)
                    {
                        identity.AddClaim(OpenIdConnectConstants.Claims.Scope, scope);
                    }

                    // Note: when used as an access token, a JWT token doesn't have to expose a "sub" claim
                    // but the name identifier claim is used as a substitute when it has been explicitly added.
                    // See https://tools.ietf.org/html/rfc7519#section-4.1.2
                    var subject = identity.FindFirst(JwtRegisteredClaimNames.Sub);
                    if (subject == null)
                    {
                        var identifier = identity.FindFirst(ClaimTypes.NameIdentifier);
                        if (identifier != null)
                        {
                            identity.AddClaim(JwtRegisteredClaimNames.Sub, identifier.Value);
                        }
                    }

                    // Remove the ClaimTypes.NameIdentifier claims to avoid getting duplicate claims.
                    // Note: the "sub" claim is automatically mapped by JwtSecurityTokenHandler
                    // to ClaimTypes.NameIdentifier when validating a JWT token.
                    // Note: make sure to call ToArray() to avoid an InvalidOperationException
                    // on old versions of Mono, where FindAll() is implemented using an iterator.
                    foreach (var claim in identity.FindAll(ClaimTypes.NameIdentifier).ToArray())
                    {
                        identity.RemoveClaim(claim);
                    }

                    // Store the audiences as claims.
                    foreach (var audience in notification.Audiences)
                    {
                        identity.AddClaim(JwtRegisteredClaimNames.Aud, audience);
                    }

                    // List the client application as an authorized party.
                    if (!string.IsNullOrEmpty(notification.Client))
                    {
                        identity.AddClaim(JwtRegisteredClaimNames.Azp, notification.Client);
                    }

                    var handler = notification.SecurityTokenHandler as JwtSecurityTokenHandler;
                    if (handler != null)
                    {
                        var token = handler.CreateToken(
                            subject: identity,
                            issuer: notification.Issuer,
                            signingCredentials: notification.SigningCredentials,
                            issuedAt: payload.Properties.IssuedUtc.Value.UtcDateTime,
                            notBefore: payload.Properties.IssuedUtc.Value.UtcDateTime,
                            expires: payload.Properties.ExpiresUtc.Value.UtcDateTime);

                        if (notification.SigningCredentials != null)
                        {
                            var x509SecurityKey = notification.SigningCredentials.Key as X509SecurityKey;
                            if (x509SecurityKey != null)
                            {
                                // Note: unlike "kid", "x5t" is not automatically added by JwtHeader's constructor in IdentityModel for ASP.NET 5.
                                // Though not required by the specifications, this property is needed for IdentityModel for Katana to work correctly.
                                // See https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/132
                                // and https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/181.
                                token.Header[JwtHeaderParameterNames.X5t] = Base64UrlEncoder.Encode(x509SecurityKey.Certificate.GetCertHash());
                            }

                            object identifier;
                            if (!token.Header.TryGetValue(JwtHeaderParameterNames.Kid, out identifier) || identifier == null)
                            {
                                // When the token doesn't contain a "kid" parameter in the header, automatically
                                // add one using the identifier specified in the signing credentials.
                                identifier = notification.SigningCredentials.Kid;

                                if (identifier == null)
                                {
                                    // When no key identifier has been explicitly added by the developer, a "kid" is automatically
                                    // inferred from the hexadecimal representation of the certificate thumbprint (SHA-1).
                                    if (x509SecurityKey != null)
                                    {
                                        identifier = x509SecurityKey.Certificate.Thumbprint;
                                    }

                                    // When no key identifier has been explicitly added by the developer, a "kid"
                                    // is automatically inferred from the modulus if the signing key is a RSA key.
                                    var rsaSecurityKey = notification.SigningCredentials.Key as RsaSecurityKey;
                                    if (rsaSecurityKey != null)
                                    {
                                        // Only use the 40 first chars to match the identifier used by the JWKS endpoint.
                                        identifier = Base64UrlEncoder.Encode(rsaSecurityKey.Parameters.Modulus)
                                                     .Substring(0, 40)
                                                     .ToUpperInvariant();
                                    }
                                }

                                token.Header[JwtHeaderParameterNames.Kid] = identifier;
                            }
                        }

                        return(Task.FromResult(handler.WriteToken(token)));
                    }

                    else
                    {
                        var token = notification.SecurityTokenHandler.CreateToken(new SecurityTokenDescriptor {
                            Claims             = payload.Principal.Claims,
                            Issuer             = notification.Issuer,
                            Audience           = notification.Audiences.ElementAtOrDefault(0),
                            SigningCredentials = notification.SigningCredentials,
                            IssuedAt           = notification.AuthenticationTicket.Properties.IssuedUtc.Value.UtcDateTime,
                            NotBefore          = notification.AuthenticationTicket.Properties.IssuedUtc.Value.UtcDateTime,
                            Expires            = notification.AuthenticationTicket.Properties.ExpiresUtc.Value.UtcDateTime
                        });

                        // Note: the security token is manually serialized to prevent
                        // an exception from being thrown if the handler doesn't implement
                        // the SecurityTokenHandler.WriteToken overload returning a string.
                        var builder = new StringBuilder();
                        using (var writer = XmlWriter.Create(builder, new XmlWriterSettings {
                            Encoding = new UTF8Encoding(false), OmitXmlDeclaration = true
                        })) {
                            notification.SecurityTokenHandler.WriteToken(writer, token);
                        }

                        return(Task.FromResult(builder.ToString()));
                    }
                };

                await Options.Provider.SerializeAccessToken(notification);

                // Treat a non-null access token like an implicit HandleResponse call.
                if (notification.HandledResponse || !string.IsNullOrEmpty(notification.AccessToken))
                {
                    return(notification.AccessToken);
                }

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

                // Allow the application to change the authentication
                // ticket from the SerializeAccessTokenAsync event.
                ticket = notification.AuthenticationTicket;
                ticket.Properties.CopyTo(properties);

                return(await notification.SerializeTicketAsync());
            }

            catch (Exception exception) {
                Logger.LogWarning("An exception occured when serializing an access token.", exception);

                return(null);
            }
        }
        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,
                SignatureProvider    = Options.SignatureProvider,
                SigningCredentials   = Options.SigningCredentials.FirstOrDefault()
            };

            await Options.Provider.DeserializeAccessToken(notification);

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

            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 InvokeValidationEndpointAsync or InvokeTokenEndpointAsync.
            var parameters = new TokenValidationParameters {
                IssuerSigningKey = notification.SigningCredentials.Key,
                ValidIssuer      = notification.Issuer,
                ValidateAudience = false,
                ValidateLifetime = false
            };

            SecurityToken   securityToken;
            ClaimsPrincipal principal;

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

            catch (Exception exception) {
                Logger.LogVerbose("An exception occured when deserializing an identity 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 audiences = principal.FindAll(JwtRegisteredClaimNames.Aud);

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

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

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

            if (principal.Claims.Any(claim => claim.Type == OpenIdConnectConstants.Extra.Confidential))
            {
                properties.Items[OpenIdConnectConstants.Extra.Confidential] = "true";
            }

            // Ensure the received ticket is an access token.
            var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

            if (!ticket.IsAccessToken())
            {
                Logger.LogVerbose("The received token was not an access token: {Token}.", token);

                return(null);
            }

            return(ticket);
        }