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 void TestParsing_01() { HttpRedirectBindingBuilder bindingBuilder = new HttpRedirectBindingBuilder(); string request = string.Empty.PadLeft(350, 'A') + "ֶ״ֵזרו"; bindingBuilder.Request = request; string query = bindingBuilder.ToQuery(); NameValueCollection coll = QueryToNameValueCollection(query); Assert.That(coll.Count == 1); Uri url = new Uri("http://localhost/?" + query); HttpRedirectBindingParser bindingParser = new HttpRedirectBindingParser(url); Assert.That(bindingParser.IsRequest); Assert.That(!bindingParser.IsResponse); Assert.That(!bindingParser.IsSigned); Assert.AreEqual(request, bindingParser.Message); try { bindingParser.CheckSignature(new RSACryptoServiceProvider()); Assert.Fail("Trying to verify signature of an unsigned request should have thrown an exception."); } catch (InvalidOperationException) {} }
/// <summary> /// Transfers the message to the given endpoint using the HTTP-Redirect binding. /// </summary> protected static void HTTPRedirect(SAMLAction action, IDPEndPointElement endpoint, XmlNode message) { if (message.FirstChild is XmlDeclaration) { message.RemoveChild(message.FirstChild); } HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); if (action == SAMLAction.SAMLRequest) { builder.Request = message.OuterXml; } else { builder.Response = message.OuterXml; } builder.signingKey = IDPConfig.IDPCertificate.PrivateKey; UriBuilder url = new UriBuilder(endpoint.Url); url.Query = builder.ToQuery(); HttpContext.Current.Response.Redirect(url.ToString(), true); }
public void ParserCanEncodeAndDecodeRequestWithRelayState() { // Arrange var request = string.Empty.PadRight(140, 'l'); var relaystate = "A relaystate test. @@@!!!&&&///"; var bindingBuilder = new HttpRedirectBindingBuilder() { Request = request, RelayState = relaystate }; var query = bindingBuilder.ToQuery(); var coll = QueryToNameValueCollection(query); var url = new Uri("http://localhost/?" + query); // Act var bindingParser = new HttpRedirectBindingParser(url); // Assert Assert.Equal(2, coll.Count); Assert.True(bindingParser.IsRequest); Assert.False(bindingParser.IsResponse); Assert.False(bindingParser.IsSigned); Assert.NotNull(bindingParser.RelayState); Assert.Equal(relaystate, bindingParser.RelayStateDecoded); Assert.Equal(request, bindingParser.Message); }
public void TestIntegrity_02() { HttpRedirectBindingBuilder binding = new HttpRedirectBindingBuilder(); binding.Response = "Response"; try { binding.Request = "Request"; Assert.Fail("HttpRedirectBinding did not throw an exception when both Request and Response were set."); } catch (ArgumentException) { } }
public void DoesNotAllowRequestAndResponseToBothBeSet() { // Arrange var binding = new HttpRedirectBindingBuilder { Request = "Request" }; // Act Assert.Throws <ArgumentException>(() => binding.Response = "Response"); }
public void DoesNotAllowRequestAndResponseToBothBeSet() { // Arrange var binding = new HttpRedirectBindingBuilder { Request = "Request" }; // Act binding.Response = "Response"; // Assert Assert.Fail("HttpRedirectBinding did not throw an exception when both Request and Response were set."); }
public void TestRelaystate_01() { HttpRedirectBindingBuilder bindingBuilder = new HttpRedirectBindingBuilder(); bindingBuilder.Request = "A random request... !!!! .... "; string relaystate = string.Empty.PadRight(10, 'A'); bindingBuilder.RelayState = relaystate; // When using the builder to create a request, the relaystate should be encoded. string query = bindingBuilder.ToQuery(); Assert.That(!query.Contains(relaystate)); }
//"HttpRedirectBinding did not throw an exception when both Request and Response were set." public void DoesNotAllowRequestAndResponseToBothBeSet() { // Arrange var binding = new HttpRedirectBindingBuilder() { Request = "Request" }; // Assert Assert.Throws(typeof(ArgumentException), () => { // Act binding.Response = "Response"; }); }
public void TestRelaystate_02() { HttpRedirectBindingBuilder bindingBuilder = new HttpRedirectBindingBuilder(); bindingBuilder.Response = "A random response... !!!! .... "; string relaystate = string.Empty.PadRight(10, 'A'); bindingBuilder.RelayState = relaystate; // When using the builder to create a response, the relaystate // should not be encoded, and can thus be located in the query-string string query = bindingBuilder.ToQuery(); Assert.That(query.Contains(relaystate)); }
public void EncodesRelayStateForRequests() { // Arrange var relaystate = string.Empty.PadRight(10, 'A'); var bindingBuilder = new HttpRedirectBindingBuilder { Request = "A random request... !!!! .... ", RelayState = relaystate }; // Act var query = bindingBuilder.ToQuery(); // Assert Assert.That(!query.Contains(relaystate)); }
public void DoesNotEncodeRelayStateForResponse() { // Arrange var relaystate = string.Empty.PadRight(10, 'A'); var bindingBuilder = new HttpRedirectBindingBuilder { RelayState = relaystate, Response = "A random response... !!!! .... " }; // Act var query = bindingBuilder.ToQuery(); // Assert Assert.That(query.Contains(relaystate)); }
public void ParserThrowsExceptionWhenTryingToVerifySignatureOfUnsignedRequest() { // Arrange var request = string.Empty.PadLeft(350, 'A') + "������"; var bindingBuilder = new HttpRedirectBindingBuilder() { Request = request }; var query = bindingBuilder.ToQuery(); var url = new Uri("http://localhost/?" + query); var bindingParser = new HttpRedirectBindingParser(url); // Assert Assert.Throws(typeof(InvalidOperationException), () => { // Act bindingParser.CheckSignature(new RSACryptoServiceProvider()); }); }
public void TestDSASigning() { HttpRedirectBindingBuilder binding = new HttpRedirectBindingBuilder(); DSACryptoServiceProvider key = new DSACryptoServiceProvider(); binding.signingKey = key; binding.Request = string.Empty.PadLeft(500, 'a'); // Now, parse the query. Uri url = new Uri("http://localhost/?" + binding.ToQuery()); HttpRedirectBindingParser parser = new HttpRedirectBindingParser(url); Assert.That(parser.IsSigned); Assert.That(parser.IsRequest); Assert.That(parser.CheckSignature(key)); // Create a new key set, and check that it can not verify the signature. DSACryptoServiceProvider evilKey = new DSACryptoServiceProvider(); Assert.IsFalse(parser.CheckSignature(evilKey)); }
public void ParserCanSignAuthnRequestWithDsaKey() { // Arrange var key = new DSACryptoServiceProvider(); var evilKey = new DSACryptoServiceProvider(); var binding = new HttpRedirectBindingBuilder() { Request = string.Empty.PadLeft(500, 'a'), //SigningCertificate = key }; var url = new Uri("http://localhost/?" + binding.ToQuery()); // Act var parser = new HttpRedirectBindingParser(url); // Assert Assert.True(parser.IsSigned); Assert.True(parser.IsRequest); Assert.True(parser.CheckSignature(key)); Assert.False(parser.CheckSignature(evilKey)); }
public void ParserCanEncodeAndDecodeRequest() { // Arrange var request = string.Empty.PadLeft(350, 'A') + "������"; var bindingBuilder = new HttpRedirectBindingBuilder() { Request = request }; var query = bindingBuilder.ToQuery(); var coll = QueryToNameValueCollection(query); var url = new Uri("http://localhost/?" + query); // Act var bindingParser = new HttpRedirectBindingParser(url); // Assert Assert.True(coll.Count == 1); Assert.True(bindingParser.IsRequest); Assert.True(!bindingParser.IsResponse); Assert.True(!bindingParser.IsSigned); Assert.Equal(request, bindingParser.Message); }
public void TestParsing_02() { HttpRedirectBindingBuilder bindingBuilder = new HttpRedirectBindingBuilder(); string request = string.Empty.PadRight(140, 'l'); string relaystate = "A relaystate test. @@@!!!&&&///"; bindingBuilder.Request = request; bindingBuilder.RelayState = relaystate; string query = bindingBuilder.ToQuery(); NameValueCollection coll = QueryToNameValueCollection(query); Assert.AreEqual(2, coll.Count); Uri url = new Uri("http://localhost/?" + query); HttpRedirectBindingParser bindingParser = new HttpRedirectBindingParser(url); Assert.IsTrue(bindingParser.IsRequest); Assert.IsFalse(bindingParser.IsResponse); Assert.IsFalse(bindingParser.IsSigned); Assert.IsNotNull(bindingParser.RelayState); Assert.AreEqual(relaystate, bindingParser.RelayStateDecoded); Assert.AreEqual(request, bindingParser.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(); }
private void HandleRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleRequest()"); //Fetch config object SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); LogoutRequest logoutRequest = null; IDPEndPoint endpoint = null; string message = string.Empty; //Build the response object var response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; response.StatusCode = Saml20Constants.StatusCodes.Success; // Default success. Is overwritten if something fails. if(context.Request.RequestType == "GET") // HTTP Redirect binding { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, string.Format("Binding: redirect, Signature algorithm: {0} Signature: {1}, Message: {2}", parser.SignatureAlgorithm, parser.Signature, parser.Message)); if (!parser.IsSigned) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP: " + logoutRequest.Issuer.Value); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } Saml20MetadataDocument metadata = endpoint.metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature redirect-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; } else if (context.Request.RequestType == "POST") // HTTP Post binding { HttpPostBindingParser parser = new HttpPostBindingParser(context); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Binding: POST, Message: " + parser.Message); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } Saml20MetadataDocument metadata = endpoint.metadata; // handle a logout-request if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature post-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; }else { //Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS // Not able to return a response as we do not understand the request. HandleError(context, Resources.UnsupportedRequestTypeFormat(context.Request.RequestType)); } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); // Check that idp in session and request matches. string idpRequest = logoutRequest.Issuer.Value; if (!context.Session.IsNewSession) { object idpSession = context.Session[IDPLoginSessionKey]; if (idpSession != null && idpSession.ToString() != idpRequest) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, Resources.IdPMismatchBetweenRequestAndSessionFormat(idpSession, idpRequest), message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } } else { // All other status codes than Success results in the IdP throwing an error page. Therefore we return default Success even if we do not have a session. AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Session does not exist. Continues the redirect logout procedure with status code success." + idpRequest, message); } // Only logout if request is valid and we are working on an existing Session. if (Saml20Constants.StatusCodes.Success == response.StatusCode && !context.Session.IsNewSession) { // Execute all actions that the service provider has configured DoLogout(context, true); } // Update the response object with informations that first is available when request has been parsed. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); response.Destination = destination.Url; response.InResponseTo = logoutRequest.ID; //Respond using redirect binding if(destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.RelayState = context.Request.Params["RelayState"]; builder.Response = response.GetXml().OuterXml; builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; string s = destination.Url + "?" + builder.ToQuery(); context.Response.Redirect(s, true); return; } //Respond using post binding if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); builder.Action = SAMLAction.SAMLResponse; XmlDocument responseDocument = response.GetXml(); XmlSignatureUtils.SignDocument(responseDocument, response.ID); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; builder.GetPage().ProcessRequest(context); return; } }
private void TransferClient(IDPEndPoint endpoint, HttpContext context) { Trace.TraceMethodCalled(GetType(), "TransferClient()"); Saml20LogoutRequest request = Saml20LogoutRequest.GetDefault(); AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = endpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); request.Destination = destination.Url; string nameIdFormat = context.Session[IDPNameIdFormat].ToString(); request.SubjectToLogOut.Format = nameIdFormat; if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); XmlDocument requestDocument = request.GetXml(); XmlSignatureUtils.SignDocument(requestDocument, request.ID); builder.Request = requestDocument.OuterXml; if(Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "POST", endpoint.Id, requestDocument.OuterXml)); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Binding: POST"); builder.GetPage().ProcessRequest(context); context.Response.End(); return; } if(destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); builder.Request = request.GetXml().OuterXml; string redirectUrl = destination.Url + "?" + builder.ToQuery(); if (Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "REDIRECT", endpoint.Id, redirectUrl)); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Binding: Redirect"); context.Response.Redirect(redirectUrl, true); return; } if(destination.Binding == SAMLBinding.ARTIFACT) { if (Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "ARTIFACT", endpoint.Id, string.Empty)); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Method: Artifact"); builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N")); } HandleError(context, Resources.BindingError); }
/// <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) { // Set the last IDP we attempted to login at. StateService.Set(IdpTempSessionKey, identityProvider.Id); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.SignOnEndpoint, identityProvider.Metadata.SSOEndpoints); request.Destination = destination.Url; if (identityProvider.ForceAuth) { request.ForceAuthn = true; } // Check isPassive status var isPassiveFlag = StateService.Get <bool?>(IdpIsPassive); if (isPassiveFlag != null && (bool)isPassiveFlag) { request.IsPassive = true; StateService.Set(IdpIsPassive, null); } if (identityProvider.IsPassive) { request.IsPassive = true; } // Check if request should forceAuthn var forceAuthnFlag = StateService.Get <bool?>(IdpForceAuthn); if (forceAuthnFlag != null && (bool)forceAuthnFlag) { request.ForceAuthn = true; StateService.Set(IdpForceAuthn, null); } // Check if protocol binding should be forced if (identityProvider.SignOnEndpoint != null) { if (!string.IsNullOrEmpty(identityProvider.SignOnEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = identityProvider.SignOnEndpoint.ForceProtocolBinding; } } // Save request message id to session StateService.Set(ExpectedInResponseToSessionKey, 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 + (request.Destination.Contains("?") ? "&" : "?") + 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); postBuilder.Request = requestXml.OuterXml; Logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request); postBuilder.GetPage().ProcessRequest(context); break; case BindingType.Artifact: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact); var artifactBuilder = new HttpArtifactBindingBuilder(context); // 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); } }
private void TransferClient(IDPEndPoint idpEndpoint, Saml20AuthnRequest request, HttpContext context) { AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = idpEndpoint.Id; //Set the last IDP we attempted to login at. context.Session[IDPTempSessionKey] = idpEndpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SSOEndpoint, idpEndpoint.metadata.SSOEndpoints()); request.Destination = destination.Url; if (idpEndpoint.ForceAuthn) { request.ForceAuthn = true; } object isPassiveFlag = context.Session[IDPIsPassive]; if (isPassiveFlag != null && (bool)isPassiveFlag) { request.IsPassive = true; context.Session[IDPIsPassive] = null; } if (idpEndpoint.IsPassive) { request.IsPassive = true; } object forceAuthnFlag = context.Session[IDPForceAuthn]; if (forceAuthnFlag != null && (bool)forceAuthnFlag) { request.ForceAuthn = true; context.Session[IDPForceAuthn] = null; } if (idpEndpoint.SSOEndpoint != null) { if (!string.IsNullOrEmpty(idpEndpoint.SSOEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = idpEndpoint.SSOEndpoint.ForceProtocolBinding; } } //Save request message id to session context.Session.Add(ExpectedInResponseToSessionKey, request.ID); if (destination.Binding == SAMLBinding.REDIRECT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Redirect, idpEndpoint.Id)); HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = _certificate.PrivateKey; builder.Request = request.GetXml().OuterXml; string s = request.Destination + "?" + builder.ToQuery(); AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT, "Redirecting user to IdP for authentication", builder.Request); context.Response.Redirect(s, true); return; } if (destination.Binding == SAMLBinding.POST) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Post, idpEndpoint.Id)); HttpPostBindingBuilder builder = 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.HTTP_Post; } XmlDocument req = request.GetXml(); XmlSignatureUtils.SignDocument(req, request.ID); builder.Request = req.OuterXml; AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_POST); builder.GetPage().ProcessRequest(context); return; } if (destination.Binding == SAMLBinding.ARTIFACT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Artifact, idpEndpoint.Id)); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); //Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HTTP_Artifact; } AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT_ARTIFACT); builder.RedirectFromLogin(destination, request); } HandleError(context, Resources.BindingError); }
/// <summary> /// Transfers the client. /// </summary> /// <param name="idp">The identity provider.</param> /// <param name="context">The context.</param> private void TransferClient(IdentityProviderElement idp, HttpContext context) { var request = Saml20LogoutRequest.GetDefault(); // Determine which endpoint to use from the configuration file or the endpoint metadata. var destination = DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.LogoutEndpoint, idp.Metadata.IDPSLOEndpoints); request.Destination = destination.Url; var nameIdFormat = StateService.Get <string>(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 = StateService.Get <string>(IdpNameId); request.SessionIndex = StateService.Get <string>(IdpSessionIdKey); var requestDocument = request.GetXml(); XmlSignatureUtils.SignDocument(requestDocument, request.Id); builder.Request = requestDocument.OuterXml; Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "POST", builder.Request); builder.GetPage().ProcessRequest(context); context.Response.End(); return; } // Handle Redirect binding if (destination.Binding == BindingType.Redirect) { request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = StateService.Get <string>(IdpNameId); request.SessionIndex = StateService.Get <string>(IdpSessionIdKey); var builder = new HttpRedirectBindingBuilder { Request = request.GetXml().OuterXml, SigningKey = Saml2Config.GetConfig().ServiceProvider.SigningCertificate.GetCertificate().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 = StateService.Get <string>(IdpNameId); request.SessionIndex = StateService.Get <string>(IdpSessionIdKey); Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "ARTIFACT", request.GetXml().OuterXml); var builder = new HttpArtifactBindingBuilder(context); builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N")); } Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); }
private void TransferClient(IDPEndPoint idpEndpoint, Saml20AuthnRequest request, HttpContext context) { AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = idpEndpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SSOEndpoint, idpEndpoint.metadata.SSOEndpoints()); request.Destination = destination.Url; bool isPassive; string isPassiveAsString = context.Request.Params[IDPIsPassive]; if (bool.TryParse(isPassiveAsString, out isPassive)) { request.IsPassive = isPassive; } if (idpEndpoint.IsPassive) { request.IsPassive = true; } bool forceAuthn; string forceAuthnAsString = context.Request.Params[IDPForceAuthn]; if (bool.TryParse(forceAuthnAsString, out forceAuthn)) { request.ForceAuthn = forceAuthn; } if (idpEndpoint.ForceAuthn) { request.ForceAuthn = true; } if (idpEndpoint.SSOEndpoint != null) { if (!string.IsNullOrEmpty(idpEndpoint.SSOEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = idpEndpoint.SSOEndpoint.ForceProtocolBinding; } } //Save request message id to session SessionStore.CurrentSession[SessionConstants.ExpectedInResponseTo] = request.ID; var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(idpEndpoint.ShaHashingAlgorithm); if (destination.Binding == SAMLBinding.REDIRECT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Redirect, idpEndpoint.Id)); HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = _certificate.PrivateKey; builder.Request = request.GetXml().OuterXml; builder.ShaHashingAlgorithm = shaHashingAlgorithm; string s = request.Destination + "?" + builder.ToQuery(); AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT, "Redirecting user to IdP for authentication", builder.Request); context.Response.Redirect(s, true); return; } if (destination.Binding == SAMLBinding.POST) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Post, idpEndpoint.Id)); HttpPostBindingBuilder builder = 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.HTTP_Post; } XmlDocument req = request.GetXml(); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(req, request.ID, signingCertificate); builder.Request = req.OuterXml; AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_POST); builder.GetPage().ProcessRequest(context); return; } if (destination.Binding == SAMLBinding.ARTIFACT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Artifact, idpEndpoint.Id)); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); //Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HTTP_Artifact; } AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT_ARTIFACT); builder.RedirectFromLogin(idpEndpoint, destination, request); } HandleError(context, Resources.BindingError); }
private void HandleRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleRequest()"); //Fetch the endpoint configuration IDPEndPoint idpEndpoint = RetrieveIDPConfiguration(context.Session[IDPLoginSessionKey].ToString()); IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SLOEndpoint, idpEndpoint.metadata.SLOEndpoints()); //Fetch config object SAML20FederationConfig config = ConfigurationReader.GetConfig <SAML20FederationConfig>(); //Build the response object Saml20LogoutResponse response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; response.Destination = destination.Url; response.StatusCode = Saml20Constants.StatusCodes.Success; string message = string.Empty; if (context.Request.RequestType == "GET") // HTTP Redirect binding { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); IDPEndPoint endpoint = config.FindEndPoint(idpEndpoint.Id); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); HandleError(context, "Cannot find metadata for IdP " + idpEndpoint.Id); return; } Saml20MetadataDocument metadata = endpoint.metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Invalid signature redirect-binding, msg: " + parser.Message); HandleError(context, Resources.SignatureInvalid); return; } message = parser.Message; } else if (context.Request.RequestType == "POST") // HTTP Post binding { HttpPostBindingParser parser = new HttpPostBindingParser(context); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); HandleError(context, Resources.SignatureNotPresent); } IDPEndPoint endpoint = config.FindEndPoint(idpEndpoint.Id); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); HandleError(context, "Cannot find metadata for IdP " + idpEndpoint.Id); return; } Saml20MetadataDocument metadata = endpoint.metadata; // handle a logout-request if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Invalid signature post-binding, msg: " + parser.Message); HandleError(context, Resources.SignatureInvalid); } message = parser.Message; } else { //Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS HandleError(context, Resources.UnsupportedRequestTypeFormat(context.Request.RequestType)); } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); //Log the user out locally DoLogout(context, true); LogoutRequest req = Serialization.DeserializeFromXmlString <LogoutRequest>(message); response.InResponseTo = req.ID; //Respond using redirect binding if (destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.RelayState = context.Request.Params["RelayState"]; builder.Response = response.GetXml().OuterXml; builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; string s = destination.Url + "?" + builder.ToQuery(); context.Response.Redirect(s, true); return; } //Respond using post binding if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); builder.Action = SAMLAction.SAMLResponse; XmlDocument responseDocument = response.GetXml(); XmlSignatureUtils.SignDocument(responseDocument, response.ID); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; builder.GetPage().ProcessRequest(context); return; } }
/// <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) { IdentityProviderEndpoint destination = ConfigureRequest(identityProvider, request, context); 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 + (request.Destination.Contains("?") ? "&" : "?") + 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.PostSimpleSign: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPostSimpleSign); var postSimpleSignBuilder = 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.HttpPostSimpleSign; } var requestXmlSimpleSign = request.GetXml(); XmlSignatureUtils.SignDocument(requestXmlSimpleSign, request.Id, config.ServiceProvider.SigningCertificate); postSimpleSignBuilder.Request = requestXmlSimpleSign.OuterXml; Logger.DebugFormat(TraceMessages.AuthnRequestSent, postSimpleSignBuilder.Request); context.Response.Write(postSimpleSignBuilder.GetPage()); break; case BindingType.Artifact: Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact); var artifactBuilder = GetBuilder(context); // 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, context.Request.Params["relayState"], (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration)); break; default: Logger.Error(ErrorMessages.EndpointBindingInvalid); throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid); } }
private void TransferClient(IDPEndPoint idpEndpoint, Saml20AuthnRequest request, HttpContext context) { AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = idpEndpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SSOEndpoint, idpEndpoint.metadata.SSOEndpoints()); request.Destination = destination.Url; bool isPassive; string isPassiveAsString = context.Request.Params[IDPIsPassive]; if (bool.TryParse(isPassiveAsString, out isPassive)) { request.IsPassive = isPassive; } var requestContextItems = new List <(string value, ItemsChoiceType7 type)>(); if (!string.IsNullOrEmpty(context.Request.Params[NsisLoa])) { string demandedLevelOfAssurance = context.Request.Params[NsisLoa].ToString(); if (!new[] { "Low", "Substantial", "High" }.Contains(demandedLevelOfAssurance)) { HandleError(context, string.Format(Resources.DemandingLevelOfAssuranceError, demandedLevelOfAssurance)); return; } requestContextItems.Add((DKSaml20NsisLoaAttribute.NAME + "/" + demandedLevelOfAssurance, ItemsChoiceType7.AuthnContextClassRef)); // Persist demanded LoA in session to be able to verify assertion SessionStore.CurrentSession[SessionConstants.ExpectedNsisLoa] = demandedLevelOfAssurance; Trace.TraceData(TraceEventType.Information, string.Format(Tracing.DemandingLevelOfAssurance, demandedLevelOfAssurance)); } if (!string.IsNullOrEmpty(context.Request.Params[Profile])) { string demandedProfile = context.Request.Params[Profile].ToString(); if (!new[] { "Professional", "Person" }.Contains(demandedProfile)) { HandleError(context, string.Format(Resources.DemandingProfileError, demandedProfile)); return; } requestContextItems.Add(("https://data.gov.dk/eid/" + demandedProfile, ItemsChoiceType7.AuthnContextClassRef)); Trace.TraceData(TraceEventType.Information, string.Format(Tracing.DemandingProfile, demandedProfile)); } if (requestContextItems.Count > 0) { request.Request.RequestedAuthnContext = new RequestedAuthnContext(); request.Request.RequestedAuthnContext.Comparison = AuthnContextComparisonType.minimum; request.Request.RequestedAuthnContext.ItemsElementName = requestContextItems.Select(x => x.type).ToArray(); request.Request.RequestedAuthnContext.Items = requestContextItems.Select(x => x.value).ToArray(); } if (idpEndpoint.IsPassive) { request.IsPassive = true; } bool forceAuthn; string forceAuthnAsString = context.Request.Params[IDPForceAuthn]; if (bool.TryParse(forceAuthnAsString, out forceAuthn)) { request.ForceAuthn = forceAuthn; } if (idpEndpoint.ForceAuthn) { request.ForceAuthn = true; } if (idpEndpoint.SSOEndpoint != null) { if (!string.IsNullOrEmpty(idpEndpoint.SSOEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = idpEndpoint.SSOEndpoint.ForceProtocolBinding; } } //Save request message id to session SessionStore.CurrentSession[SessionConstants.ExpectedInResponseTo] = request.ID; var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(idpEndpoint.ShaHashingAlgorithm); if (destination.Binding == SAMLBinding.REDIRECT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Redirect, idpEndpoint.Id)); HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = _certificate.PrivateKey; builder.Request = request.GetXml().OuterXml; builder.ShaHashingAlgorithm = shaHashingAlgorithm; string s = request.Destination + "?" + builder.ToQuery(); AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT, "Redirecting user to IdP for authentication", builder.Request); context.Response.Redirect(s, true); return; } if (destination.Binding == SAMLBinding.POST) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Post, idpEndpoint.Id)); HttpPostBindingBuilder builder = 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.HTTP_Post; } XmlDocument req = request.GetXml(); var signingCertificate = FederationConfig.GetConfig().GetFirstValidCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(req, request.ID, signingCertificate); builder.Request = req.OuterXml; AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_POST); builder.GetPage().ProcessRequest(context); return; } if (destination.Binding == SAMLBinding.ARTIFACT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Artifact, idpEndpoint.Id)); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); //Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HTTP_Artifact; } AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT_ARTIFACT); builder.RedirectFromLogin(idpEndpoint, destination, request); } HandleError(context, Resources.BindingError); }
/// <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()); } }
private void HandleRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleRequest()"); //Fetch config object SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); LogoutRequest logoutRequest = null; IDPEndPoint endpoint = null; string message = string.Empty; //Build the response object var response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; response.StatusCode = Saml20Constants.StatusCodes.Success; // Default success. Is overwritten if something fails. if (context.Request.RequestType == "GET") // HTTP Redirect binding { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, string.Format("Binding: redirect, Signature algorithm: {0} Signature: {1}, Message: {2}", parser.SignatureAlgorithm, parser.Signature, parser.Message)); if (!parser.IsSigned) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP: " + logoutRequest.Issuer.Value); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } Saml20MetadataDocument metadata = endpoint.metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature redirect-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; } else if (context.Request.RequestType == "POST") // HTTP Post binding { HttpPostBindingParser parser = new HttpPostBindingParser(context); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Binding: POST, Message: " + parser.Message); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } if (logoutRequest.NotOnOrAfter.HasValue) { var allowedClockSkewTime = DateTime.UtcNow.AddMinutes(FederationConfig.GetConfig().AllowedClockSkewMinutes); if (logoutRequest.NotOnOrAfter >= allowedClockSkewTime) { var errormessage = $"Logout request NotOnOrAfter ({logoutRequest.NotOnOrAfter}) is after allowed time ({allowedClockSkewTime})"; AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, errormessage); HandleError(context, errormessage); return; } } Saml20MetadataDocument metadata = endpoint.metadata; // handle a logout-request if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature post-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; } else { //Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS // Not able to return a response as we do not understand the request. HandleError(context, string.Format(Resources.UnsupportedRequestType, context.Request.RequestType)); return; } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); // Check that idp in session and request matches. string idpRequest = logoutRequest.Issuer.Value; // SessionFactory.SessionContext.Current.New is never the first call to Current due to the logic in Application_AuthenticateRequest() ... Saml20Identity.IsInitialized() // Hence we need to check on Saml20Identity.IsInitialized() instead of using SessionFactory.SessionContext.Current.New. bool isOioSamlSessionActive = Saml20Identity.IsInitialized(); if (isOioSamlSessionActive) { object idpId = Saml20PrincipalCache.GetSaml20AssertionLite().Issuer; if (idpId != null && idpId.ToString() != idpRequest) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, string.Format(Resources.IdPMismatchBetweenRequestAndSession, idpId, idpRequest), message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } } else { // All other status codes than Success results in the IdP throwing an error page. Therefore we return default Success even if we do not have a session. AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Session does not exist. Continues the redirect logout procedure with status code success." + idpRequest, message); } // Only logout if request is valid and we are working on an existing Session. if (Saml20Constants.StatusCodes.Success == response.StatusCode && isOioSamlSessionActive) { // Execute all actions that the service provider has configured DoLogout(context, true); } // Update the response object with informations that first is available when request has been parsed. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); response.Destination = destination.Url; response.InResponseTo = logoutRequest.ID; //Respond using redirect binding var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(endpoint.ShaHashingAlgorithm); if (destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.RelayState = context.Request.Params["RelayState"]; builder.Response = response.GetXml().OuterXml; builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; builder.ShaHashingAlgorithm = shaHashingAlgorithm; string s = destination.Url + "?" + builder.ToQuery(); context.Response.Redirect(s, true); return; } //Respond using post binding if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); builder.Action = SAMLAction.SAMLResponse; XmlDocument responseDocument = response.GetXml(); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(responseDocument, response.ID, signingCertificate); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; builder.GetPage().ProcessRequest(context); return; } }
/// <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); }