/// <summary>
        /// Determines whether the authentication ticket contains at least one scope.
        /// </summary>
        /// <param name="ticket">The authentication ticket.</param>
        /// <returns><c>true</c> if the ticket contains at least one scope.</returns>
        public static bool HasScope([NotNull] this AuthenticationTicket ticket)
        {
            if (ticket == null)
            {
                throw new ArgumentNullException(nameof(ticket));
            }

            return(ticket.HasProperty(OpenIdConnectConstants.Properties.Scopes));
        }
Пример #2
0
        public void HasProperty_ReturnsExpectedResult(string value, bool result)
        {
            // Arrange
            var ticket = new AuthenticationTicket(
                new ClaimsIdentity(),
                new AuthenticationProperties());

            ticket.Properties.Dictionary["property"] = value;

            // Act and assert
            Assert.Equal(result, ticket.HasProperty("property"));
            Assert.Equal(result, ticket.Properties.HasProperty("property"));
        }
        private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket)
        {
            // Extract the OpenID Connect request from the ASP.NET context.
            // If it cannot be found or doesn't correspond to an authorization
            // or a token request, throw an InvalidOperationException.
            var request = Context.GetOpenIdConnectRequest();

            if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest()))
            {
                throw new InvalidOperationException("An OpenID Connect response cannot be returned from this endpoint.");
            }

            // Note: if an OpenID Connect response was already generated, throw an exception.
            var response = Context.GetOpenIdConnectResponse();

            if (response != null)
            {
                throw new InvalidOperationException("An OpenID Connect response has already been sent.");
            }

            if (!ticket.Principal.HasClaim(claim => claim.Type == ClaimTypes.NameIdentifier))
            {
                throw new InvalidOperationException("The authentication ticket was rejected because it didn't " +
                                                    "contain the mandatory ClaimTypes.NameIdentifier claim.");
            }

            // Prepare a new OpenID Connect response.
            response = new OpenIdConnectResponse();

            if (request.IsAuthorizationRequest())
            {
                response.RedirectUri = request.RedirectUri;
                response.State       = request.State;

                // Keep the code_challenge, code_challenge_method, nonce and redirect_uri parameters for later comparison.
                ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallenge, request.CodeChallenge);
                ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod, request.CodeChallengeMethod);
                ticket.SetProperty(OpenIdConnectConstants.Properties.Nonce, request.Nonce);
                ticket.SetProperty(OpenIdConnectConstants.Properties.RedirectUri, request.RedirectUri);
            }

            // Copy the confidentiality level associated with the request to the authentication ticket.
            if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel))
            {
                ticket.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel,
                                   request.GetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel));
            }

            // Always include the "openid" scope when the developer doesn't explicitly call SetScopes.
            // Note: the application is allowed to specify a different "scopes": in this case,
            // don't replace the "scopes" property stored in the authentication ticket.
            if (!ticket.HasProperty(OpenIdConnectConstants.Properties.Scopes) && request.HasScope(OpenIdConnectConstants.Scopes.OpenId))
            {
                ticket.SetProperty(OpenIdConnectConstants.Properties.Scopes, OpenIdConnectConstants.Scopes.OpenId);
            }

            // When a "resources" property cannot be found in the ticket, infer it from the "audiences" property.
            if (!ticket.HasProperty(OpenIdConnectConstants.Properties.Resources))
            {
                ticket.SetProperty(OpenIdConnectConstants.Properties.Resources,
                                   ticket.GetProperty(OpenIdConnectConstants.Properties.Audiences));
            }

            // Only return an authorization code if the request is an authorization request and has response_type=code.
            if (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code))
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                // properties.IssuedUtc and properties.ExpiresUtc are always
                // explicitly set to null to avoid aligning the expiration date
                // of the authorization code with the lifetime of the other tokens.
                properties.IssuedUtc = properties.ExpiresUtc = null;

                response.Code = await SerializeAuthorizationCodeAsync(ticket.Principal, properties, request, response);
            }

            // Only return an access token if the request is a token request
            // or an authorization request that specifies response_type=token.
            if (request.IsTokenRequest() || (request.IsAuthorizationRequest() &&
                                             request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)))
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                // When receiving a grant_type=refresh_token request, determine whether the client application
                // requests a limited set of scopes/resources and replace the corresponding properties if necessary.
                // Note: at this stage, request.GetResources() cannot return more items than the ones that were initially granted
                // by the resource owner as the "resources" parameter is always validated when receiving the token request.
                if (request.IsTokenRequest() && request.IsRefreshTokenGrantType())
                {
                    if (!string.IsNullOrEmpty(request.Resource))
                    {
                        // Replace the resources initially granted by the resources listed by the client application in the token request.
                        // Note: request.GetResources() automatically removes duplicate entries, so additional filtering is not necessary.
                        properties.SetProperty(OpenIdConnectConstants.Properties.Resources, string.Join(" ", request.GetResources()));
                    }

                    if (!string.IsNullOrEmpty(request.Scope))
                    {
                        // Replace the scopes initially granted by the scopes listed by the client application in the token request.
                        // Note: request.GetScopes() automatically removes duplicate entries, so additional filtering is not necessary.
                        properties.SetProperty(OpenIdConnectConstants.Properties.Scopes, string.Join(" ", request.GetScopes()));
                    }
                }

                var resources = properties.GetProperty(OpenIdConnectConstants.Properties.Resources);
                if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(resources) &&
                                                               !string.IsNullOrEmpty(request.Resource) &&
                                                               !string.Equals(request.Resource, resources, StringComparison.Ordinal)))
                {
                    response.Resource = resources;
                }

                var scopes = properties.GetProperty(OpenIdConnectConstants.Properties.Scopes);
                if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(scopes) &&
                                                               !string.IsNullOrEmpty(request.Scope) &&
                                                               !string.Equals(request.Scope, scopes, StringComparison.Ordinal)))
                {
                    response.Scope = scopes;
                }

                response.TokenType   = OpenIdConnectConstants.TokenTypes.Bearer;
                response.AccessToken = await SerializeAccessTokenAsync(ticket.Principal, properties, request, response);

                // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user
                // is free to set a null value directly in the SerializeAccessToken event.
                if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow)
                {
                    var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow;

                    response.ExpiresIn = (long)(lifetime.TotalSeconds + .5);
                }
            }

            // Only return a refresh token if the request is a token request that specifies scope=offline_access.
            if (request.IsTokenRequest() && ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess))
            {
                // Note: when sliding expiration is enabled, don't return a new refresh token,
                // unless the token request is not a grant_type=refresh_token request.
                if (!request.IsRefreshTokenGrantType() || Options.UseSlidingExpiration)
                {
                    // Make sure to create a copy of the authentication properties
                    // to avoid modifying the properties set on the original ticket.
                    var properties = ticket.Properties.Copy();

                    response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Principal, properties, request, response);
                }
            }

            // Only return an identity token if the openid scope was requested and granted
            // to avoid generating and returning an unnecessary token to pure OAuth2 clients.
            if (ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId))
            {
                // Note: don't return an identity token if the request is an
                // authorization request that doesn't use response_type=id_token.
                if (request.IsTokenRequest() || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken))
                {
                    // Make sure to create a copy of the authentication properties
                    // to avoid modifying the properties set on the original ticket.
                    var properties = ticket.Properties.Copy();

                    // properties.IssuedUtc and properties.ExpiresUtc are always
                    // explicitly set to null to avoid aligning the expiration date
                    // of the identity token with the lifetime of the other tokens.
                    properties.IssuedUtc = properties.ExpiresUtc = null;

                    response.IdToken = await SerializeIdentityTokenAsync(ticket.Principal, properties, request, response);
                }
            }

            if (request.IsAuthorizationRequest())
            {
                return(await SendAuthorizationResponseAsync(response, ticket));
            }

            return(await SendTokenResponseAsync(response, ticket));
        }
        private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket)
        {
            // Extract the OpenID Connect request from the OWIN context.
            // If it cannot be found or doesn't correspond to an authorization
            // or a token request, throw an InvalidOperationException.
            var request = Context.GetOpenIdConnectRequest();

            if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest()))
            {
                throw new InvalidOperationException("An authorization or token response cannot be returned from this endpoint.");
            }

            // Note: if a response was already generated, throw an exception.
            var response = Context.GetOpenIdConnectResponse();

            if (response != null)
            {
                throw new InvalidOperationException("A response has already been sent.");
            }

            if (string.IsNullOrEmpty(ticket.Identity.GetClaim(OpenIdConnectConstants.Claims.Subject)))
            {
                throw new InvalidOperationException("The authentication ticket was rejected because " +
                                                    "the mandatory subject claim was missing.");
            }

            Logger.LogTrace("A sign-in operation was triggered: {Claims} ; {Properties}.",
                            ticket.Identity.Claims, ticket.Properties.Dictionary);

            // Prepare a new OpenID Connect response.
            response = new OpenIdConnectResponse();

            // Copy the confidentiality level associated with the request to the authentication ticket.
            if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel))
            {
                ticket.SetConfidentialityLevel(request.GetProperty <string>(OpenIdConnectConstants.Properties.ConfidentialityLevel));
            }

            // Always include the "openid" scope when the developer doesn't explicitly call SetScopes.
            // Note: the application is allowed to specify a different "scopes": in this case,
            // don't replace the "scopes" property stored in the authentication ticket.
            if (request.HasScope(OpenIdConnectConstants.Scopes.OpenId) && !ticket.HasScope())
            {
                ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId);
            }

            // When a "resources" property cannot be found in the ticket,
            // infer it from the "audiences" property.
            if (ticket.HasAudience() && !ticket.HasResource())
            {
                ticket.SetResources(ticket.GetAudiences());
            }

            // Add the validated client_id to the list of authorized presenters,
            // unless the presenters were explicitly set by the developer.
            var presenter = request.GetProperty <string>(OpenIdConnectConstants.Properties.ValidatedClientId);

            if (!string.IsNullOrEmpty(presenter) && !ticket.HasPresenter())
            {
                ticket.SetPresenters(presenter);
            }

            var notification = new ProcessSigninResponseContext(Context, Options, ticket, request, response);

            if (request.IsAuthorizationRequest())
            {
                // By default, return an authorization code if a response type containing code was specified.
                notification.IncludeAuthorizationCode = request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code);

                // By default, return an access token if a response type containing token was specified.
                notification.IncludeAccessToken = request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token);

                // By default, prevent a refresh token from being returned as the OAuth2 specification
                // explicitly disallows returning a refresh token from the authorization endpoint.
                // See https://tools.ietf.org/html/rfc6749#section-4.2.2 for more information.
                notification.IncludeRefreshToken = false;

                // By default, return an identity token if a response type containing code
                // was specified and if the openid scope was explicitly or implicitly granted.
                notification.IncludeIdentityToken =
                    request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken) &&
                    ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId);
            }

            else
            {
                // By default, prevent an authorization code from being returned as this type of token
                // cannot be issued from the token endpoint in the standard OAuth2/OpenID Connect flows.
                notification.IncludeAuthorizationCode = false;

                // By default, always return an access token.
                notification.IncludeAccessToken = true;

                // By default, only return a refresh token is the offline_access scope was granted and if
                // sliding expiration is disabled or if the request is not a grant_type=refresh_token request.
                notification.IncludeRefreshToken =
                    ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
                    (Options.UseSlidingExpiration || !request.IsRefreshTokenGrantType());

                // By default, only return an identity token if the openid scope was granted.
                notification.IncludeIdentityToken = ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId);
            }

            await Options.Provider.ProcessSigninResponse(notification);

            if (notification.HandledResponse)
            {
                Logger.LogDebug("The sign-in response was handled in user code.");

                return(true);
            }

            else if (notification.Skipped)
            {
                Logger.LogDebug("The default sign-in handling was skipped from user code.");

                return(false);
            }

            else if (notification.IsRejected)
            {
                Logger.LogError("The request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ notification.ErrorDescription);

                if (request.IsAuthorizationRequest())
                {
                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = notification.ErrorDescription,
                        ErrorUri = notification.ErrorUri
                    }));
                }

                return(await SendTokenResponseAsync(new OpenIdConnectResponse
                {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            // Flow the changes made to the ticket.
            ticket = notification.Ticket;

            // Ensure an authentication ticket has been provided or return
            // an error code indicating that the request was rejected.
            if (ticket == null)
            {
                Logger.LogError("The request was rejected because no authentication ticket was provided.");

                if (request.IsAuthorizationRequest())
                {
                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.AccessDenied,
                        ErrorDescription = "The authorization was denied by the resource owner."
                    }));
                }

                return(await SendTokenResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The token request was rejected by the authorization server."
                }));
            }

            if (notification.IncludeAuthorizationCode)
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                response.Code = await SerializeAuthorizationCodeAsync(ticket.Identity, properties, request, response);
            }

            if (notification.IncludeAccessToken)
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                // When receiving a grant_type=refresh_token request, determine whether the client application
                // requests a limited set of scopes and replace the corresponding properties if necessary.
                if (!string.IsNullOrEmpty(request.Scope) && request.IsTokenRequest() && request.IsRefreshTokenGrantType())
                {
                    Logger.LogDebug("The access token scopes will be limited to the scopes requested " +
                                    "by the client application: {Scopes}.", request.GetScopes());

                    // Replace the scopes initially granted by the scopes listed by the client
                    // application in the token request. Note: request.GetScopes() automatically
                    // removes duplicate entries, so additional filtering is not necessary.
                    properties.SetProperty(OpenIdConnectConstants.Properties.Scopes,
                                           new JArray(request.GetScopes()).ToString(Formatting.None));
                }

                var scopes = ticket.GetScopes();
                if ((request.IsTokenRequest() && request.IsAuthorizationCodeGrantType()) ||
                    !new HashSet <string>(scopes).SetEquals(request.GetScopes()))
                {
                    response.Scope = string.Join(" ", scopes);
                }

                response.TokenType   = OpenIdConnectConstants.TokenTypes.Bearer;
                response.AccessToken = await SerializeAccessTokenAsync(ticket.Identity, properties, request, response);

                // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user
                // is free to set a null value directly in the SerializeAccessToken event.
                if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow)
                {
                    var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow;

                    response.ExpiresIn = (long)(lifetime.TotalSeconds + .5);
                }
            }

            if (notification.IncludeRefreshToken)
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Identity, properties, request, response);
            }

            if (notification.IncludeIdentityToken)
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                response.IdToken = await SerializeIdentityTokenAsync(ticket.Identity, properties, request, response);
            }

            if (request.IsAuthorizationRequest())
            {
                return(await SendAuthorizationResponseAsync(response, ticket));
            }

            return(await SendTokenResponseAsync(response, ticket));
        }
        private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket)
        {
            // Extract the OpenID Connect request from the ASP.NET Core context.
            // If it cannot be found or doesn't correspond to an authorization
            // or a token request, throw an InvalidOperationException.
            var request = Context.GetOpenIdConnectRequest();

            if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest()))
            {
                throw new InvalidOperationException("An authorization or token response cannot be returned from this endpoint.");
            }

            // Note: if a response was already generated, throw an exception.
            var response = Context.GetOpenIdConnectResponse();

            if (response != null || Response.HasStarted)
            {
                throw new InvalidOperationException("A response has already been sent.");
            }

            if (string.IsNullOrEmpty(ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject)))
            {
                throw new InvalidOperationException("The authentication ticket was rejected because " +
                                                    "the mandatory subject claim was missing.");
            }

            Logger.LogTrace("A sign-in operation was triggered: {Claims} ; {Properties}.",
                            ticket.Principal.Claims, ticket.Properties.Items);

            // Prepare a new OpenID Connect response.
            response = new OpenIdConnectResponse();

            // Copy the confidentiality level associated with the request to the authentication ticket.
            if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel))
            {
                ticket.SetConfidentialityLevel(request.GetProperty <string>(OpenIdConnectConstants.Properties.ConfidentialityLevel));
            }

            // Always include the "openid" scope when the developer doesn't explicitly call SetScopes.
            // Note: the application is allowed to specify a different "scopes": in this case,
            // don't replace the "scopes" property stored in the authentication ticket.
            if (request.HasScope(OpenIdConnectConstants.Scopes.OpenId) && !ticket.HasScope())
            {
                ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId);
            }

            // When a "resources" property cannot be found in the ticket,
            // infer it from the "audiences" property.
            if (ticket.HasAudience() && !ticket.HasResource())
            {
                ticket.SetResources(ticket.GetAudiences());
            }

            // Add the validated client_id to the list of authorized presenters,
            // unless the presenters were explicitly set by the developer.
            var presenter = request.GetProperty <string>(OpenIdConnectConstants.Properties.ValidatedClientId);

            if (!string.IsNullOrEmpty(presenter) && !ticket.HasPresenter())
            {
                ticket.SetPresenters(presenter);
            }

            // Only return an authorization code if the request is an authorization request and has response_type=code.
            if (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code))
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                response.Code = await SerializeAuthorizationCodeAsync(ticket.Principal, properties, request, response);
            }

            // Only return an access token if the request is a token request
            // or an authorization request that specifies response_type=token.
            if (request.IsTokenRequest() || (request.IsAuthorizationRequest() &&
                                             request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)))
            {
                // Make sure to create a copy of the authentication properties
                // to avoid modifying the properties set on the original ticket.
                var properties = ticket.Properties.Copy();

                // When receiving a grant_type=refresh_token request, determine whether the client application
                // requests a limited set of scopes/resources and replace the corresponding properties if necessary.
                // Note: at this stage, request.GetResources() cannot return more items than the ones that were initially granted
                // by the resource owner as the "resources" parameter is always validated when receiving the token request.
                if (request.IsTokenRequest() && request.IsRefreshTokenGrantType())
                {
                    if (!string.IsNullOrEmpty(request.Resource))
                    {
                        Logger.LogDebug("The access token resources will be limited to the resources requested " +
                                        "by the client application: {Resources}.", request.GetResources());

                        // Replace the resources initially granted by the resources listed by the client
                        // application in the token request. Note: request.GetResources() automatically
                        // removes duplicate entries, so additional filtering is not necessary.
                        properties.SetProperty(OpenIdConnectConstants.Properties.Resources,
                                               new JArray(request.GetResources()).ToString(Formatting.None));
                    }

                    if (!string.IsNullOrEmpty(request.Scope))
                    {
                        Logger.LogDebug("The access token scopes will be limited to the scopes requested " +
                                        "by the client application: {Scopes}.", request.GetScopes());

                        // Replace the scopes initially granted by the scopes listed by the client
                        // application in the token request. Note: request.GetScopes() automatically
                        // removes duplicate entries, so additional filtering is not necessary.
                        properties.SetProperty(OpenIdConnectConstants.Properties.Scopes,
                                               new JArray(request.GetScopes()).ToString(Formatting.None));
                    }
                }

                var resources = ticket.GetResources();
                if (request.IsAuthorizationCodeGrantType() || !new HashSet <string>(resources).SetEquals(request.GetResources()))
                {
                    response.Resource = string.Join(" ", resources);
                }

                var scopes = ticket.GetScopes();
                if (request.IsAuthorizationCodeGrantType() || !new HashSet <string>(scopes).SetEquals(request.GetScopes()))
                {
                    response.Scope = string.Join(" ", scopes);
                }

                response.TokenType   = OpenIdConnectConstants.TokenTypes.Bearer;
                response.AccessToken = await SerializeAccessTokenAsync(ticket.Principal, properties, request, response);

                // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user
                // is free to set a null value directly in the SerializeAccessToken event.
                if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow)
                {
                    var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow;

                    response.ExpiresIn = (long)(lifetime.TotalSeconds + .5);
                }
            }

            // Only return a refresh token if the request is a token request that specifies scope=offline_access.
            if (request.IsTokenRequest() && ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess))
            {
                // Note: when sliding expiration is disabled, don't return a new refresh token,
                // unless the token request is not a grant_type=refresh_token request.
                if (Options.UseSlidingExpiration || !request.IsRefreshTokenGrantType())
                {
                    // Make sure to create a copy of the authentication properties
                    // to avoid modifying the properties set on the original ticket.
                    var properties = ticket.Properties.Copy();

                    response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Principal, properties, request, response);
                }
            }

            // Only return an identity token if the openid scope was requested and granted
            // to avoid generating and returning an unnecessary token to pure OAuth2 clients.
            if (ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId))
            {
                // Note: don't return an identity token if the request is an
                // authorization request that doesn't use response_type=id_token.
                if (request.IsTokenRequest() || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken))
                {
                    // Make sure to create a copy of the authentication properties
                    // to avoid modifying the properties set on the original ticket.
                    var properties = ticket.Properties.Copy();

                    response.IdToken = await SerializeIdentityTokenAsync(ticket.Principal, properties, request, response);
                }
            }

            if (request.IsAuthorizationRequest())
            {
                return(await SendAuthorizationResponseAsync(response, ticket));
            }

            return(await SendTokenResponseAsync(response, ticket));
        }