private void HandleRequest(SamlHttpContext 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, ResourcesEx.UnsupportedRequestTypeFormat(context.Request.RequestType)); } 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, ResourcesEx.IdPMismatchBetweenRequestAndSessionFormat(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 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) { throw new NotImplementedException("HttpPostBindingBuilder not ported"); //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 HandleResponse(SamlHttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleResponse()"); string message = string.Empty; if (context.Request.RequestType == "GET") { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); LogoutResponse response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Binding: redirect, Signature algorithm: {0} Signature: {1}, Message: {2}", parser.SignatureAlgorithm, parser.Signature, parser.Message)); IDPEndPoint idp = RetrieveIDPConfiguration(response.Issuer.Value); AuditLogging.IdpId = idp.Id; AuditLogging.AssertionId = response.ID; if (idp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("No IDP metadata, unknown IDP, response: {0}", parser.Message)); HandleError(context, Resources.UnknownIDP); return; } if (!parser.VerifySignature(idp.metadata.Keys)) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Invalid signature in redirect-binding, response: {0}", parser.Message)); HandleError(context, Resources.SignatureInvalid); return; } message = parser.Message; } else if (context.Request.RequestType == "POST") { HttpPostBindingParser parser = new HttpPostBindingParser(context); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, "Binding: POST, Message: " + parser.Message); LogoutResponse response = Serialization.DeserializeFromXmlString <LogoutResponse>(parser.Message); IDPEndPoint idp = RetrieveIDPConfiguration(response.Issuer.Value); if (idp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("No IDP metadata, unknown IDP, response: {0}", parser.Message)); HandleError(context, Resources.UnknownIDP); return; } if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Signature not present, response: {0}", parser.Message)); HandleError(context, Resources.SignatureNotPresent); } // signature on final message in logout if (!parser.CheckSignature(idp.metadata.Keys)) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Invalid signature in post-binding, response: {0}", parser.Message)); HandleError(context, Resources.SignatureInvalid); } message = parser.Message; } else { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Unsupported request type format, type: {0}", context.Request.RequestType)); HandleError(context, ResourcesEx.UnsupportedRequestTypeFormat(context.Request.RequestType)); } XmlDocument doc = new XmlDocument(); doc.XmlResolver = null; doc.PreserveWhitespace = true; doc.LoadXml(message); XmlElement statElem = (XmlElement)doc.GetElementsByTagName(Status.ELEMENT_NAME, Saml20Constants.PROTOCOL)[0]; Status status = Serialization.DeserializeFromXmlString <Status>(statElem.OuterXml); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Unexpected status code: {0}, msg: {1}", status.StatusCode.Value, message)); HandleError(context, status); return; } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, "Assertion validated succesfully"); //Log the user out locally DoLogout(context); }