private static CommandResult HandleResponse(UnbindResult unbindResult, StoredRequestState storedRequestState, IOptions options, Uri returnUrl) { var logoutResponse = Saml2LogoutResponse.FromXml(unbindResult.Data); var notificationHandledTheStatus = options.Notifications.ProcessSingleLogoutResponseStatus(logoutResponse, storedRequestState); if (!notificationHandledTheStatus) { var status = logoutResponse.Status; if (status != Saml2StatusCode.Success) { throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture, "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.", status)); } } var commandResult = new CommandResult { HttpStatusCode = HttpStatusCode.SeeOther }; if (!options.SPOptions.Compatibility.DisableLogoutStateCookie) { commandResult.ClearCookieName = StoredRequestState.CookieNameBase + unbindResult.RelayState; } commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl; options.SPOptions.Logger.WriteInformation("Received logout response " + logoutResponse.Id + ", redirecting to " + commandResult.Location); return(commandResult); }
private static void VerifyMessageIsSigned(UnbindResult unbindResult, IOptions options) { if (unbindResult.TrustLevel < TrustLevel.Signature) { var issuer = unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name]?.InnerText; if (issuer == null) { throw new InvalidSignatureException( "There is no Issuer element in the message, so there is no way to know what certificate to use to validate the signature."); } var idp = options.IdentityProviders[new EntityId(issuer)]; if (!unbindResult.Data.IsSignedByAny( idp.SigningKeys, options.SPOptions.ValidateCertificates, options.SPOptions.MinIncomingSigningAlgorithm)) { throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture, "Received a {0} from {1} that cannot be processed because it is not signed.", unbindResult.Data.LocalName, unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name].InnerText)); } } }
private static CommandResult HandleRequest(UnbindResult unbindResult, HttpRequestData httpRequest, IOptions options) { var request = Saml2LogoutRequest.FromXml(unbindResult.Data); var idp = options.IdentityProviders[request.Issuer]; if (options.SPOptions.SigningServiceCertificate == null) { throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture, "Received a LogoutRequest from \"{0}\" but cannot reply because single logout responses " + "must be signed and there is no signing certificate configured. Looks like the idp is " + "configured for Single Logout despite Saml2 not exposing that functionality in the metadata.", request.Issuer.Id)); } if (idp.SingleLogoutServiceResponseUrl == null) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Received a LogoutRequest from \"{0}\" but cannot reply because on logout endpoint is " + "configured on the idp. Set a SingleLogoutServiceUrl if the idp is configured manually, " + "or check that the idp metadata contains a SingleLogoutService endpoint.", idp.EntityId.Id)); } var response = new Saml2LogoutResponse(Saml2StatusCode.Success) { DestinationUrl = idp.SingleLogoutServiceResponseUrl, SigningCertificate = options.SPOptions.SigningServiceCertificate, SigningAlgorithm = idp.OutboundSigningAlgorithm, InResponseTo = request.Id, Issuer = options.SPOptions.EntityId, RelayState = unbindResult.RelayState }; options.Notifications.LogoutResponseCreated(response, request, httpRequest.User, idp); options.SPOptions.Logger.WriteInformation("Got a logout request " + request.Id + ", responding with logout response " + response.Id); var result = Saml2Binding.Get(idp.SingleLogoutServiceBinding).Bind( response, options.SPOptions.Logger, options.Notifications.LogoutResponseXmlCreated); result.TerminateLocalSession = true; return(result); }
/// <summary> /// Run the command, initiating or handling the assertion consumer sequence. /// </summary> /// <param name="request">Request data.</param> /// <param name="options">Options</param> /// <returns>CommandResult</returns> public CommandResult Run(HttpRequestData request, IOptions options) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } var binding = options.Notifications.GetBinding(request); if (binding != null) { UnbindResult unbindResult = null; try { unbindResult = binding.Unbind(request, options); options.Notifications.MessageUnbound(unbindResult); var samlResponse = new Saml2Response(unbindResult.Data, request.StoredRequestState?.MessageId, options); var result = ProcessResponse(options, samlResponse, request.StoredRequestState); if (unbindResult.RelayState != null) { result.ClearCookieName = StoredRequestState.CookieNameBase + unbindResult.RelayState; } options.Notifications.AcsCommandResultCreated(result, samlResponse); return(result); } catch (FormatException ex) { throw new BadFormatSamlResponseException( "The SAML Response did not contain valid BASE64 encoded data.", ex); } catch (XmlException ex) { var newEx = new BadFormatSamlResponseException( "The SAML response contains incorrect XML", ex); // Add the payload to the exception if (unbindResult != null) { newEx.Data["Saml2Response"] = unbindResult.Data.OuterXml; } throw newEx; } catch (Exception ex) { if (unbindResult != null) { // Add the payload to the existing exception ex.Data["Saml2Response"] = unbindResult.Data.OuterXml; } throw; } } throw new NoSamlResponseFoundException(); }