/// <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);
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); }
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); }