public void AcsCommand_Run_WithReturnUrl_SuccessfulResult()
        {
            var idp = Options.FromConfiguration.IdentityProviders.Default;

            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 + @""" InResponseTo = ""InResponseToId"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
                <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 + @"_Assertion2""
                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 responseFormValue = Convert.ToBase64String
                                        (Encoding.UTF8.GetBytes(SignedXmlHelper.SignXml(response)));
            var relayStateFormValue = "rs1234";

            var r = new HttpRequestData(
                "POST",
                new Uri("http://localhost"),
                "/ModulePath",
                new KeyValuePair <string, IEnumerable <string> >[]
            {
                new KeyValuePair <string, IEnumerable <string> >("SAMLResponse", new string[] { responseFormValue }),
                new KeyValuePair <string, IEnumerable <string> >("RelayState", new string[] { relayStateFormValue })
            },
                new StoredRequestState(
                    new EntityId("https://idp.example.com"),
                    new Uri("http://localhost/testUrl.aspx"),
                    new Saml2Id("InResponseToId"),
                    null)
                );

            var ids = new ClaimsIdentity[] { new ClaimsIdentity("Federation"), new ClaimsIdentity("ClaimsAuthenticationManager") };

            ids[0].AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp.example.com"));
            ids[1].AddClaim(new Claim(ClaimTypes.Role, "RoleFromClaimsAuthManager", null, "ClaimsAuthenticationManagerStub"));

            var expected = new CommandResult()
            {
                Principal       = new ClaimsPrincipal(ids),
                HttpStatusCode  = HttpStatusCode.SeeOther,
                Location        = new Uri("http://localhost/testUrl.aspx"),
                ClearCookieName = "Kentor." + relayStateFormValue
            };

            new AcsCommand().Run(r, StubFactory.CreateOptions())
            .ShouldBeEquivalentTo(expected, opt => opt.IgnoringCyclicReferences());
        }
示例#2
0
 public void Cleanup()
 {
     SignedXmlHelper.RemoveGlobalSha256XmlSignatureSupport();
 }
示例#3
0
        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 = $"{StoredRequestState.CookieNameBase}{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);
        }
示例#4
0
        public async Task KentorAuthServicesAuthenticationMiddleware_UsesCommandResultLocation()
        {
            // For Owin middleware, the redirect uri is part of the
            // authentication properties, but we don't want to use it as it
            // is because it can be empty (e.g. on unsolicited responses
            // or until #182 is fixed). The redirect uri should be taken
            // from the commandresult location instead.

            var context = OwinTestHelpers.CreateOwinContext();

            context.Request.Method = "POST";

            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"">
                <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))))
            };

            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 middleware = new KentorAuthServicesAuthenticationMiddleware(null, CreateAppBuilder(),
                                                                            new KentorAuthServicesAuthenticationOptions(true)
            {
                SignInAsAuthenticationType = "AuthType"
            });

            await middleware.Invoke(context);

            context.Response.StatusCode.Should().Be(302);
            context.Response.Headers["Location"].Should().Be("http://localhost/LoggedIn");
        }
示例#5
0
        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");
        }
示例#6
0
        public void AcsCommand_Run_CallsNotifications()
        {
            var messageId = MethodBase.GetCurrentMethod().Name;
            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 = """ + messageId + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
                <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=""" + messageId + @"_Assertion""
                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 formValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(
                                                       SignedXmlHelper.SignXml(response)));

            var requestData = new HttpRequestData(
                "POST",
                new Uri("http://localhost"),
                "/ModulePath",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLResponse", new string[] { formValue })
            },
                null);

            var options = StubFactory.CreateOptions();

            var responseUnboundCalled = false;

            options.Notifications.MessageUnbound = ur =>
            {
                ur.Should().NotBeNull();
                responseUnboundCalled = true;
            };

            CommandResult notifiedCommandResult = null;

            options.Notifications.AcsCommandResultCreated = (cr, r) =>
            {
                notifiedCommandResult = cr;
                r.Id.Value.Should().Be(messageId);
            };

            new AcsCommand().Run(requestData, options)
            .Should().BeSameAs(notifiedCommandResult);

            responseUnboundCalled.Should().BeTrue("the ResponseUnbound notification should have been called.");
        }
示例#7
0
        static IDictionary <string, string> GetContent()
        {
            var content = new Dictionary <string, string>();

            content["/idpMetadata"] = string.Format(
                @"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
    entityID=""http://*****:*****@"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
    entityID=""http://*****:*****@"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
    entityID=""http://other.entityid.example.com"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol""
      WantAuthnRequestsSigned=""true"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://wrong.entityid.example.com/acs""/>
    </IDPSSODescriptor>
  </EntityDescriptor>", SignedXmlHelper.KeyInfoXml);

            content["/federationMetadata"] = string.Format(
                @"<EntitiesDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" validUntil=""2100-01-01T14:43:15Z"">
  <EntityDescriptor entityID=""http://idp.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp.federation.example.com/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://sp.federation.example.com/metadata"">
    <SPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <AssertionConsumerService index=""0""
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST""
        Location=""http://sp.federation.example.com/acs"" />
    </SPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>
", SignedXmlHelper.KeyInfoXml);

            var federationMetadataSigned = string.Format(
                @"<EntitiesDescriptor ID=""federationMetadataSigned"" xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" validUntil=""2100-01-01T14:43:15Z"">
  <EntityDescriptor entityID=""http://idp.signedfederation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp.signedfederation.example.com/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>
", SignedXmlHelper.KeyInfoXml);

            content["/federationMetadataSigned"] =
                SignedXmlHelper.SignXml(federationMetadataSigned);

            var federationMetadataSignedTampered = string.Format(
                @"<EntitiesDescriptor ID=""federationMetadataSignedTampered"" xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" validUntil=""2100-01-01T14:43:15Z"">
  <EntityDescriptor entityID=""http://idp.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp.federation.example.com/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>
", SignedXmlHelper.KeyInfoXml);

            federationMetadataSignedTampered = SignedXmlHelper.SignXml(federationMetadataSignedTampered);

            content["/federationMetadataSignedTampered"] =
                federationMetadataSignedTampered.Replace("ssoService", "tampered");

            if (IdpAndFederationShortCacheDurationAvailable)
            {
                content["/federationMetadataVeryShortCacheDuration"] = string.Format(
                    @"<EntitiesDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" cacheDuration=""PT0.001S"">
  <EntityDescriptor entityID=""http://idp1.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp1.federation.example.com:{1}/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://idp2.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp2.federation.example.com:{1}/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://sp.federation.example.com/metadata"">
    <SPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <AssertionConsumerService index=""0""
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST""
        Location=""http://sp.federation.example.com/acs"" />
    </SPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>",
                    SignedXmlHelper.KeyInfoXml,
                    IdpAndFederationVeryShortCacheDurationPort);
            }

            if (FederationVeryShortCacheDurationSecondAlternativeEnabled)
            {
                content["/federationMetadataVeryShortCacheDuration"] = string.Format(
                    @"<EntitiesDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" cacheDuration=""PT0.001S"">
  <EntityDescriptor entityID=""http://idp1.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp1.federation.example.com:{1}/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://idp3.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp3.federation.example.com:{1}/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://sp.federation.example.com/metadata"">
    <SPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <AssertionConsumerService index=""0""
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST""
        Location=""http://sp.federation.example.com/acs"" />
    </SPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>",
                    SignedXmlHelper.KeyInfoXml,
                    IdpAndFederationVeryShortCacheDurationPort);
            }

            if (IdpAndFederationShortCacheDurationAvailable)
            {
                content["/federationMetadataShortCacheDuration"] = string.Format(
                    @"<EntitiesDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata"" cacheDuration=""PT0.500S"">
  <EntityDescriptor entityID=""http://idp1.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp1.federation.example.com/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://idp2.federation.example.com/metadata"">
    <IDPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <KeyDescriptor use=""signing"">
        {0}
      </KeyDescriptor>
      <SingleSignOnService
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
        Location=""http://idp2.federation.example.com/ssoService"" />
    </IDPSSODescriptor>
  </EntityDescriptor>
  <EntityDescriptor entityID=""http://sp.federation.example.com/metadata"">
    <SPSSODescriptor
      protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
      <AssertionConsumerService index=""0""
        Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST""
        Location=""http://sp.federation.example.com/acs"" />
    </SPSSODescriptor>
  </EntityDescriptor>
</EntitiesDescriptor>",
                    SignedXmlHelper.KeyInfoXml);
            }

            content["/idpMetadataWithMultipleBindings"] = string.Format(
                @"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
  entityID=""http://*****:*****@"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
  entityID=""some-idp"">
  <IDPSSODescriptor
    protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
    <KeyDescriptor use=""signing"">
      {0}
    </KeyDescriptor>
    <SingleSignOnService
      Binding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect""
      Location=""http://idp.example.com/SsoService"" />
  </IDPSSODescriptor>
</EntityDescriptor>", SignedXmlHelper.KeyInfoXml);

            content["/idpMetadataWithArtifactBinding"] = string.Format(
                @"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
  entityID=""http://*****:*****@"<KeyDescriptor use=""signing"">{0}</KeyDescriptor>",
                                                  IdpVeryShortCacheDurationIncludeInvalidKey ? "Gibberish" : SignedXmlHelper.KeyInfoXml2)
                    : "";

                content["/idpMetadataVeryShortCacheDuration"] = string.Format(
                    @"<EntityDescriptor xmlns=""urn:oasis:names:tc:SAML:2.0:metadata""
entityID=""http://localhost:13428/idpMetadataVeryShortCacheDuration"" cacheDuration=""PT0.001S"">
<IDPSSODescriptor
    protocolSupportEnumeration=""urn:oasis:names:tc:SAML:2.0:protocol"">
    {0}
    <SingleSignOnService
      Binding=""{1}""
      Location=""http://localhost:{2}/acs""/>
    <ArtifactResolutionService
      index=""0""
      Location=""http://localhost:{2}/ars""
      Binding=""urn:oasis:names:tc:SAML:2.0:bindings:SOAP"" />
      <SingleLogoutService
        Binding=""{1}""
        Location=""http://localhost:{2}/logout""
        ResponseLocation=""http://localhost:{2}/logoutResponse""/>
</IDPSSODescriptor>
</EntityDescriptor>",
                    keyElement, IdpVeryShortCacheDurationBinding, IdpAndFederationVeryShortCacheDurationPort);
            }

            return(content);
        }
示例#8
0
        public void Saml2PostBinding_Bind_SignsXmlAndPreserversXmlDeclaration()
        {
            var message = new Saml2MessageImplementation
            {
                DestinationUrl     = new Uri("http://www.example.com/acs"),
                XmlData            = "<root ID=\"id\">\r\n  <content>data</content>\r\n</root>",
                MessageName        = "SAMLMessageName",
                RelayState         = "ABC1234",
                SigningCertificate = SignedXmlHelper.TestCert,
                SigningAlgorithm   = SecurityAlgorithms.RsaSha256Signature
            };

            var signedXml = "<?xml version=\"1.0\" encoding=\"blaha\"?>\r\n"
                            + SignedXmlHelper.SignXml(message.XmlData, true);
            var expectedValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedXml));

            var notificationCalled = false;
            var result             = Saml2Binding.Get(Saml2BindingType.HttpPost).Bind(message, null, (m, xd, bt) =>
            {
                xd.Declaration = new XDeclaration("1.0", "blaha", null);
                m.Should().BeSameAs(message);
                bt.Should().Be(Saml2BindingType.HttpPost);
                notificationCalled = true;
            });

            var expected = new CommandResult()
            {
                ContentType = "text/html",
                Content     = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.1//EN""
""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"">
<html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"">
<head>
<meta http-equiv=""Content-Security-Policy"" content=""script-src 'sha256-H3SVZBYrbqBt3ncrT/nNmOb6nwCjC12cPQzh5jnW4Y0='"">
</head>
<body>
<noscript>
<p>
<strong>Note:</strong> Since your browser does not support JavaScript, 
you must press the Continue button once to proceed.
</p>
</noscript>
<form action=""http://www.example.com/acs"" method=""post"" name=""sustainsysSamlPostBindingSubmit"">
<div>
<input type=""hidden"" name=""RelayState"" value=""ABC1234""/>
<input type=""hidden"" name=""SAMLMessageName""
value=""" + expectedValue + @"""/>
</div>
<noscript>
<div>
<input type=""submit"" value=""Continue""/>
</div>
</noscript>
</form>
<script type=""text/javascript"">
document.forms.sustainsysSamlPostBindingSubmit.submit();
</script>
</body>
</html>"
            };

            result.Should().BeEquivalentTo(expected);
            notificationCalled.Should().BeTrue();
        }
示例#9
0
        public void AcsCommand_Run_UsesIdpFromNotification()
        {
            var messageId = MethodBase.GetCurrentMethod().Name;
            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 = ""{messageId}"" Version=""2.0"" InResponseTo=""InResponseToID"" IssueInstant=""2013-01-01T00:00:00Z"">
                <saml2:Issuer>
                    https://other.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=""{messageId}_Assertion""
                IssueInstant=""2013-09-25T00:00:00Z"">
                    <saml2:Issuer>https://other.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:AuthnStatement AuthnInstant=""{DateTime.UtcNow.ToSaml2DateTimeString()}"">
                        <saml2:AuthnContext>
                            <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
                        </saml2:AuthnContext>
                    </saml2:AuthnStatement>
                </saml2:Assertion>
            </saml2p:Response>";

            var formValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(
                                                       SignedXmlHelper.SignXml(response)));

            var relayData = new Dictionary <string, string>
            {
                { "key", "value" }
            };

            var requestData = new HttpRequestData(
                "POST",
                new Uri("http://localhost"),
                "/ModulePath",
                new KeyValuePair <string, IEnumerable <string> >[]
            {
                new KeyValuePair <string, IEnumerable <string> >("SAMLResponse", new string[] { formValue })
            },
                new StoredRequestState(
                    new EntityId("https://other.idp.example.com"),
                    new Uri("http://localhost/testUrl.aspx"),
                    new Saml2Id("InResponseToID"),
                    relayData));

            var options = StubFactory.CreateOptions();

            options.Notifications.GetIdentityProvider = (idpEntityId, rd, opt) =>
            {
                idpEntityId.Id.Should().Be("https://other.idp.example.com");
                rd["key"].Should().Be("value");

                var idp = new IdentityProvider(new EntityId("https://other.idp.example.com"), options.SPOptions);

                idp.SigningKeys.AddConfiguredKey(SignedXmlHelper.TestCert);

                return(idp);
            };

            var subject = new AcsCommand();
            var actual  = subject.Run(requestData, options);

            actual.Principal.Claims.First().Issuer.Should().Be("https://other.idp.example.com");
        }
示例#10
0
        public void Saml2Controller_Acs_Works()
        {
            var request = Substitute.For <HttpRequestBase>();

            request.HttpMethod.Returns("POST");

            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 formValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(
                                                       SignedXmlHelper.SignXml(response)));

            var relayState = "rs1234";

            request.Form.Returns(new NameValueCollection()
            {
                { "SAMLResponse", formValue },
                { "RelayState", relayState }
            });
            request.Url.Returns(new Uri("http://url.example.com/url"));
            request.Cookies.Returns(new HttpCookieCollection());
            request.Cookies.Add(new HttpCookie(StoredRequestState.CookieNameBase + relayState,
                                               HttpRequestData.ConvertBinaryData(
                                                   MachineKey.Protect(
                                                       new StoredRequestState(null, null, new Saml2Id("InResponseToId"), null).Serialize(),
                                                       HttpRequestBaseExtensions.ProtectionPurpose))));

            var httpContext = Substitute.For <HttpContextBase>();

            httpContext.Request.Returns(request);

            var controller = new Saml2Controller();

            controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);

            var expected = new
            {
                Permanent = false,
                Url       = Saml2Controller.Options.SPOptions.ReturnUrl.OriginalString
            };

            controller.Acs().As <RedirectResult>().ShouldBeEquivalentTo(expected);

            controller.Response.Received().SetCookie(
                Arg.Is <HttpCookie>(c => c.Expires.Year == 1970));
        }
示例#11
0
        private void RelayStateAsReturnUrl(string relayState, IOptions options, [CallerMemberName] string caller = null)
        {
            if (string.IsNullOrEmpty(caller))
            {
                throw new ArgumentNullException(nameof(caller));
            }

            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 = """ + caller + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
                <saml2:Issuer>
                    https://idp5.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=""" + caller + @"_Assertion""
                IssueInstant=""2013-09-25T00:00:00Z"">
                    <saml2:Issuer>https://idp5.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 responseFormValue = Convert.ToBase64String
                                        (Encoding.UTF8.GetBytes(SignedXmlHelper.SignXml(response)));

            var formData = new List <KeyValuePair <string, IEnumerable <string> > >
            {
                new KeyValuePair <string, IEnumerable <string> >("SAMLResponse", new string[] { responseFormValue }),
            };

            if (relayState != null)
            {
                formData.Add(new KeyValuePair <string, IEnumerable <string> >("RelayState", new string[] { relayState }));
            }

            var r = new HttpRequestData(
                "POST",
                new Uri("http://localhost"),
                "/ModulePath",
                formData,
                null);

            var ids = new ClaimsIdentity[] { new ClaimsIdentity("Federation") };

            ids[0].AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp5.example.com"));

            var expected = new CommandResult()
            {
                Principal      = new ClaimsPrincipal(ids),
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location       = relayState != null ? new Uri(relayState, UriKind.RelativeOrAbsolute) : null,
            };

            new AcsCommand().Run(r, options)
            .Location.OriginalString.Should().Be(relayState);
        }
示例#12
0
        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);
        }