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 void SecureKeyGenerator_CreateRelayState() { // Loop until we've seen the replacement work. var containedDash = false; var containedUnderscore = false; for (int i = 0; !containedDash || !containedUnderscore; i++) { i.Should().BeLessThan(1000, because: "if replacement works, we should have found the replacement characters sooner"); var result = SecureKeyGenerator.CreateRelayState(); // Can't really test a random algo any better than expecting a // specific length of the result and the right chars. result.Length.Should().Be(24); containedDash = containedDash || result.Contains("-"); containedUnderscore = containedUnderscore || result.Contains("_"); // Resulting string should not need to be URL encoded. result.Should().MatchRegex("^[A-Za-z0-9\\-_]*$"); } }
public async Task Saml2Handler_Acs_Works() { var context = new Saml2HandlerTestContext(); context.HttpContext.Request.Method = "POST"; context.HttpContext.Request.Path = "/Saml2/Acs"; var authProps = new AuthenticationProperties() { IssuedUtc = new DateTimeOffset(DateTime.UtcNow) }; authProps.Items["Test"] = "TestValue"; var state = new StoredRequestState( new EntityId("https://idp.example.com"), new Uri("https://localhost/LoggedIn"), new Saml2Id("InResponseToId"), authProps.Items); var relayState = SecureKeyGenerator.CreateRelayState(); var cookieData = HttpRequestData.ConvertBinaryData( StubDataProtector.Protect(state.Serialize())); var cookieName = $"Kentor.{relayState}"; context.HttpContext.Request.Cookies = new StubCookieCollection( Enumerable.Repeat(new KeyValuePair <string, string>( cookieName, cookieData), 1)); 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:AudienceRestriction> <saml2:Audience>http://sp.example.com/saml2</saml2:Audience> </saml2:AudienceRestriction> </saml2:Conditions> </saml2:Assertion> </saml2p:Response>"; var form = Substitute.For <IFormCollection>(); IEnumerator <KeyValuePair <string, StringValues> > formCollectionEnumerator = new KeyValuePair <string, StringValues>[] { new KeyValuePair <string, StringValues>( "SAMLResponse", new StringValues( Convert.ToBase64String( Encoding.UTF8.GetBytes(SignedXmlHelper.SignXml(response))))), new KeyValuePair <string, StringValues>( "RelayState", new StringValues(relayState)) }.AsEnumerable().GetEnumerator(); form.GetEnumerator().Returns(formCollectionEnumerator); context.HttpContext.Request.Form.Returns(form); var authService = Substitute.For <IAuthenticationService>(); context.HttpContext.RequestServices.GetService(typeof(IAuthenticationService)) .Returns(authService); ClaimsPrincipal principal = null; AuthenticationProperties actualAuthProps = null; await authService.SignInAsync( context.HttpContext, TestHelpers.defaultSignInScheme, Arg.Do <ClaimsPrincipal>(p => principal = p), Arg.Do <AuthenticationProperties>(ap => actualAuthProps = ap)); await context.Subject.HandleRequestAsync(); principal.HasClaim(ClaimTypes.NameIdentifier, "SomeUser").Should().BeTrue(); actualAuthProps.IssuedUtc.Should().Be(authProps.IssuedUtc); actualAuthProps.Items["Test"].Should().Be("TestValue"); context.HttpContext.Response.Headers["Location"].Single().Should().Be( state.ReturnUrl.OriginalString); context.HttpContext.Response.StatusCode.Should().Be(303); }
/// <summary> /// Default constructor /// </summary> public Saml2AuthenticationRequest() { RelayState = SecureKeyGenerator.CreateRelayState(); }
/// <summary> /// Ctor /// </summary> public Saml2LogoutRequest() { RelayState = SecureKeyGenerator.CreateRelayState(); }
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 = SecureKeyGenerator.CreateRelayState(); 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"); }
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); }