private void ValidateLogoutViaPost(System.Collections.Specialized.NameValueCollection requestParams, out string message, out LogoutResponse response) { var parser = new HttpPostBindingParser(requestParams); logger.DebugFormat(TraceMessages.LogoutResponsePostBindingParse, parser.Message); response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(response.Issuer.Value, config); if (idp.Metadata == null) { logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } if (!parser.IsSigned) { logger.Error(ErrorMessages.ResponseSignatureMissing); throw new Saml20Exception(ErrorMessages.ResponseSignatureMissing); } // signature on final message in logout if (!parser.CheckSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ResponseSignatureInvalid); } message = parser.Message; }
/// <summary> /// Handle the authentication response from the IDP. /// </summary> /// <param name="context">The context.</param> private void HandleResponse(HttpContext context, Saml2Configuration config) { var defaultEncoding = Encoding.UTF8; var doc = GetDecodedSamlResponse(context, defaultEncoding); Logger.DebugFormat(TraceMessages.SamlResponseReceived, doc.OuterXml); // Determine whether the assertion should be decrypted before being validated. bool isEncrypted; var assertion = GetAssertion(doc.DocumentElement, out isEncrypted); if (isEncrypted) { assertion = GetDecryptedAssertion(assertion, config).Assertion.DocumentElement; } // Check if an encoding-override exists for the IdP endpoint in question var issuer = GetIssuer(assertion); var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); if (!endpoint.AllowReplayAttacks) { CheckReplayAttack(context, doc.DocumentElement, !endpoint.AllowIdPInitiatedSso); } var status = GetStatusElement(doc.DocumentElement); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { if (status.StatusCode.Value == Saml20Constants.StatusCodes.NoPassive) { Logger.Error(ErrorMessages.ResponseStatusIsNoPassive); throw new Saml20Exception(ErrorMessages.ResponseStatusIsNoPassive); } Logger.ErrorFormat(ErrorMessages.ResponseStatusNotSuccessful, status); throw new Saml20Exception(string.Format(ErrorMessages.ResponseStatusNotSuccessful, status)); } if (!string.IsNullOrEmpty(endpoint.ResponseEncoding)) { Encoding encodingOverride; try { encodingOverride = Encoding.GetEncoding(endpoint.ResponseEncoding); } catch (ArgumentException ex) { Logger.ErrorFormat(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding); throw new ArgumentException(string.Format(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding), ex); } if (encodingOverride.CodePage != defaultEncoding.CodePage) { var doc1 = GetDecodedSamlResponse(context, encodingOverride); assertion = GetAssertion(doc1.DocumentElement, out isEncrypted); } } HandleAssertion(context, assertion, config); }
/// <summary> /// Handle the authentication response from the IDP. /// </summary> /// <param name="context">The context.</param> public static Saml20Assertion HandleResponse(Saml2Configuration config, string samlResponse, IDictionary <string, object> session, Func <string, object> getFromCache, Action <string, object, DateTime> setInCache) { var defaultEncoding = Encoding.UTF8; var doc = Utility.GetDecodedSamlResponse(samlResponse, defaultEncoding); logger.DebugFormat(TraceMessages.SamlResponseReceived, doc.OuterXml); // Determine whether the assertion should be decrypted before being validated. bool isEncrypted; var assertion = Utility.GetAssertion(doc.DocumentElement, out isEncrypted); if (isEncrypted) { assertion = Utility.GetDecryptedAssertion(assertion, config).Assertion.DocumentElement; } // Check if an encoding-override exists for the IdP endpoint in question var issuer = Utility.GetIssuer(assertion); var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); if (!endpoint.AllowReplayAttacks) { Utility.CheckReplayAttack(doc.DocumentElement, !endpoint.AllowIdPInitiatedSso, session); } var status = Utility.GetStatusElement(doc.DocumentElement); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { if (status.StatusCode.Value == Saml20Constants.StatusCodes.NoPassive) { logger.Error(ErrorMessages.ResponseStatusIsNoPassive); throw new Saml20Exception(ErrorMessages.ResponseStatusIsNoPassive); } logger.ErrorFormat(ErrorMessages.ResponseStatusNotSuccessful, status); throw new Saml20Exception(string.Format(ErrorMessages.ResponseStatusNotSuccessful, status)); } if (!string.IsNullOrEmpty(endpoint.ResponseEncoding)) { Encoding encodingOverride; try { encodingOverride = Encoding.GetEncoding(endpoint.ResponseEncoding); } catch (ArgumentException ex) { logger.ErrorFormat(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding); throw new ArgumentException(string.Format(ErrorMessages.UnknownEncoding, endpoint.ResponseEncoding), ex); } if (encodingOverride.CodePage != defaultEncoding.CodePage) { var doc1 = GetDecodedSamlResponse(samlResponse, encodingOverride); assertion = GetAssertion(doc1.DocumentElement, out isEncrypted); } } return(HandleAssertion(assertion, config, getFromCache, setInCache)); }
/// <summary> /// Send an authentication request to the IDP. /// </summary> /// <param name="context">The context.</param> private void SendRequest(HttpContext context, Saml2Configuration config) { // See if the "ReturnUrl" - parameter is set. var returnUrl = context.Request.QueryString["ReturnUrl"]; if (!string.IsNullOrEmpty(returnUrl) && context.Session != null) { context.Session["RedirectUrl"] = returnUrl; } var isRedirected = false; var selectionUtil = new IdpSelectionUtil(Logger); var idp = selectionUtil.RetrieveIDP(context.Request.Params, context.Request.QueryString, config, s => { context.Response.Redirect(s); isRedirected = true; }); if (isRedirected) { return; } if (idp == null) { // Display a page to the user where she can pick the IDP Logger.DebugFormat(TraceMessages.IdentityProviderRedirect); var page = new SelectSaml20IDP(); page.ProcessRequest(context); return; } var authnRequest = Saml20AuthnRequest.GetDefault(config); TransferClient(idp, authnRequest, context, config); }
private string LogoutRequestForIdp(IdentityProvider identityProvider, Saml20LogoutRequest request, IOwinContext context, Saml2Configuration config) { var logger = SAML2.Logging.LoggerProvider.LoggerFor(typeof(SamlMessage)); var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.DefaultLogoutEndpoint, identityProvider.Metadata.IDPSLOEndpoints); request.Destination = destination.Url; if (destination.Binding == BindingType.Redirect) { // do not set the Reason for DigiD //request.Reason = Saml20Constants.Reasons.User; context.Set(IdpTempSessionKey, identityProvider.Id); var identity = context.Request.User.Identity as ClaimsIdentity; var nameId = identity.Claims.Single(c => c.Type == ClaimTypes.NameID).Value; request.SubjectToLogOut.Value = nameId; var builder = new HttpRedirectBindingBuilder { Request = request.GetXml().OuterXml, SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey }; var redirectUrl = destination.Url + (destination.Url.Contains("?") ? "&" : "?") + builder.ToQuery(); logger.DebugFormat(TraceMessages.LogoutRequestSent, identityProvider.Id, "REDIRECT", redirectUrl); return(redirectUrl); } throw new NotImplementedException(); }
public override string BuildRedirectUrl() { string rc = null; var logger = SAML2.Logging.LoggerProvider.LoggerFor(typeof(SamlMessage)); var selectionUtil = new IdpSelectionUtil(logger); var allparams = BuildParams(form, context.Request.Query); var idp = selectionUtil.RetrieveIDP(allparams, BuildParams(context.Request.Query), config, s => rc = s); if (rc != null) { return(rc); // IDP selection screen } if (idp == null) { // Display a page to the user where she can pick the IDP logger.DebugFormat(TraceMessages.IdentityProviderRedirect); throw new NotImplementedException("Selection of IDP not yet done (probably need a map call on middleware extension method)"); //var page = new SelectSaml20IDP(); //page.ProcessRequest(context); //return; } var authnRequest = Saml20AuthnRequest.GetDefault(config); return(AuthnRequestForIdp(idp, authnRequest, context, config)); }
/// <summary> /// Handles the SOAP. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> public static void HandleSoap(HttpArtifactBindingBuilder builder, Stream inputStream, Saml2Configuration config, Action<Saml20Assertion> signonCallback, Func<string, object> getFromCache, Action<string, object, DateTime> setInCache, IDictionary<string, object> session) { var parser = new HttpArtifactBindingParser(inputStream); logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); if (parser.IsArtifactResolve) { logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve, ((XmlDocument)getFromCache(parser.ArtifactResolve.Artifact)).DocumentElement); } else if (parser.IsArtifactResponse) { logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == Response.ElementName) { Utility.CheckReplayAttack(parser.ArtifactResponse.Any, true, session); var responseStatus = Utility.GetStatusElement(parser.ArtifactResponse.Any); if (responseStatus.StatusCode.Value != Saml20Constants.StatusCodes.Success) { logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value)); } bool isEncrypted; var assertion = Utility.GetAssertion(parser.ArtifactResponse.Any, out isEncrypted); if (assertion == null) { logger.Error(ErrorMessages.ArtifactResponseMissingAssertion); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingAssertion); } var samlAssertion = isEncrypted ? Utility.HandleEncryptedAssertion(assertion, config, getFromCache, setInCache) : Utility.HandleAssertion(assertion, config, getFromCache, setInCache); signonCallback(samlAssertion); } else { logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else { logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
/// <summary> /// Handles the selection of an IDP. If only one IDP is found, the user is automatically redirected to it. /// If several are found, and nothing indicates to which one the user should be sent, this method returns null. /// </summary> /// <param name="context">The context.</param> /// <returns>The <see cref="IdentityProviderElement"/>.</returns> public IdentityProviderElement RetrieveIDP(HttpContext context) { var config = Saml2Config.GetConfig(); // If idpChoice is set, use it value if (!string.IsNullOrEmpty(context.Request.Params[IdpChoiceParameterName])) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromQueryString, context.Request.Params[IdpChoiceParameterName]); var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == context.Request.Params[IdpChoiceParameterName]); if (endPoint != null) { return(endPoint); } } // If we have a common domain cookie, use it's value // It must have been returned from the local common domain cookie reader endpoint. if (!string.IsNullOrEmpty(context.Request.QueryString["_saml_idp"])) { var cdc = new CommonDomainCookie(context.Request.QueryString["_saml_idp"]); if (cdc.IsSet) { var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == cdc.PreferredIDP); if (endPoint != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromCommonDomainCookie, cdc.PreferredIDP); return(endPoint); } Logger.WarnFormat(ErrorMessages.CommonDomainCookieIdentityProviderInvalid, cdc.PreferredIDP); } } // If there is only one configured IdentityProviderEndpointElement lets just use that if (config.IdentityProviders.Count == 1 && config.IdentityProviders[0].Metadata != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, config.IdentityProviders[0].Name); return(config.IdentityProviders[0]); } // If one of the endpoints are marked with default, use that one var defaultIDP = config.IdentityProviders.FirstOrDefault(idp => idp.Default); if (defaultIDP != null) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, defaultIDP.Id); return(defaultIDP); } // In case an IDP selection url has been configured, redirect to that one. if (!string.IsNullOrEmpty(config.IdentityProviders.SelectionUrl)) { Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromSelection, config.IdentityProviders.SelectionUrl); context.Response.Redirect(config.IdentityProviders.SelectionUrl); } // If an IDPSelectionEvent handler is present, request the handler for an IDP endpoint to use. return(IdpSelectionUtil.InvokeIDPSelectionEventHandler(config.IdentityProviders)); }
/// <summary> /// Handles the SOAP message. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private async Task HandleSoap(IOwinContext context, Stream inputStream, NameValueCollection requestParams) { var config = options.Configuration; var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = GetBuilder(context); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (parser.IsLogoutReqest) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.SamlMessage.OuterXml); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.ErrorFormat(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } var req = parser.LogoutRequest; var logoutRequestReceivedNotification = new LogoutRequestReceivedNotification <LogoutRequest, SamlAuthenticationOptions>(context, options) { ProtocolMessage = req }; await options.Notifications.LogoutRequestReceived(logoutRequestReceivedNotification); DoLogout(context, true); // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; // response.Destination = destination.Url; var doc = response.GetXml(); XmlSignatureUtils.SignDocument(doc, response.Id, config.ServiceProvider.SigningCertificate); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } SendResponseMessage(doc.OuterXml, context); } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> /// <param name="context">The context.</param> /// <param name="elem">The elem.</param> public static Saml20Assertion HandleAssertion(XmlElement elem, Saml2Configuration config, Func <string, object> getFromCache, Action <string, object, DateTime> setInCache) { logger.DebugFormat(TraceMessages.AssertionProcessing, elem.OuterXml); var issuer = GetIssuer(elem); var endp = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); PreHandleAssertion(elem, endp); if (endp == null || endp.Metadata == null) { logger.Error(ErrorMessages.AssertionIdentityProviderUnknown); throw new Saml20Exception(ErrorMessages.AssertionIdentityProviderUnknown); } var quirksMode = endp.QuirksMode; var assertion = new Saml20Assertion(elem, null, quirksMode, config); // Check signatures if (!endp.OmitAssertionSignatureCheck) { var keys = endp.Metadata.GetKeys(KeyTypes.Signing); if (keys == null || !keys.Any()) { keys = endp.Metadata.GetKeys(KeyTypes.Encryption); } var trusted = GetTrustedSigners(keys, endp); if (!assertion.CheckSignature(trusted)) { logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } } // Check expiration if (assertion.IsExpired) { logger.Error(ErrorMessages.AssertionExpired); throw new Saml20Exception(ErrorMessages.AssertionExpired); } // Check one time use if (assertion.IsOneTimeUse) { if (getFromCache(assertion.Id) != null) { logger.Error(ErrorMessages.AssertionOneTimeUseExceeded); throw new Saml20Exception(ErrorMessages.AssertionOneTimeUseExceeded); } setInCache(assertion.Id, string.Empty, assertion.NotOnOrAfter); } logger.DebugFormat(TraceMessages.AssertionParsed, assertion.Id); return(assertion); }
private static IdentityProviderEndpoint ConfigureRequest(IdentityProvider identityProvider, Saml20AuthnRequest request, HttpContext context) { // Set the last IDP we attempted to login at. if (context.Session != null) { context.Session[IdpTempSessionKey] = identityProvider.Id; } context.Items[IdpTempSessionKey] = identityProvider.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.DefaultSignOnEndpoint, identityProvider.Metadata.SSOEndpoints); request.Destination = destination.Url; if (identityProvider.ForceAuth) { request.ForceAuthn = true; } // Check isPassive status var isPassiveFlag = context.Session != null ? context.Session[IdpIsPassive] : null; if (isPassiveFlag != null && (bool)isPassiveFlag) { request.IsPassive = true; context.Session[IdpIsPassive] = null; } if (identityProvider.IsPassive) { request.IsPassive = true; } // Check if request should forceAuthn var forceAuthnFlag = context.Session != null ? context.Session[IdpForceAuthn] : null; if (forceAuthnFlag != null && (bool)forceAuthnFlag) { request.ForceAuthn = true; context.Session[IdpForceAuthn] = null; } // Check if protocol binding should be forced if (identityProvider.Endpoints.DefaultSignOnEndpoint != null) { if (!string.IsNullOrEmpty(identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding; } } Utility.AddExpectedResponse(request, SessionToDictionary(context.Session)); return(destination); }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> /// <param name="context">The context.</param> /// <param name="elem">The elem.</param> private void HandleAssertion(HttpContext context, XmlElement elem, Saml2Configuration config) { Logger.DebugFormat(TraceMessages.AssertionProcessing, elem.OuterXml); var issuer = GetIssuer(elem); var endp = IdpSelectionUtil.RetrieveIDPConfiguration(issuer, config); PreHandleAssertion(context, elem, endp); if (endp == null || endp.Metadata == null) { Logger.Error(ErrorMessages.AssertionIdentityProviderUnknown); throw new Saml20Exception(ErrorMessages.AssertionIdentityProviderUnknown); } var quirksMode = endp.QuirksMode; var assertion = new Saml20Assertion(elem, null, quirksMode, config); // Check signatures if (!endp.OmitAssertionSignatureCheck) { if (!assertion.CheckSignature(GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp))) { Logger.Error(ErrorMessages.AssertionSignatureInvalid); throw new Saml20Exception(ErrorMessages.AssertionSignatureInvalid); } } // Check expiration if (assertion.IsExpired) { Logger.Error(ErrorMessages.AssertionExpired); throw new Saml20Exception(ErrorMessages.AssertionExpired); } // Check one time use if (assertion.IsOneTimeUse) { if (context.Cache[assertion.Id] != null) { Logger.Error(ErrorMessages.AssertionOneTimeUseExceeded); throw new Saml20Exception(ErrorMessages.AssertionOneTimeUseExceeded); } context.Cache.Insert(assertion.Id, string.Empty, null, assertion.NotOnOrAfter, Cache.NoSlidingExpiration); } Logger.DebugFormat(TraceMessages.AssertionParsed, assertion.Id); DoSignOn(context, assertion, config); }
/// <summary> /// Handles a request. /// </summary> /// <param name="context">The context.</param> public void Handle(HttpContext context, Saml2Configuration config) { Logger.Debug(TraceMessages.LogoutHandlerCalled); // Some IDP's are known to fail to set an actual value in the SOAPAction header // so we just check for the existence of the header field. if (Array.Exists(context.Request.Headers.AllKeys, s => s == SoapConstants.SoapAction)) { HandleSoap(context, context.Request.InputStream, config); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { HandleArtifact(context, ConfigurationFactory.Instance.Configuration, HandleSoap); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLResponse"])) { HandleResponse(context, config); } else if (!string.IsNullOrEmpty(context.Request.Params["SAMLRequest"])) { HandleRequest(context); } else { IdentityProvider idpEndpoint = null; // context.Session[IDPLoginSessionKey] may be null if IIS has been restarted if (context.Session[IdpSessionIdKey] != null) { idpEndpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config); } if (idpEndpoint == null) { // TODO: Reconsider how to accomplish this. context.User = null; FormsAuthentication.SignOut(); Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, string.Empty); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, string.Empty)); } TransferClient(idpEndpoint, context, config); } }
/// <summary> /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event. /// </summary> /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param> protected override void OnLoad(EventArgs e) { TitleText = Resources.PageIdentityProviderSelectTitle; HeaderText = Resources.PageIdentityProviderSelectTitle; BodyPanel.Controls.Add(new LiteralControl(Resources.PageIdentityProviderSelectDescription)); BodyPanel.Controls.Add(new LiteralControl("<br/><br/>")); var config = ConfigurationFactory.Instance.Configuration; foreach (var endPoint in config.IdentityProviders) { if (endPoint.Metadata != null) { var link = new HyperLink { Text = string.IsNullOrEmpty(endPoint.Name) ? endPoint.Metadata.EntityId : endPoint.Name, NavigateUrl = IdpSelectionUtil.GetIdpLoginUrl(endPoint.Id, ConfigurationFactory.Instance.Configuration) }; // Link text. If a name has been specified in web.config, use it. Otherwise, use id from metadata. BodyPanel.Controls.Add(link); BodyPanel.Controls.Add(new LiteralControl("<br/>")); } else { var label = new Label { Text = endPoint.Name }; label.Style.Add(HtmlTextWriterStyle.TextDecoration, "line-through"); BodyPanel.Controls.Add(label); label = new Label { Text = " (Metadata not found)" }; label.Style.Add(HtmlTextWriterStyle.FontSize, "x-small"); BodyPanel.Controls.Add(label); BodyPanel.Controls.Add(new LiteralControl("<br/>")); } } }
public string BuildSignOutRedirectUrl() { string rc = null; var logger = SAML2.Logging.LoggerProvider.LoggerFor(typeof(SamlMessage)); var selectionUtil = new IdpSelectionUtil(logger); var allparams = BuildParams(form, context.Request.Query); var idp = selectionUtil.RetrieveIDP(allparams, BuildParams(context.Request.Query), config, s => rc = s); if (rc != null) { return(rc); // IDP selection screen } if (idp == null) { logger.DebugFormat(TraceMessages.IdentityProviderRedirect); throw new NotImplementedException("Selection of IDP not yet done (probably need a map call on middleware extension method)"); } var logoutRequest = Saml20LogoutRequest.GetDefault(config); return(LogoutRequestForIdp(idp, logoutRequest, context, config)); }
private void ValidateLogoutViaGet(Uri requestUrl, out string message, out LogoutResponse response) { var parser = new HttpRedirectBindingParser(requestUrl); response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); logger.DebugFormat(TraceMessages.LogoutResponseRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(response.Issuer.Value, config); if (idp.Metadata == null) { logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } if (!parser.VerifySignature(idp.Metadata.Keys)) { logger.Error(ErrorMessages.ResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ResponseSignatureInvalid); } message = parser.Message; }
private string AuthnRequestForIdp(IdentityProvider identityProvider, Saml20AuthnRequest request, IOwinContext context, Saml2Configuration config) { var logger = SAML2.Logging.LoggerProvider.LoggerFor(typeof(SamlMessage)); context.Set(IdpTempSessionKey, identityProvider.Id); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.DefaultSignOnEndpoint, identityProvider.Metadata.SSOEndpoints); request.Destination = destination.Url; if (identityProvider.ForceAuth) { request.ForceAuthn = true; } // Check isPassive status if (context.Get <bool>(IdpIsPassive)) { request.IsPassive = true; } if (identityProvider.IsPassive) { request.IsPassive = true; } // Check if request should forceAuthn if (context.Get <bool>(IdpForceAuthn)) { request.ForceAuthn = true; } // Check if protocol binding should be forced if (identityProvider.Endpoints.DefaultSignOnEndpoint != null) { if (!string.IsNullOrEmpty(identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding; } } // Save request message id to session Utility.AddExpectedResponseId(request.Id); switch (destination.Binding) { case BindingType.Redirect: logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpRedirect); var redirectBuilder = new HttpRedirectBindingBuilder { SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey, Request = request.GetXml().OuterXml }; if (context.Authentication != null && context.Authentication.AuthenticationResponseChallenge != null && context.Authentication.AuthenticationResponseChallenge.Properties != null && context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary != null && context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary.Count > 0) { redirectBuilder.RelayState = context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary.ToDelimitedString(); } logger.DebugFormat(TraceMessages.AuthnRequestSent, redirectBuilder.Request); var redirectLocation = request.Destination + (request.Destination.Contains("?") ? "&" : "?") + redirectBuilder.ToQuery(); return(redirectLocation); case BindingType.Post: case BindingType.PostSimpleSign: throw new NotImplementedException(); //logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPost); //var postBuilder = new HttpPostBindingBuilder(destination); //// Honor the ForceProtocolBinding and only set this if it's not already set //if (string.IsNullOrEmpty(request.ProtocolBinding)) { // request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost; //} //var requestXml = request.GetXml(); //XmlSignatureUtils.SignDocument(requestXml, request.Id, config.ServiceProvider.SigningCertificate); //postBuilder.Request = requestXml.OuterXml; //logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request); //context.Response.Write(postBuilder.GetPage()); //break; case BindingType.Artifact: throw new NotImplementedException(); //logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact); //var artifactBuilder = new HttpArtifactBindingBuilder(context, config); //// Honor the ForceProtocolBinding and only set this if it's not already set //if (string.IsNullOrEmpty(request.ProtocolBinding)) { // request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpArtifact; //} //logger.DebugFormat(TraceMessages.AuthnRequestSent, request.GetXml().OuterXml); //artifactBuilder.RedirectFromLogin(destination, request); //break; default: logger.Error(SAML2.ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(SAML2.ErrorMessages.EndpointBindingInvalid); } throw new NotImplementedException(); }
/// <summary> /// Transfers the client. /// </summary> /// <param name="identityProvider">The identity provider.</param> /// <param name="request">The request.</param> /// <param name="context">The context.</param> private void TransferClient(IdentityProvider identityProvider, Saml20AuthnRequest request, HttpContext context, Saml2Configuration config) { // Set the last IDP we attempted to login at. if (context.Session != null) { context.Session[IdpTempSessionKey] = identityProvider.Id; } context.Items[IdpTempSessionKey] = identityProvider.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.DefaultSignOnEndpoint, identityProvider.Metadata.SSOEndpoints); request.Destination = destination.Url; if (identityProvider.ForceAuth) { request.ForceAuthn = true; } // Check isPassive status var isPassiveFlag = context.Session != null ? context.Session[IdpIsPassive] : null; if (isPassiveFlag != null && (bool)isPassiveFlag) { request.IsPassive = true; context.Session[IdpIsPassive] = null; } if (identityProvider.IsPassive) { request.IsPassive = true; } // Check if request should forceAuthn var forceAuthnFlag = context.Session != null ? context.Session[IdpForceAuthn] : null; if (forceAuthnFlag != null && (bool)forceAuthnFlag) { request.ForceAuthn = true; context.Session[IdpForceAuthn] = null; } // Check if protocol binding should be forced if (identityProvider.Endpoints.DefaultSignOnEndpoint != null) { if (!string.IsNullOrEmpty(identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding; } } // Save request message id to session if (context.Session != null) { context.Session.Add(ExpectedInResponseToSessionKey, request.Id); } else { ExpectedResponses.Add(request.Id); } switch (destination.Binding) { case BindingType.Redirect: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpRedirect); var redirectBuilder = new HttpRedirectBindingBuilder { SigningKey = _certificate.PrivateKey, Request = request.GetXml().OuterXml }; Logger.DebugFormat(TraceMessages.AuthnRequestSent, redirectBuilder.Request); var redirectLocation = request.Destination + "?" + redirectBuilder.ToQuery(); context.Response.Redirect(redirectLocation, true); break; case BindingType.Post: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPost); var postBuilder = new HttpPostBindingBuilder(destination); // Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost; } var requestXml = request.GetXml(); XmlSignatureUtils.SignDocument(requestXml, request.Id, config.ServiceProvider.SigningCertificate); postBuilder.Request = requestXml.OuterXml; Logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request); context.Response.Write(postBuilder.GetPage()); break; case BindingType.Artifact: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact); var artifactBuilder = new HttpArtifactBindingBuilder(context, config); // Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpArtifact; } Logger.DebugFormat(TraceMessages.AuthnRequestSent, request.GetXml().OuterXml); artifactBuilder.RedirectFromLogin(destination, request); break; default: Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); } }
/// <summary> /// Handles the SOAP. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private void HandleSoap(HttpContext context, Stream inputStream, Saml2Configuration config) { var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = new HttpArtifactBindingBuilder(context, config); if (parser.IsArtifactResolve) { Logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve); } else if (parser.IsArtifactResponse) { Logger.Debug(TraceMessages.ArtifactResolveReceived); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == Response.ElementName) { CheckReplayAttack(context, parser.ArtifactResponse.Any, true); var responseStatus = GetStatusElement(parser.ArtifactResponse.Any); if (responseStatus.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, responseStatus.StatusCode.Value)); } bool isEncrypted; var assertion = GetAssertion(parser.ArtifactResponse.Any, out isEncrypted); if (assertion == null) { Logger.Error(ErrorMessages.ArtifactResponseMissingAssertion); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingAssertion); } if (isEncrypted) { HandleEncryptedAssertion(context, assertion, config); } else { HandleAssertion(context, assertion, config); } } else { Logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }
private void SendAuthNRequest(Saml2Configuration config) { _logger.LogDebug("Begin Transfer User to IDP"); if (!config.IdentityProviders.Any()) { _logger.LogError("No IdentityProviders configured"); } // order signon endpoints by index and then check for default. var idp = (GetFirstIdp(config)?.Metadata.SSOEndpoints.FirstOrDefault(x => x.Binding == BindingType.Post && x.Type == EndpointType.SignOn) ?? GetFirstIdp(config)?.Metadata.SSOEndpoints.FirstOrDefault(x => x.Binding == BindingType.Redirect && x.Type == EndpointType.SignOn)); if (idp == null) { _logger.LogError("IdentityProvider configured does not have POST or Redirect binding", config.IdentityProviders.FirstOrDefault()); } _logger.LogDebug($"IdentityProvider found: {idp.Binding.ToString()}"); var authnRequest = CreateAuthNRequest(config); if (idp.Binding == BindingType.Post) { var post = new SAMLSilly.Bindings.HttpPostBindingBuilder(idp); if (string.IsNullOrEmpty(authnRequest.ProtocolBinding)) { authnRequest.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost; } var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Post, idp, config.IdentityProviders[0].Metadata.SSOEndpoints); authnRequest.Destination = destination.Url; var requestXml = authnRequest.GetXml(); if (config.ServiceProvider.AuthNRequestsSigned) { _logger.LogDebug("Sign AuthNRequest"); XmlSignatureUtils.SignDocument(requestXml, authnRequest.Id, config); } post.Request = requestXml.OuterXml; _httpContextAccessor.HttpContext.Response.WriteAsync(post.GetPage()); return; } else { var redirectBuilder = new SAMLSilly.AspNetCore.BindingBuilders.HttpRedirectBindingBuilder(config); // if (string.IsNullOrEmpty(authnRequest.ProtocolBinding)) // { authnRequest.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost; // } var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, idp, config.IdentityProviders[0].Metadata.SSOEndpoints); authnRequest.Destination = destination.Url; redirectBuilder.Request = authnRequest.GetXml().OuterXml; redirectBuilder.SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey; var query = redirectBuilder.ToQuery(); var url = $"{idp.Url}?{query}"; _httpContextAccessor.HttpContext.Response.Redirect(url, false); return; } throw new NotImplementedException(); }
/// <summary> /// Transfers the client. /// </summary> /// <param name="idp">The identity provider.</param> /// <param name="context">The context.</param> private void TransferClient(IdentityProvider idp, HttpContext context, Saml2Configuration config) { var request = Saml20LogoutRequest.GetDefault(config); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.DefaultLogoutEndpoint, idp.Metadata.IDPSLOEndpoints); request.Destination = destination.Url; var nameIdFormat = (string)context.Session[IdpNameIdFormat]; request.SubjectToLogOut.Format = nameIdFormat; // Handle POST binding if (destination.Binding == BindingType.Post) { var builder = new HttpPostBindingBuilder(destination); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = (string)context.Session[IdpNameId]; request.SessionIndex = (string)context.Session[IdpSessionIdKey]; var requestDocument = request.GetXml(); XmlSignatureUtils.SignDocument(requestDocument, request.Id, config); builder.Request = requestDocument.OuterXml; Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "POST", builder.Request); context.Response.Write(builder.GetPage()); context.Response.End(); return; } // Handle Redirect binding if (destination.Binding == BindingType.Redirect) { request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = (string)context.Session[IdpNameId]; request.SessionIndex = (string)context.Session[IdpSessionIdKey]; var builder = new HttpRedirectBindingBuilder { Request = request.GetXml().OuterXml, SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey }; var redirectUrl = destination.Url + "?" + builder.ToQuery(); Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "REDIRECT", redirectUrl); context.Response.Redirect(redirectUrl, true); return; } // Handle Artifact binding if (destination.Binding == BindingType.Artifact) { request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = (string)context.Session[IdpNameId]; request.SessionIndex = (string)context.Session[IdpSessionIdKey]; Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "ARTIFACT", request.GetXml().OuterXml); var builder = GetBuilder(context); builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N"), (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration)); } Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); }
/// <summary> /// Handles the request. /// </summary> /// <param name="context">The context.</param> private void HandleRequest(HttpContext context) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived); // Fetch the endpoint configuration var idp = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], ConfigurationFactory.Instance.Configuration); var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.DefaultLogoutEndpoint, idp.Metadata.IDPSLOEndpoints); // Fetch config object var config = ConfigurationFactory.Instance.Configuration; // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, Destination = destination.Url, StatusCode = Saml20Constants.StatusCodes.Success }; string message; if (context.Request.RequestType == "GET") { // HTTP Redirect binding var parser = new HttpRedirectBindingParser(context.Request.Url); Logger.DebugFormat(TraceMessages.LogoutRequestRedirectBindingParse, parser.Message, parser.SignatureAlgorithm, parser.Signature); var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id); if (endpoint == null || endpoint.Metadata == null) { Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } var metadata = endpoint.Metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.Signing))) { Logger.Error(ErrorMessages.RequestSignatureInvalid); throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid); } message = parser.Message; } else if (context.Request.RequestType == "POST") { // HTTP Post binding var parser = new HttpPostBindingParser(context.Request.Params); Logger.DebugFormat(TraceMessages.LogoutRequestPostBindingParse, parser.Message); if (!parser.IsSigned) { Logger.Error(ErrorMessages.RequestSignatureMissing); throw new Saml20Exception(ErrorMessages.RequestSignatureMissing); } var endpoint = config.IdentityProviders.FirstOrDefault(x => x.Id == idp.Id); if (endpoint == null || endpoint.Metadata == null) { Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, idp.Id); throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, idp.Id)); } var metadata = endpoint.Metadata; // Check signature if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.Signing))) { Logger.Error(ErrorMessages.RequestSignatureInvalid); throw new Saml20Exception(ErrorMessages.RequestSignatureInvalid); } message = parser.Message; } else { // Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS Logger.ErrorFormat(ErrorMessages.UnsupportedRequestType, context.Request.RequestType); throw new Saml20Exception(string.Format(ErrorMessages.UnsupportedRequestType, context.Request.RequestType)); } Logger.DebugFormat(TraceMessages.LogoutRequestParsed, message); // Log the user out locally DoLogout(context, true); var req = Serialization.DeserializeFromXmlString <LogoutRequest>(message); response.InResponseTo = req.Id; // Respond using redirect binding if (destination.Binding == BindingType.Redirect) { var builder = new HttpRedirectBindingBuilder { RelayState = context.Request.Params["RelayState"], Response = response.GetXml().OuterXml, SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey }; Logger.DebugFormat(TraceMessages.LogoutResponseSent, builder.Response); context.Response.Redirect(destination.Url + "?" + builder.ToQuery(), true); return; } // Respond using post binding if (destination.Binding == BindingType.Post) { var builder = new HttpPostBindingBuilder(destination) { Action = SamlActionType.SAMLResponse }; var responseDocument = response.GetXml(); Logger.DebugFormat(TraceMessages.LogoutResponseSent, responseDocument.OuterXml); XmlSignatureUtils.SignDocument(responseDocument, response.Id, config); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; context.Response.Write(builder.GetPage()); } }
/// <summary> /// Handles the SOAP message. /// </summary> /// <param name="context">The context.</param> /// <param name="inputStream">The input stream.</param> private void HandleSoap(HttpContext context, Stream inputStream, Saml2Configuration config) { var parser = new HttpArtifactBindingParser(inputStream); Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml); var builder = GetBuilder(context); var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config); if (parser.IsArtifactResolve) { Logger.DebugFormat(TraceMessages.ArtifactResolveReceived, parser.SamlMessage); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.ErrorFormat(ErrorMessages.ArtifactResolveSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid); } builder.RespondToArtifactResolve(parser.ArtifactResolve, parser.SamlMessage); } else if (parser.IsArtifactResponse) { Logger.DebugFormat(TraceMessages.ArtifactResponseReceived, parser.SamlMessage); if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys)) { Logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid); throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid); } var status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value); throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value)); } if (parser.ArtifactResponse.Any.LocalName == LogoutRequest.ElementName) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.ArtifactResponse.Any.OuterXml); var req = Serialization.DeserializeFromXmlString <LogoutRequest>(parser.ArtifactResponse.Any.OuterXml); // Send logoutresponse via artifact var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config); var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, endpoint.Endpoints.DefaultLogoutEndpoint, endpoint.Metadata.IDPSLOEndpoints); builder.RedirectFromLogout(destination, response, context.Request.Params["relayState"], (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration)); } else if (parser.ArtifactResponse.Any.LocalName == LogoutResponse.ElementName) { DoLogout(context, false, config); } else { Logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse); throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse); } } else if (parser.IsLogoutReqest) { Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.SamlMessage.OuterXml); var req = parser.LogoutRequest; // Build the response object var response = new Saml20LogoutResponse { Issuer = config.ServiceProvider.Id, StatusCode = Saml20Constants.StatusCodes.Success, InResponseTo = req.Id }; // response.Destination = destination.Url; var doc = response.GetXml(); XmlSignatureUtils.SignDocument(doc, response.Id, config); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } SendResponseMessage(doc.OuterXml, context); } else { Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage); throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage); } }