private async Task <bool> HandleChallengeAsync(AuthenticationProperties properties)
        {
            // Extract the OpenID Connect request from the OWIN/Katana 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.");
            }

            // Prepare a new OpenID Connect response.
            response = new OpenIdConnectResponse
            {
                Error            = properties.GetProperty(OpenIdConnectConstants.Properties.Error),
                ErrorDescription = properties.GetProperty(OpenIdConnectConstants.Properties.ErrorDescription),
                ErrorUri         = properties.GetProperty(OpenIdConnectConstants.Properties.ErrorUri)
            };

            // Remove the error/error_description/error_uri properties from the ticket.
            properties.RemoveProperty(OpenIdConnectConstants.Properties.Error)
            .RemoveProperty(OpenIdConnectConstants.Properties.ErrorDescription)
            .RemoveProperty(OpenIdConnectConstants.Properties.ErrorUri);

            if (string.IsNullOrEmpty(response.Error))
            {
                response.Error = request.IsAuthorizationRequest() ?
                                 OpenIdConnectConstants.Errors.AccessDenied :
                                 OpenIdConnectConstants.Errors.InvalidGrant;
            }

            if (string.IsNullOrEmpty(response.ErrorDescription))
            {
                response.ErrorDescription = request.IsAuthorizationRequest() ?
                                            "The authorization was denied by the resource owner." :
                                            "The token request was rejected by the authorization server.";
            }

            Logger.LogTrace("A challenge operation was triggered: {Properties}.", properties.Dictionary);

            var notification = new ProcessChallengeResponseContext(Context, Options, properties, request, response);
            await Options.Provider.ProcessChallengeResponse(notification);

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

                return(true);
            }

            else if (notification.Skipped)
            {
                Logger.LogDebug("The default challenge 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 properties.
            properties = notification.Properties;

            // Create a new ticket containing an empty identity and
            // the authentication properties extracted from the context.
            var ticket = new AuthenticationTicket(new ClaimsIdentity(), properties);

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

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