/// <summary>
 /// Represents an event called for each request to the logout endpoint to give the user code
 /// a chance to manually extract the logout request from the ambient HTTP context.
 /// </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 ExtractLogoutRequest(ExtractLogoutRequestContext context)
 => OnExtractLogoutRequest(context);
Example #2
0
        private async Task <bool> InvokeLogoutEndpointAsync()
        {
            OpenIdConnectRequest request;

            // Note: logout requests must be made via GET but POST requests
            // are also accepted to allow flowing large logout payloads.
            // See https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
            if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
            {
                request = new OpenIdConnectRequest(Request.Query);
            }

            else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
            {
                // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    Logger.LogError("The logout request was rejected because " +
                                    "the mandatory 'Content-Type' header was missing.");

                    return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The mandatory 'Content-Type' header must be specified."
                    }));
                }

                // May have media/type; charset=utf-8, allow partial match.
                if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    Logger.LogError("The logout request was rejected because an invalid 'Content-Type' " +
                                    "header was specified: {ContentType}.", Request.ContentType);

                    return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The specified 'Content-Type' header is not valid."
                    }));
                }

                request = new OpenIdConnectRequest(await Request.ReadFormAsync());
            }

            else
            {
                Logger.LogError("The logout request was rejected because an invalid " +
                                "HTTP method was specified: {Method}.", Request.Method);

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified HTTP method is not valid."
                }));
            }

            // Note: set the message type before invoking the ExtractLogoutRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.LogoutRequest);

            // Store the logout request in the OWIN context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractLogoutRequestContext(Context, Options, request);
            await Options.Provider.ExtractLogoutRequest(@event);

            if (@event.HandledResponse)
            {
                Logger.LogDebug("The logout request was handled in user code.");

                return(true);
            }

            else if (@event.Skipped)
            {
                Logger.LogDebug("The default logout request handling was skipped from user code.");

                return(false);
            }

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

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            Logger.LogInformation("The logout request was successfully extracted " +
                                  "from the HTTP request: {Request}.", request);

            var context = new ValidateLogoutRequestContext(Context, Options, request);
            await Options.Provider.ValidateLogoutRequest(context);

            if (context.HandledResponse)
            {
                Logger.LogDebug("The logout request was handled in user code.");

                return(true);
            }

            else if (context.Skipped)
            {
                Logger.LogDebug("The default logout request handling was skipped from user code.");

                return(false);
            }

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

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

            // Store the validated post_logout_redirect_uri as a request property.
            request.SetProperty(OpenIdConnectConstants.Properties.PostLogoutRedirectUri, context.PostLogoutRedirectUri);

            Logger.LogInformation("The logout request was successfully validated.");

            var notification = new HandleLogoutRequestContext(Context, Options, request);
            await Options.Provider.HandleLogoutRequest(notification);

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

                return(true);
            }

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

                return(false);
            }

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

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

            return(false);
        }
Example #3
0
        private async Task <bool> InvokeLogoutEndpointAsync()
        {
            OpenIdConnectMessage request;

            // Note: logout requests must be made via GET but POST requests
            // are also accepted to allow flowing large logout payloads.
            // See https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
            if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
            {
                request = new OpenIdConnectMessage(Request.Query)
                {
                    RequestType = OpenIdConnectRequestType.LogoutRequest
                };
            }

            else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
            {
                // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    Options.Logger.LogError("The logout request was rejected because " +
                                            "the mandatory 'Content-Type' header was missing.");

                    return(await SendLogoutResponseAsync(null, new OpenIdConnectMessage {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "A malformed logout request has been received: " +
                                           "the mandatory 'Content-Type' header was missing from the POST request."
                    }));
                }

                // May have media/type; charset=utf-8, allow partial match.
                if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    Options.Logger.LogError("The logout request was rejected because an invalid 'Content-Type' " +
                                            "header was received: {ContentType}.", Request.ContentType);

                    return(await SendLogoutResponseAsync(null, new OpenIdConnectMessage {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "A malformed logout request has been received: " +
                                           "the 'Content-Type' header contained an unexcepted value. " +
                                           "Make sure to use 'application/x-www-form-urlencoded'."
                    }));
                }

                request = new OpenIdConnectMessage(await Request.ReadFormAsync())
                {
                    RequestType = OpenIdConnectRequestType.LogoutRequest
                };
            }

            else
            {
                Options.Logger.LogError("The logout request was rejected because an invalid " +
                                        "HTTP method was received: {Method}.", Request.Method);

                return(await SendLogoutResponseAsync(null, new OpenIdConnectMessage {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "A malformed logout request has been received: " +
                                       "make sure to use either GET or POST."
                }));
            }

            var @event = new ExtractLogoutRequestContext(Context, Options, request);
            await Options.Provider.ExtractLogoutRequest(@event);

            // Allow the application code to replace the logout request.
            request = @event.Request;

            if (@event.HandledResponse)
            {
                return(true);
            }

            else if (@event.Skipped)
            {
                return(false);
            }

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

                return(await SendLogoutResponseAsync(null, new OpenIdConnectMessage {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            // Store the logout request in the OWIN context.
            Context.SetOpenIdConnectRequest(request);

            var context = new ValidateLogoutRequestContext(Context, Options, request);
            await Options.Provider.ValidateLogoutRequest(context);

            if (context.HandledResponse)
            {
                return(true);
            }

            else if (context.Skipped)
            {
                return(false);
            }

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

                return(await SendLogoutResponseAsync(request, new OpenIdConnectMessage {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            var notification = new HandleLogoutRequestContext(Context, Options, request);
            await Options.Provider.HandleLogoutRequest(notification);

            if (notification.HandledResponse)
            {
                return(true);
            }

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

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

                return(await SendLogoutResponseAsync(request, new OpenIdConnectMessage {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            return(false);
        }