private static CommandResult ProcessResponse( IOptions options, Saml2Response samlResponse, StoredRequestState storedRequestState) { var principal = new ClaimsPrincipal(samlResponse.GetClaims(options, storedRequestState?.RelayData)); if (options.SPOptions.ReturnUrl == null) { if (storedRequestState == null) { throw new ConfigurationErrorsException(UnsolicitedMissingReturnUrlMessage); } if (storedRequestState.ReturnUrl == null) { throw new ConfigurationErrorsException(SpInitiatedMissingReturnUrl); } } options.SPOptions.Logger.WriteInformation("Successfully processed SAML response " + samlResponse.Id + " and authenticated " + principal.FindFirst(ClaimTypes.NameIdentifier)?.Value); return(new CommandResult() { HttpStatusCode = HttpStatusCode.SeeOther, Location = storedRequestState?.ReturnUrl ?? options.SPOptions.ReturnUrl, Principal = principal, RelayData = storedRequestState?.RelayData, SessionNotOnOrAfter = samlResponse.SessionNotOnOrAfter }); }
public void HttpRequestData_Ctor_Deserialize_StoredRequestState() { var url = new Uri("http://example.com:42/ApplicationPath/Path?RelayState=Foo"); string appPath = "/ApplicationPath"; var storedRequestData = new StoredRequestState( new EntityId("http://idp.example.com"), new Uri("http://sp.example.com/loggedout"), new Saml2Id("id123"), null); var cookies = new KeyValuePair <string, string>[] { new KeyValuePair <string, string>( "Kentor.Foo", HttpRequestData.ConvertBinaryData( StubDataProtector.Protect(storedRequestData.Serialize()))) }; var subject = new HttpRequestData( "GET", url, appPath, Enumerable.Empty <KeyValuePair <string, string[]> >(), cookies, StubDataProtector.Unprotect); subject.StoredRequestState.ShouldBeEquivalentTo(storedRequestData); }
private void Init( string httpMethod, Uri url, string applicationPath, IEnumerable <KeyValuePair <string, IEnumerable <string> > > formData, Func <string, string> cookieReader, Func <byte[], byte[]> cookieDecryptor, ClaimsPrincipal user) { InitBasicFields(httpMethod, url, applicationPath, formData); User = user; var relayState = QueryString["RelayState"].SingleOrDefault(); if (relayState == null) { Form.TryGetValue("RelayState", out relayState); } RelayState = relayState; if (relayState != null) { var cookieName = StoredRequestState.CookieNameBase + relayState; var cookieData = cookieReader(cookieName); if (!string.IsNullOrEmpty(cookieData)) { byte[] encryptedData = GetBinaryData(cookieData); var decryptedData = cookieDecryptor(encryptedData); StoredRequestState = new StoredRequestState(decryptedData); } } }
public async static Task <HttpRequestData> ToHttpRequestData(this IOwinContext context) { if (context == null) { return(null); } IFormCollection formData = null; if (context.Request.Body != null) { formData = await context.Request.ReadFormAsync(); } var applicationRootPath = context.Request.PathBase.Value; if (string.IsNullOrEmpty(applicationRootPath)) { applicationRootPath = "/"; } StoredRequestState storedRequestState = null; // TODO: implement storage for the request state return(new HttpRequestData( context.Request.Method, context.Request.Uri, applicationRootPath, formData, storedRequestState)); }
private static XmlElement ResolveArtifact( string artifact, StoredRequestState storedRequestState, IOptions options) { var binaryArtifact = Convert.FromBase64String(artifact); var idp = GetIdp(binaryArtifact, storedRequestState, options); var arsIndex = (binaryArtifact[2] << 8) | binaryArtifact[3]; var arsUri = idp.ArtifactResolutionServiceUrls[arsIndex]; var payload = new Saml2ArtifactResolve { Artifact = artifact, Issuer = options.SPOptions.EntityId }.ToXml(); var signingServiceCertificate = options.SPOptions.SigningServiceCertificate; var resolver = options.SPOptions.ArtifactResolver; options.SPOptions.Logger.WriteVerbose("Calling idp " + idp.EntityId.Id + " to resolve artifact\n" + artifact); var response = Saml2SoapBinding.SendSoapRequest(payload, arsUri, signingServiceCertificate, resolver); options.SPOptions.Logger.WriteVerbose("Artifact resolved returned\n" + response); return(new Saml2ArtifactResponse(response).GetMessage()); }
private void ValidateInResponseTo(IOptions options) { if (InResponseTo == null) { if (options.IdentityProviders[Issuer].AllowUnsolicitedAuthnResponse) { return; } string msg = string.Format(CultureInfo.InvariantCulture, "Unsolicited responses are not allowed for idp \"{0}\".", Issuer.Id); throw new Saml2ResponseFailedValidationException(msg); } else { StoredRequestState storedRequestState; bool knownInResponseToId = PendingAuthnRequests.TryRemove(InResponseTo, out storedRequestState); if (!knownInResponseToId) { string msg = string.Format(CultureInfo.InvariantCulture, "Replayed or unknown InResponseTo \"{0}\".", InResponseTo); throw new Saml2ResponseFailedValidationException(msg); } requestState = storedRequestState; if (requestState.Idp.Id != Issuer.Id) { var msg = string.Format(CultureInfo.InvariantCulture, "Expected response from idp \"{0}\" but received response from idp \"{1}\".", requestState.Idp.Id, issuer.Id); throw new Saml2ResponseFailedValidationException(msg); } } }
private static XmlElement ResolveArtifact( string artifact, StoredRequestState storedRequestState, IOptions options) { var binaryArtifact = Convert.FromBase64String(artifact); var idp = GetIdp(binaryArtifact, storedRequestState, options); var arsIndex = (binaryArtifact[2] << 8) | binaryArtifact[3]; var arsUri = idp.ArtifactResolutionServiceUrls[arsIndex]; var payload = new Saml2ArtifactResolve() { Artifact = artifact, Issuer = options.SPOptions.EntityId }.ToXml(); if (options.SPOptions.SigningServiceCertificate != null) { var xmlDoc = XmlHelpers.XmlDocumentFromString(payload); xmlDoc.Sign(options.SPOptions.SigningServiceCertificate, true); payload = xmlDoc.OuterXml; } options.SPOptions.Logger.WriteVerbose("Calling idp " + idp.EntityId.Id + " to resolve artifact\n" + artifact); var clientCertificates = options.SPOptions.ServiceCertificates .Where(sc => sc.Use.HasFlag(CertificateUse.TlsClient) && sc.Status == CertificateStatus.Current) .Select(sc => sc.Certificate); var response = Saml2SoapBinding.SendSoapRequest(payload, arsUri, clientCertificates); options.SPOptions.Logger.WriteVerbose("Artifact resolved returned\n" + response); return(new Saml2ArtifactResponse(response).GetMessage()); }
private static XmlElement ResolveArtifact( string artifact, StoredRequestState storedRequestState, IOptions options) { var binaryArtifact = Convert.FromBase64String(artifact); var idp = GetIdp(binaryArtifact, storedRequestState, options); var arsIndex = (binaryArtifact[2] << 8) | binaryArtifact[3]; var arsUri = idp.ArtifactResolutionServiceUrls[arsIndex]; var payload = new Saml2ArtifactResolve() { Artifact = artifact, Issuer = options.SPOptions.EntityId }.ToXml(); if (options.SPOptions.SigningServiceCertificate != null) { var xmlDoc = new XmlDocument() { PreserveWhitespace = true }; xmlDoc.LoadXml(payload); xmlDoc.Sign(options.SPOptions.SigningServiceCertificate, true); payload = xmlDoc.OuterXml; } var response = Saml2SoapBinding.SendSoapRequest(payload, arsUri); return(new Saml2ArtifactResponse(response).GetMessage()); }
public async Task Saml2Handler_RedirectsToDefaultIdpOnChallenge() { var context = new Saml2HandlerTestContext(); var authProps = new AuthenticationProperties() { IssuedUtc = new DateTimeOffset(new DateTime(2017, 09, 30)), RedirectUri = "https://sp.example.com/LoggedIn" }; var response = context.HttpContext.Response; string cookieData = null; response.Cookies.Append( Arg.Any <string>(), Arg.Do <string>(v => cookieData = v), Arg.Any <CookieOptions>()); await context.Subject.ChallengeAsync(authProps); response.StatusCode.Should().Be(303); response.Headers["Location"].Single() .Should().StartWith("https://idp.example.com/sso?SAMLRequest="); var state = new StoredRequestState(StubDataProtector.Unprotect( HttpRequestData.GetBinaryData(cookieData))); state.ReturnUrl.OriginalString.Should().Be("https://sp.example.com/LoggedIn"); // Don't dual-store the return-url. state.RelayData.Values.Should().NotContain("https://sp.example.com/LoggedIn"); }
private static CommandResult HandleResponse(UnbindResult unbindResult, StoredRequestState storedRequestState, IOptions options, Uri returnUrl) { var logoutResponse = Saml2LogoutResponse.FromXml(unbindResult.Data); var notificationHandledTheStatus = options.Notifications.ProcessSingleLogoutResponseStatus(logoutResponse, storedRequestState); if (!notificationHandledTheStatus) { var status = logoutResponse.Status; if (status != Saml2StatusCode.Success) { throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture, "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.", status)); } } var commandResult = new CommandResult { HttpStatusCode = HttpStatusCode.SeeOther }; if (!options.SPOptions.Compatibility.DisableLogoutStateCookie) { commandResult.ClearCookieName = StoredRequestState.CookieNameBase + unbindResult.RelayState; } commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl; options.SPOptions.Logger.WriteInformation("Received logout response " + logoutResponse.Id + ", redirecting to " + commandResult.Location); return(commandResult); }
public void HttpRequestData_Ctor_Deserialize_StoredRequestState() { var url = new Uri("http://example.com:42/ApplicationPath/Path?RelayState=Foo"); string appPath = "/ApplicationPath"; var storedRequestData = new StoredRequestState( new EntityId("http://idp.example.com"), new Uri("http://sp.example.com/loggedout"), new Saml2Id("id123"), null); var cookies = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "Kentor.Foo", HttpRequestData.ConvertBinaryData( StubDataProtector.Protect(storedRequestData.Serialize()))) }; var subject = new HttpRequestData( "GET", url, appPath, Enumerable.Empty<KeyValuePair<string, string[]>>(), cookies, StubDataProtector.Unprotect); subject.StoredRequestState.ShouldBeEquivalentTo(storedRequestData); }
private IEnumerable <ClaimsIdentity> CreateClaims(StoredRequestState storedRequestState, IOptions options) { Validate(storedRequestState, options); if (status != Saml2StatusCode.Success) { throw new InvalidOperationException("The Saml2Response must have status success to extract claims."); } foreach (XmlElement assertionNode in AllAssertionElementNodes) { using (var reader = new FilteringXmlNodeReader(SignedXml.XmlDsigNamespaceUrl, "Signature", assertionNode)) { var handler = options.SPOptions.Saml2PSecurityTokenHandler; var token = (Saml2SecurityToken)handler.ReadToken(reader); handler.DetectReplayedToken(token); var validateAudience = token.Assertion.Conditions.AudienceRestrictions.Count > 0; handler.ValidateConditions(token.Assertion.Conditions, validateAudience); yield return(handler.CreateClaims(token)); } } }
private void Init( string httpMethod, Uri url, string applicationPath, IEnumerable <KeyValuePair <string, string[]> > formData, IEnumerable <KeyValuePair <string, string> > cookies, Func <byte[], byte[]> cookieDecryptor, ClaimsPrincipal user) { InitBasicFields(httpMethod, url, applicationPath, formData); User = user; var relayState = QueryString["RelayState"].SingleOrDefault(); if (relayState == null) { Form.TryGetValue("RelayState", out relayState); } if (relayState != null) { var cookieName = "Kentor." + relayState; if (cookies.Any(c => c.Key == cookieName)) { var cookieData = cookies.SingleOrDefault(c => c.Key == cookieName).Value; byte[] encryptedData = GetBinaryData(cookieData); var decryptedData = cookieDecryptor(encryptedData); StoredRequestState = new StoredRequestState(decryptedData); } } }
private void Validate(StoredRequestState storedRequestState, IOptions options) { if (!validated) { try { ValidateInResponseTo(storedRequestState, options); ValidateSignature(options); } catch (Saml2ResponseFailedValidationException ex) { validationException = ex; throw; } finally { validated = true; } } else { if (validationException != null) { throw validationException; } } }
private static CommandResult ProcessResponse( IOptions options, Saml2Response samlResponse, StoredRequestState storedRequestState) { var principal = new ClaimsPrincipal(samlResponse.GetClaims(options)); principal = options.SPOptions.SystemIdentityModelIdentityConfiguration .ClaimsAuthenticationManager.Authenticate(null, principal); if (options.SPOptions.ReturnUrl == null) { if (storedRequestState == null) { throw new ConfigurationErrorsException(UnsolicitedMissingReturnUrlMessage); } if (storedRequestState.ReturnUrl == null) { throw new ConfigurationErrorsException(SpInitiatedMissingReturnUrl); } } return(new CommandResult() { HttpStatusCode = HttpStatusCode.SeeOther, Location = storedRequestState == null?null:storedRequestState.ReturnUrl ?? options.SPOptions.ReturnUrl, Principal = principal, RelayData = storedRequestState == null?null:storedRequestState.RelayData }); }
public async Task Saml2Handler_ChallengeAsync_UsesCurrentUrlAsReturnUrlIfAuthPropsAreMissing() { var context = new Saml2HandlerTestContext(); var response = context.HttpContext.Response; string cookieData = null; response.Cookies.Append( Arg.Any <string>(), Arg.Do <string>(v => cookieData = v), Arg.Any <CookieOptions>()); await context.Subject.ChallengeAsync(null); response.StatusCode.Should().Be(303); response.Headers["Location"].Single() .Should().StartWith("https://idp.example.com/sso?SAMLRequest="); var state = new StoredRequestState(StubDataProtector.Unprotect( HttpRequestData.GetBinaryData(cookieData))); state.ReturnUrl.OriginalString.Should().Be("https://sp.example.com/somePath?param=value"); // Don't dual-store the return-url. state.RelayData.Values.Should().NotContain("https://sp.example.com/somePath?param=value"); }
private static Uri GetLocation(StoredRequestState storedRequestState, IdentityProvider identityProvider, string relayState, IOptions options) { // When SP-Initiated if (storedRequestState != null) { return(storedRequestState.ReturnUrl ?? options.SPOptions.ReturnUrl); } else { //When IDP-Initiated if (identityProvider.RelayStateUsedAsReturnUrl) { if (!PathHelper.IsLocalWebUrl(relayState)) { if (!options.Notifications.ValidateAbsoluteReturnUrl(relayState)) { throw new InvalidOperationException("Return Url must be a relative Url."); } } return(new Uri(relayState, UriKind.RelativeOrAbsolute)); } } return(options.SPOptions.ReturnUrl); }
private static CommandResult RedirectToDiscoveryService( string returnPath, SPOptions spOptions, Saml2Urls saml2Urls, IDictionary <string, string> relayData) { string returnUrl = saml2Urls.SignInUrl.OriginalString; var relayState = SecureKeyGenerator.CreateRelayState(); returnUrl += "?RelayState=" + Uri.EscapeDataString(relayState); var redirectLocation = string.Format( CultureInfo.InvariantCulture, "{0}?entityID={1}&return={2}&returnIDParam=idp", spOptions.DiscoveryServiceUrl, Uri.EscapeDataString(spOptions.EntityId.Id), Uri.EscapeDataString(returnUrl)); var requestState = new StoredRequestState( null, returnPath == null ? null : new Uri(returnPath, UriKind.RelativeOrAbsolute), null, relayData); return(new CommandResult() { HttpStatusCode = HttpStatusCode.SeeOther, Location = new Uri(redirectLocation), RequestState = requestState, SetCookieName = StoredRequestState.CookieNameBase + relayState }); }
public HttpRequestData( string httpMethod, Uri url, string applicationPath, IEnumerable <KeyValuePair <string, string[]> > formData, StoredRequestState storedRequestState) { Init(httpMethod, url, applicationPath, formData, storedRequestState); }
public void PendingAuthnRequests_Add_ThrowsOnExisting() { var id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx")); PendingAuthnRequests.Add(id, requestData); Action a = () => PendingAuthnRequests.Add(id, requestData); a.ShouldThrow<InvalidOperationException>(); }
public void PendingAuthnRequests_Remove_FalseOnRemovedTwice() { var id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx")); StoredRequestState responseData; PendingAuthnRequests.Add(id, requestData); PendingAuthnRequests.TryRemove(id, out responseData).Should().BeTrue(); PendingAuthnRequests.TryRemove(id, out responseData).Should().BeFalse(); }
// Used by tests. internal HttpRequestData( string httpMethod, Uri url, string applicationPath, IEnumerable <KeyValuePair <string, string[]> > formData, StoredRequestState storedRequestState) { InitBasicFields(httpMethod, url, applicationPath, formData); StoredRequestState = storedRequestState; }
public void PendingAuthnRequests_Add_ThrowsOnExisting() { var id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx")); PendingAuthnRequests.Add(id, requestData); Action a = () => PendingAuthnRequests.Add(id, requestData); a.ShouldThrow <InvalidOperationException>(); }
public void PendingAuthnRequests_Remove_FalseOnRemovedTwice() { var id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx")); StoredRequestState responseData; PendingAuthnRequests.Add(id, requestData); PendingAuthnRequests.TryRemove(id, out responseData).Should().BeTrue(); PendingAuthnRequests.TryRemove(id, out responseData).Should().BeFalse(); }
// Used by tests. internal HttpRequestData( string httpMethod, Uri url, string applicationPath, IEnumerable<KeyValuePair<string, string[]>> formData, StoredRequestState storedRequestState) { InitBasicFields(httpMethod, url, applicationPath, formData); StoredRequestState = storedRequestState; }
private void PrepareArtifactState(string RelayState, EntityId idp) { var storedState = new StoredRequestState( idp, new Uri("http://return.org"), new Saml2Id(), null); PendingAuthnRequests.Add(RelayState, storedState); }
/// <summary> /// Stores the StoredRequestState using the Saml2Id /// </summary> /// <param name="id"></param> /// <param name="idp"></param> public void Add(Saml2Id id, StoredRequestState idp) { lock (PendingAuthnInMemoryContainer.Container) { if (PendingAuthnInMemoryContainer.Container.ContainsKey(id)) { throw new InvalidOperationException("AuthnRequest id can't be reused."); } PendingAuthnInMemoryContainer.Container.Add(id, idp); } }
//private static readonly Dictionary<Saml2Id, StoredRequestState> pendingAuthnRequest = new Dictionary<Saml2Id, StoredRequestState>(); internal static void Add(Saml2Id id, StoredRequestState idp) { lock (pendingRequestCache) { if (pendingRequestCache.Contains(id.Value)) { throw new InvalidOperationException("AuthnRequest id can't be reused."); } pendingRequestCache.Add(id.Value, idp, new CacheItemPolicy { }); } }
private static byte[] Serialize(StoredRequestState obj) { using (var stream = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, obj); stream.Flush(); return(stream.ToArray()); } }
/// <summary> /// Stores the StoredRequestState using the Saml2Id /// </summary> /// <param name="id">The Saml2Id used as key to store the pending authentication data.</param> /// <param name="idp">The state of the pending authentication request.</param> public void Add(Saml2Id id, StoredRequestState idp) { lock (container) { if (container.ContainsKey(id)) { throw new InvalidOperationException("AuthnRequest id can't be reused."); } container.Add(id, idp); } }
internal static void Add(Saml2Id id, StoredRequestState idp) { lock (pendingAuthnRequest) { if (pendingAuthnRequest.ContainsKey(id)) { throw new InvalidOperationException("AuthnRequest id can't be reused."); } pendingAuthnRequest.Add(id, idp); } }
internal static void Add(string id, StoredRequestState idp) { lock (pendingAuthnRequest) { if (pendingAuthnRequest.ContainsKey(id)) { throw new InvalidOperationException("AuthnRequest id can't be reused."); } pendingAuthnRequest.Add(id, idp); } }
public void PendingAuthnInMemoryStorage_AddRemove() { var id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx")); var container = new PendingAuthnInMemoryStorage(); container.Add(id, requestData); StoredRequestState responseData; container.TryRemove(id, out responseData).Should().BeTrue(); responseData.Should().Be(requestData); responseData.Idp.Id.Should().Be("testidp"); responseData.ReturnUrl.Should().Be("http://localhost/Return.aspx"); }
/// <summary> /// Returns the Stored Request State while simultaneously deleting it from storage. /// </summary> /// <param name="id"></param> /// <param name="idp"></param> /// <returns></returns> public bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (PendingAuthnInMemoryContainer.Container) { if (id != null && PendingAuthnInMemoryContainer.Container.ContainsKey(id)) { idp = PendingAuthnInMemoryContainer.Container[id]; return(PendingAuthnInMemoryContainer.Container.Remove(id)); } idp = null; return(false); } }
/// <summary> /// Returns the Stored Request State while simultaneously deleting it from storage. /// </summary> /// <param name="id">The Saml2Id key used to retrieve the pending authentication data.</param> /// <param name="idp">The state of the pending authentication request.</param> /// <returns></returns> public bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (container) { if (id != null && container.ContainsKey(id)) { idp = container[id]; return container.Remove(id); } idp = null; return false; } }
public void PendingAuthnRequests_AddRemove() { var relayState = RelayStateGenerator.CreateSecureKey(); var saml2Id = new Saml2Id(); var requestData = new StoredRequestState(new EntityId("testidp"), new Uri("http://localhost/Return.aspx"), saml2Id); PendingAuthnRequests.Add(relayState, requestData); StoredRequestState responseData; PendingAuthnRequests.TryRemove(relayState, out responseData).Should().BeTrue(); responseData.Should().Be(requestData); responseData.Idp.Id.Should().Be("testidp"); responseData.ReturnUrl.Should().Be("http://localhost/Return.aspx"); responseData.MessageId.Should().Be(saml2Id); }
public void PendingAuthnRequests_Remove_FalseOnRemovedTwice() { var relayState = RelayStateGenerator.CreateSecureKey(); var requestData = new StoredRequestState( new EntityId("testidp"), new Uri("http://localhost/Return.aspx"), new Saml2Id()); StoredRequestState responseData; PendingAuthnRequests.Add(relayState, requestData); PendingAuthnRequests.TryRemove(relayState, out responseData).Should().BeTrue(); PendingAuthnRequests.TryRemove(relayState, out responseData).Should().BeFalse(); }
internal static bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (pendingAuthnRequest) { if (id != null && pendingAuthnRequest.ContainsKey(id)) { idp = pendingAuthnRequest[id]; return(pendingAuthnRequest.Remove(id)); } idp = null; return(false); } }
/// <summary> /// Returns the Stored Request State while simultaneously deleting it from storage. /// </summary> /// <param name="id"></param> /// <param name="idp"></param> /// <returns></returns> public bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (PendingAuthnInMemoryContainer.Container) { if (id != null && PendingAuthnInMemoryContainer.Container.ContainsKey(id)) { idp = PendingAuthnInMemoryContainer.Container[id]; return PendingAuthnInMemoryContainer.Container.Remove(id); } idp = null; return false; } }
internal static bool TryRemove(string id, out StoredRequestState state) { lock (pendingAuthnRequest) { if (id != null && pendingAuthnRequest.ContainsKey(id)) { state = pendingAuthnRequest[id]; return(pendingAuthnRequest.Remove(id)); } state = null; return(false); } }
internal static bool TryRemove(string id, out StoredRequestState state) { lock (pendingAuthnRequest) { if (id != null && pendingAuthnRequest.ContainsKey(id)) { state = pendingAuthnRequest[id]; return pendingAuthnRequest.Remove(id); } state = null; return false; } }
public void PendingAuthnRequests_AddRemove() { var id = new Saml2Id(); var requestData = new StoredRequestState("testidp", new Uri("http://localhost/Return.aspx")); PendingAuthnRequests.Add(id, requestData); StoredRequestState responseData; PendingAuthnRequests.TryRemove(id, out responseData).Should().BeTrue(); responseData.Should().Be(requestData); responseData.Idp.Should().Be("testidp"); responseData.ReturnUri.Should().Be("http://localhost/Return.aspx"); }
internal static bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (pendingAuthnRequest) { if (id != null && pendingAuthnRequest.ContainsKey(id)) { idp = pendingAuthnRequest[id]; return pendingAuthnRequest.Remove(id); } idp = null; return false; } }
internal static bool TryRemove(Saml2Id id, out StoredRequestState idp) { lock (pendingRequestCache) { if (id != null && pendingRequestCache.Contains(id.Value)) { idp = pendingRequestCache[id.Value] as StoredRequestState; pendingRequestCache.Remove(id.Value); return true; } idp = null; return false; } }
public void PendingAuthnRequests_Remove_FalseOnRemovedTwice() { var relayState = RelayStateGenerator.CreateSecureKey(); var requestData = new StoredRequestState( new EntityId("testidp"), new Uri("http://localhost/Return.aspx"), new Saml2Id()); StoredRequestState responseData; PendingAuthnRequests.Add(relayState, requestData); PendingAuthnRequests.TryRemove(relayState, out responseData).Should().BeTrue(); PendingAuthnRequests.TryRemove(relayState, out responseData).Should().BeFalse(); }
public async Task OwinContextExtensionsTests_ToHttpRequestData_ReadsRelayStateCookie() { var ctx = OwinTestHelpers.CreateOwinContext(); ctx.Request.QueryString = new QueryString("RelayState", "SomeState"); var storedRequestState = new StoredRequestState( null, new Uri("http://sp.example.com"), null, null); var cookieData = HttpRequestData.ConvertBinaryData( StubDataProtector.Protect(storedRequestState.Serialize())); ctx.Request.Headers["Cookie"] = $"Kentor.SomeState={cookieData}"; var actual = await ctx.ToHttpRequestData(StubDataProtector.Unprotect); actual.StoredRequestState.ShouldBeEquivalentTo(storedRequestState); }
private static CommandResult ProcessResponse( IOptions options, Saml2Response samlResponse, StoredRequestState storedRequestState) { var principal = new ClaimsPrincipal(samlResponse.GetClaims(options)); principal = options.SPOptions.SystemIdentityModelIdentityConfiguration .ClaimsAuthenticationManager.Authenticate(null, principal); if(options.SPOptions.ReturnUrl == null) { if (storedRequestState == null) { throw new ConfigurationErrorsException(UnsolicitedMissingReturnUrlMessage); } if(storedRequestState.ReturnUrl == null) { throw new ConfigurationErrorsException(SpInitiatedMissingReturnUrl); } } return new CommandResult() { HttpStatusCode = HttpStatusCode.SeeOther, Location = storedRequestState?.ReturnUrl ?? options.SPOptions.ReturnUrl, Principal = principal, RelayData = storedRequestState?.RelayData, SessionNotOnOrAfter = samlResponse.SessionNotOnOrAfter }; }
private void ValidateInResponseTo(IOptions options) { if (InResponseTo == null) { if (options.IdentityProviders[Issuer].AllowUnsolicitedAuthnResponse) { return; } string msg = string.Format(CultureInfo.InvariantCulture, "Unsolicited responses are not allowed for idp \"{0}\".", Issuer.Id); throw new Saml2ResponseFailedValidationException(msg); } else { StoredRequestState storedRequestState; bool knownInResponseToId = PendingAuthnRequests.TryRemove(InResponseTo, out storedRequestState); if (!knownInResponseToId) { string msg = string.Format(CultureInfo.InvariantCulture, "Replayed or unknown InResponseTo \"{0}\".", InResponseTo); throw new Saml2ResponseFailedValidationException(msg); } requestState = storedRequestState; if (requestState.Idp.Id != Issuer.Id) { var msg = string.Format(CultureInfo.InvariantCulture, "Expected response from idp \"{0}\" but received response from idp \"{1}\".", requestState.Idp.Id, issuer.Id); throw new Saml2ResponseFailedValidationException(msg); } } }
private void Init( string httpMethod, Uri url, string applicationPath, IEnumerable<KeyValuePair<string, string[]>> formData, IEnumerable<KeyValuePair<string, string>> cookies, Func<byte[], byte[]> cookieDecryptor, ClaimsPrincipal user) { InitBasicFields(httpMethod, url, applicationPath, formData); User = user; var relayState = QueryString["RelayState"].SingleOrDefault(); if(relayState == null) { Form.TryGetValue("RelayState", out relayState); } RelayState = relayState; if (relayState != null) { var cookieName = "Kentor." + relayState; if (cookies.Any(c => c.Key == cookieName)) { var cookieData = cookies.SingleOrDefault(c => c.Key == cookieName).Value; byte[] encryptedData = GetBinaryData(cookieData); var decryptedData = cookieDecryptor(encryptedData); StoredRequestState = new StoredRequestState(decryptedData); } } }
private static CommandResult HandleResponse(UnbindResult unbindResult, StoredRequestState storedRequestState, IOptions options, Uri returnUrl) { var status = Saml2LogoutResponse.FromXml(unbindResult.Data).Status; if(status != Saml2StatusCode.Success) { throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture, "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.", status)); } var commandResult = new CommandResult { HttpStatusCode = HttpStatusCode.SeeOther }; if (!options.SPOptions.Compatibility.DisableLogoutStateCookie) { commandResult.ClearCookieName = "Kentor." + unbindResult.RelayState; } commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl; return commandResult; }
public async Task KentorAuthServicesAuthenticationMiddleware_CreatesRedirectOnAuthRevoke_UsesAuthPropsReturnUrl() { var authPropsReturnUrl = "http://sp.exmample.com/AuthPropsLogout"; var revoke = new AuthenticationResponseRevoke( new string[0], new AuthenticationProperties { RedirectUri = authPropsReturnUrl }); var options = new KentorAuthServicesAuthenticationOptions(true); options.SPOptions.PublicOrigin = new Uri("https://sp.example.com/ExternalPath/"); var subject = new KentorAuthServicesAuthenticationMiddleware( new StubOwinMiddleware(303, revoke: revoke), CreateAppBuilder(), options); var context = OwinTestHelpers.CreateOwinContext(); context.Response.Headers["Location"] = "http://sp.example.com/locationHeader"; context.Request.User = new ClaimsPrincipal( new ClaimsIdentity(new Claim[] { new Claim(AuthServicesClaimTypes.LogoutNameIdentifier, ",,,,NameId", null, "https://idp.example.com"), new Claim(AuthServicesClaimTypes.SessionIndex, "SessionId", null, "https://idp.example.com") }, "Federation")); await subject.Invoke(context); var cookieValue = context.Response.Headers["Set-Cookie"].Split(';', '=')[1]; var returnUrl = new StoredRequestState(options.DataProtector.Unprotect( HttpRequestData.GetBinaryData(cookieValue))).ReturnUrl; returnUrl.Should().Be(authPropsReturnUrl); }
private async Task KentorAuthServicesAuthenticationMiddleware_CreatesRedirectOnAuthRevoke_PreservesRedirect( string location, string expectedUrl, string path = "/Account/LogOut") { var revoke = new AuthenticationResponseRevoke(new string[0]); var options = new KentorAuthServicesAuthenticationOptions(true); ((SPOptions)options.SPOptions).PublicOrigin = new Uri("https://sp.example.com/ExternalPath/"); var subject = new KentorAuthServicesAuthenticationMiddleware( new StubOwinMiddleware(303, revoke: revoke), CreateAppBuilder(), options); var context = OwinTestHelpers.CreateOwinContext(); context.Request.Scheme = "http"; context.Request.Host = new HostString("sp-internal.example.com"); context.Request.PathBase = new PathString("/AppPath"); context.Request.Path = new PathString(path); context.Response.Headers["Location"] = location; Thread.CurrentPrincipal = new ClaimsPrincipal( new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.NameIdentifier, "NameId", null, "https://idp.example.com"), new Claim(AuthServicesClaimTypes.SessionIndex, "SessionId", null, "https://idp.example.com") }, "Federation")); await subject.Invoke(context); var cookieValue = context.Response.Headers["Set-Cookie"].Split(';', '=')[1] .Replace('_', '/').Replace('-', '+').Replace('.', '='); var returnUrl = new StoredRequestState(options.DataProtector.Unprotect( Convert.FromBase64String(cookieValue))).ReturnUrl; returnUrl.Should().Be(expectedUrl); }
public async Task KentorAuthServicesAuthenticationMiddleware_AcsWorks() { var context = OwinTestHelpers.CreateOwinContext(); context.Request.Method = "POST"; var authProps = new AuthenticationProperties() { IssuedUtc = new DateTime(1975, 05, 05, 05, 05, 05, DateTimeKind.Utc) }; authProps.Dictionary["Test"] = "TestValue"; var state = new StoredRequestState(new EntityId("https://idp.example.com"), new Uri("http://localhost/LoggedIn"), new Saml2Id("InResponseToId"), authProps.Dictionary); var relayState = SecureKeyGenerator.CreateRelayState(); var cookieData = HttpRequestData.ConvertBinaryData( CreateAppBuilder().CreateDataProtector( typeof(KentorAuthServicesAuthenticationMiddleware).FullName) .Protect(state.Serialize())); context.Request.Headers["Cookie"] = $"Kentor.{relayState}={cookieData}"; var response = @"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID = """ + MethodBase.GetCurrentMethod().Name + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo=""InResponseToId"" > <saml2:Issuer> https://idp.example.com </saml2:Issuer> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" /> </saml2p:Status> <saml2:Assertion Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; var bodyData = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("SAMLResponse", Convert.ToBase64String(Encoding.UTF8.GetBytes(SignedXmlHelper.SignXml(response)))), new KeyValuePair<string, string>("RelayState",relayState) }; var encodedBodyData = new FormUrlEncodedContent(bodyData); context.Request.Body = encodedBodyData.ReadAsStreamAsync().Result; context.Request.ContentType = encodedBodyData.Headers.ContentType.ToString(); context.Request.Host = new HostString("localhost"); context.Request.Path = new PathString("/AuthServices/Acs"); var signInAsAuthenticationType = "AuthType"; var ids = new ClaimsIdentity[] { new ClaimsIdentity(signInAsAuthenticationType), new ClaimsIdentity(signInAsAuthenticationType) }; ids[0].AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp.example.com")); ids[1].AddClaim(new Claim(ClaimTypes.Role, "RoleFromClaimsAuthManager", null, "ClaimsAuthenticationManagerStub")); var subject = new KentorAuthServicesAuthenticationMiddleware(null, CreateAppBuilder(), StubFactory.CreateOwinOptions()); await subject.Invoke(context); context.Response.StatusCode.Should().Be(303); context.Response.Headers["Location"].Should().Be("http://localhost/LoggedIn"); context.Response.Headers["Set-Cookie"].Should().Be($"Kentor.{relayState}=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"); context.Authentication.AuthenticationResponseGrant.Principal.Identities .ShouldBeEquivalentTo(ids, opt => opt.IgnoringCyclicReferences()); context.Authentication.AuthenticationResponseGrant.Properties.RedirectUri .Should().Be("http://localhost/LoggedIn", "the StoredRequestState.ReturnUrl should overtake the value in the AuthProperties and be stored in the AuthProps"); context.Authentication.AuthenticationResponseGrant.Properties.Dictionary["Test"] .Should().Be("TestValue"); context.Authentication.AuthenticationResponseGrant.Properties.IssuedUtc .Should().Be(authProps.IssuedUtc); }
public async Task KentorAuthServicesAuthenticationMiddleware_AcsWorks() { var context = OwinTestHelpers.CreateOwinContext(); context.Request.Method = "POST"; var state = new StoredRequestState(new EntityId("https://idp.example.com"), new Uri("http://localhost/LoggedIn"), new Saml2Id(MethodBase.GetCurrentMethod().Name + "RequestID"), new AuthenticationProperties()); ((AuthenticationProperties)state.RelayData).RedirectUri = state.ReturnUrl.OriginalString; ((AuthenticationProperties)state.RelayData).Dictionary["Test"] = "TestValue"; var relayState = RelayStateGenerator.CreateSecureKey(); PendingAuthnRequests.Add(relayState, state); var response = @"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID = """ + MethodBase.GetCurrentMethod().Name + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo=""" + MethodBase.GetCurrentMethod().Name + @"RequestID"" > <saml2:Issuer> https://idp.example.com </saml2:Issuer> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" /> </saml2p:Status> <saml2:Assertion Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; var bodyData = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("SAMLResponse", Convert.ToBase64String(Encoding.UTF8.GetBytes(SignedXmlHelper.SignXml(response)))), new KeyValuePair<string, string>("RelayState",relayState) }; var encodedBodyData = new FormUrlEncodedContent(bodyData); context.Request.Body = encodedBodyData.ReadAsStreamAsync().Result; context.Request.ContentType = encodedBodyData.Headers.ContentType.ToString(); context.Request.Host = new HostString("localhost"); context.Request.Path = new PathString("/AuthServices/Acs"); var signInAsAuthenticationType = "AuthType"; var ids = new ClaimsIdentity[] { new ClaimsIdentity(signInAsAuthenticationType), new ClaimsIdentity(signInAsAuthenticationType) }; ids[0].AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp.example.com")); ids[1].AddClaim(new Claim(ClaimTypes.Role, "RoleFromClaimsAuthManager", null, "ClaimsAuthenticationManagerStub")); var middleware = new KentorAuthServicesAuthenticationMiddleware(null, CreateAppBuilder(), StubFactory.CreateOwinOptions()); await middleware.Invoke(context); context.Response.StatusCode.Should().Be(302); context.Response.Headers["Location"].Should().Be("http://localhost/LoggedIn"); context.Authentication.AuthenticationResponseGrant.Principal.Identities .ShouldBeEquivalentTo(ids, opt => opt.IgnoringCyclicReferences()); context.Authentication.AuthenticationResponseGrant.Properties.RedirectUri .Should().Be("http://localhost/LoggedIn"); context.Authentication.AuthenticationResponseGrant.Properties.Dictionary["Test"] .Should().Be("TestValue"); }
private void TestSerializeDeserialize(StoredRequestState subject) { var actual = new StoredRequestState(subject.Serialize()); actual.ShouldBeEquivalentTo(subject); }
private void PrepareArtifactState(string RelayState, EntityId idp) { var storedState = new StoredRequestState( idp, new Uri("http://return.org"), new Saml2Id(), null); PendingAuthnRequests.Add(RelayState, storedState); }
internal static void Add(string id, StoredRequestState state) { ((IDictionary<string, StoredRequestState>)pendingAuthnRequest).Add(id, state); }
internal static bool TryRemove(string id, out StoredRequestState state) { state = null; return pendingAuthnRequest.TryRemove(id, out state); }