public void Saml2LogoutResponse_AppendTo_AllSupported()
        {
            var subject = new Saml2LogoutResponse(Saml2StatusCode.Requester)
            {
                Issuer = new EntityId("https://ServiceProvider.com/SAML"),
                DestinationUrl = new Uri("https://IdentityProvider.com/Logout"),
                InResponseTo = new Saml2Id()
            };
            
            var expectedXml =
                $@"<samlp:LogoutResponse xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                    xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                    ID=""{subject.Id}""
                    InResponseTo=""{subject.InResponseTo.Value}""
                    IssueInstant=""{subject.IssueInstant.ToSaml2DateTimeString()}"" Version=""2.0""
                    Destination=""https://IdentityProvider.com/Logout"">
                    <Issuer>https://ServiceProvider.com/SAML</Issuer>
                    <samlp:Status>
                        <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester""/>
                    </samlp:Status>
                </samlp:LogoutResponse>";

            var expectedElement = XmlHelpers.FromString(expectedXml).DocumentElement;

            var xmlDoc = new XmlDocument();
            subject.AppendTo(xmlDoc);

            xmlDoc.DocumentElement.Should().BeEquivalentTo(expectedElement, "XML should be full LogoutResponse");
        }
        public void Saml2LogoutResponse_ToXml()
        {
            var subject = new Saml2LogoutResponse(Saml2StatusCode.Requester)
            {
                Issuer = new EntityId("https://ServiceProvider.com/SAML"),
                DestinationUrl = new Uri("https://IdentityProvider.com/Logout"),
                InResponseTo = new Saml2Id()
            };

            var xmlDoc = new XmlDocument();
            subject.AppendTo(xmlDoc);
            var expected = xmlDoc.OuterXml;

            subject.ToXml().Should().Be(expected);
        }
        public void Saml2LogoutResponse_AppendTo_Minimal()
        {
            var subject = new Saml2LogoutResponse(Saml2StatusCode.Success);

            var expectedXml =
                $@"<samlp:LogoutResponse 
                        xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                        xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                        ID=""{subject.Id}""
                        Version=""2.0""
                        IssueInstant=""{subject.IssueInstant.ToSaml2DateTimeString()}"">
                        <samlp:Status>
                            <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success""/>
                        </samlp:Status>
                    </samlp:LogoutResponse>";

            var expectedNode = XmlHelpers.FromString(expectedXml).DocumentElement;

            var xmlDoc = new XmlDocument();

            subject.AppendTo(xmlDoc);

            xmlDoc.DocumentElement.Should().BeEquivalentTo(expectedNode, "XML should be a valid LogoutResponse");
        }
Beispiel #4
0
        private static CommandResult HandleRequest(UnbindResult unbindResult, IOptions options)
        {
            var request = Saml2LogoutRequest.FromXml(unbindResult.Data);

            var idp = options.IdentityProviders[request.Issuer];

            if(options.SPOptions.SigningServiceCertificate == null)
            {
                throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture,
                    "Received a LogoutRequest from \"{0}\" but cannot reply because single logout responses must be signed and there is no signing certificate configured. Looks like the idp is configured for Single Logout despite AuthServices not exposing that functionality in the metadata.",
                    request.Issuer.Id));
            }

            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = idp.SingleLogoutServiceResponseUrl,
                SigningCertificate = options.SPOptions.SigningServiceCertificate,
                InResponseTo = request.Id,
                Issuer = options.SPOptions.EntityId,
                RelayState = unbindResult.RelayState
            };

            var result = Saml2Binding.Get(idp.SingleLogoutServiceBinding).Bind(response);
            result.TerminateLocalSession = true;
            return result;
        }
        public async Task KentorAuthServicesAuthenticationMiddleware_HandlesLogoutResponse()
        {
            var app = CreateAppBuilder();
            var options = new KentorAuthServicesAuthenticationOptions(true);
            var subject = new KentorAuthServicesAuthenticationMiddleware(
                null,
                app,
                options);

            var context = OwinTestHelpers.CreateOwinContext();

            var relayState = "MyRelayState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("https://sp.example.com/AuthServices/Logout"),
                RelayState = relayState,
                SigningCertificate = SignedXmlHelper.TestCert,
                Issuer = new EntityId("https://idp.example.com")
            };
            var requestUri = Saml2Binding.Get(Saml2BindingType.HttpRedirect).Bind(response).Location;

            var cookieData = HttpRequestData.EscapeBase64CookieValue(
                Convert.ToBase64String(
                    options.DataProtector.Protect(
                        Encoding.UTF8.GetBytes("http://loggedout.example.com/"))));
            context.Request.Headers["Cookie"] = $"Kentor.{relayState}={cookieData}";
            context.Request.Path = new PathString(requestUri.AbsolutePath);
            context.Request.QueryString = new QueryString(requestUri.Query.TrimStart('?'));
            
            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);

            context.Response.StatusCode.Should().Be(303);
            context.Response.Headers["Location"].Should().Be("http://loggedout.example.com/");
        }
        public void LogoutCommand_Run_ThrowsOnLogoutResponseStatusNonSuccess()
        {
            var response = new Saml2LogoutResponse(Saml2StatusCode.Requester)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(response);

            var request = new HttpRequestData("GET", bindResult.Location,
                "http://sp-internal.example.com/path/AuthServices", null, null, null);

            var options = StubFactory.CreateOptions();
            options.SPOptions.PublicOrigin = new Uri("https://sp.example.com/path/");

            CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Invoking(c => c.Run(request, options))
                .ShouldThrow<UnsuccessfulSamlOperationException>()
                .And.Message.Should().Be("Idp returned status \"Requester\", indicating that the single logout failed. The local session has been successfully terminated.");
        }
        public void LogoutCommand_Run_HandlesLogoutResponse_InPost()
        {
            var relayState = "TestState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var xml = XmlHelpers.FromString(response.ToXml());
            xml.Sign(SignedXmlHelper.TestCert);

            var responseData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair<string, string[]>[]
                {
                    new KeyValuePair<string, string[]>("SAMLResponse", new[] { responseData }),
                    new KeyValuePair<string, string[]>("RelayState", new[] { relayState })
                },
                Enumerable.Empty<KeyValuePair<string, string>>(),
                null);

            httpRequest.StoredRequestState = new StoredRequestState(null, new Uri("http://loggedout.example.com"), null, null);

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Run(httpRequest, options);

            var expected = new CommandResult
            {
                Location = new Uri("http://loggedout.example.com"),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
        public void LogoutCommand_Run_HandlesLogoutResponse()
        {
            var relayState = "MyRelayState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert,
                RelayState = relayState
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(response);

            var request = new HttpRequestData("GET",
                bindResult.Location,
                "http://sp-internal.example.com/path/AuthServices",
                null,
                new StoredRequestState(null, new Uri("http://loggedout.example.com"), null, null));

            var options = StubFactory.CreateOptions();
            options.SPOptions.PublicOrigin = new Uri("https://sp.example.com/path/");

            CommandResult notifiedCommandResult = null;
            options.Notifications.LogoutCommandResultCreated = cr =>
            {
                notifiedCommandResult = cr;
            };

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Run(request, options);

            actual.Should().BeSameAs(notifiedCommandResult);

            var expected = new CommandResult
            {
                Location = new Uri("http://loggedout.example.com"),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
        public void LogoutCommand_Run_HandlesLogoutResponse()
        {
            var relayState = "MyRelayState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert,
                RelayState = relayState
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(response);

            var cookies = new KeyValuePair<string, string>[] 
            {
                new KeyValuePair<string, string>(
                    "Kentor." + relayState,
                    StubDataProtector.Protect("http://loggedout.example.com", false))
            };

            var request = new HttpRequestData("GET",
                bindResult.Location,
                "http://sp-internal.example.com/path/AuthServices",
                null,
                cookies,
                StubDataProtector.Unprotect);
            
            var options = StubFactory.CreateOptions();
            ((SPOptions)options.SPOptions).PublicOrigin = new Uri("https://sp.example.com/path/");

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Run(request, options);

            var expected = new CommandResult
            {
                Location = new Uri("http://loggedout.example.com"),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
        public void LogoutCommand_Run_HandlesLogoutResponse_UsesReturnPathWhenStateDisabled()
        {
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert,
                RelayState = null
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(response);

            var applicationPath = "http://sp-internal.example.com/path/AuthServices/";
            var returnPath = "http://sp-internal.example.com/path/anotherpath";
            var request = new HttpRequestData("GET", bindResult.Location, applicationPath, null, null);

            var options = StubFactory.CreateOptions();
            options.SPOptions.Compatibility.DisableLogoutStateCookie = true;

            var actual = LogoutCommand.Run(request, returnPath, options);

            var expected = new CommandResult
            {
                Location = new Uri( returnPath ),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = null
            };

            actual.ShouldBeEquivalentTo(expected);
        }