예제 #1
0
        private async Task <IActionResult> SingleLogoutRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding, IEnumerable <Claim> claims)
        {
            var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party, true);

            claims = await claimTransformationsLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims);

            var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig)
            {
                NameId       = samlClaimsDownLogic.GetNameId(claims),
                Destination  = new Uri(party.SingleLogoutUrl),
                SessionIndex = samlClaimsDownLogic.GetSessionIndex(claims)
            };

            binding.RelayState = SequenceString;
            binding.Bind(saml2LogoutRequest);
            logger.ScopeTrace($"SAML Single Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'.");
            logger.ScopeTrace($"Single logged out URL '{party.SingleLogoutUrl}'.");
            logger.ScopeTrace("Down, SAML Single Logout request.", triggerEvent: true);

            securityHeaderLogic.AddFormAction(party.SingleLogoutUrl);
            if (binding is Saml2Binding <Saml2RedirectBinding> )
            {
                return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync());
            }
            if (binding is Saml2Binding <Saml2PostBinding> )
            {
                return(await(binding as Saml2PostBinding).ToActionFormResultAsync());
            }
            else
            {
                throw new NotSupportedException();
            }
        }
예제 #2
0
        private async Task <IActionResult> SingleLogoutResponseAsync <T>(SamlDownParty party, Saml2Binding <T> binding)
        {
            var samlConfig = await saml2ConfigurationLogic.GetSamlDownConfigAsync(party);

            var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig);

            binding.ReadSamlResponse(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse);
            logger.ScopeTrace(() => $"SAML Single Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message);

            ValidateLogoutResponse(party, saml2LogoutResponse);
            await sequenceLogic.ValidateExternalSequenceIdAsync(binding.RelayState);

            try
            {
                binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse);
                logger.ScopeTrace(() => "Down, SAML Single Logout response accepted.", triggerEvent: true);
            }
            catch (Exception ex)
            {
                var isex = saml2ConfigurationLogic.GetInvalidSignatureValidationCertificateException(samlConfig, ex);
                if (isex != null)
                {
                    throw isex;
                }
                throw;
            }

            return(await singleLogoutDownLogic.HandleSingleLogoutAsync());
        }
예제 #3
0
        private LoginRequest GetLoginRequestAsync(SamlDownParty party, Saml2AuthnRequest saml2AuthnRequest)
        {
            var loginRequest = new LoginRequest {
                DownPartyLink = new DownPartySessionLink {
                    SupportSingleLogout = !string.IsNullOrWhiteSpace(party.SingleLogoutUrl), Id = party.Id, Type = party.Type
                }
            };

            if (saml2AuthnRequest.ForceAuthn.HasValue && saml2AuthnRequest.ForceAuthn.Value)
            {
                loginRequest.LoginAction = LoginAction.RequireLogin;
            }
            else if (saml2AuthnRequest.IsPassive.HasValue && saml2AuthnRequest.IsPassive.Value)
            {
                loginRequest.LoginAction = LoginAction.ReadSession;
            }
            else
            {
                loginRequest.LoginAction = LoginAction.ReadSessionOrLogin;
            }

            if (saml2AuthnRequest.RequestedAuthnContext?.AuthnContextClassRef?.Count() > 0)
            {
                loginRequest.Acr = saml2AuthnRequest.RequestedAuthnContext?.AuthnContextClassRef;
            }

            return(loginRequest);
        }
예제 #4
0
        public Saml2Configuration GetSamlDownConfig(SamlDownParty party, bool includeSigningCertificate = false)
        {
            var samlConfig = new Saml2Configuration();

            samlConfig.Issuer = !party.IdPIssuer.IsNullOrEmpty() ? party.IdPIssuer : trackIssuerLogic.GetIssuer();

            if (party.Keys?.Count > 0)
            {
                foreach (var key in party.Keys)
                {
                    samlConfig.SignatureValidationCertificates.Add(key.ToSaml2X509Certificate());
                }
            }

            if (includeSigningCertificate)
            {
                samlConfig.SigningCertificate = trackKeyLogic.GetPrimarySaml2X509Certificate(RouteBinding.Key);
            }
            samlConfig.SignatureAlgorithm = party.SignatureAlgorithm;

            samlConfig.CertificateValidationMode = party.CertificateValidationMode;
            samlConfig.RevocationMode            = party.RevocationMode;

            return(samlConfig);
        }
예제 #5
0
        private async Task <IActionResult> LogoutRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding)
        {
            var samlConfig = await saml2ConfigurationLogic.GetSamlDownConfigAsync(party);

            var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig);

            binding.ReadSamlRequest(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest);
            logger.ScopeTrace(() => $"SAML Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message);

            try
            {
                ValidateLogoutRequest(party, saml2LogoutRequest);

                try
                {
                    binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest);
                    logger.ScopeTrace(() => "Down, SAML Logout request accepted.", triggerEvent: true);
                }
                catch (Exception ex)
                {
                    var isex = saml2ConfigurationLogic.GetInvalidSignatureValidationCertificateException(samlConfig, ex);
                    if (isex != null)
                    {
                        throw isex;
                    }
                    throw;
                }

                await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData
                {
                    Id         = saml2LogoutRequest.Id.Value,
                    RelayState = binding.RelayState
                });

                var type = RouteBinding.ToUpParties.First().Type;
                logger.ScopeTrace(() => $"Request, Up type '{type}'.");
                switch (type)
                {
                case PartyTypes.Login:
                    return(await serviceProvider.GetService <LogoutUpLogic>().LogoutRedirect(RouteBinding.ToUpParties.First(), GetLogoutRequest(party, saml2LogoutRequest)));

                case PartyTypes.OAuth2:
                    throw new NotImplementedException();

                case PartyTypes.Oidc:
                    return(await serviceProvider.GetService <OidcRpInitiatedLogoutUpLogic <OidcUpParty, OidcUpClient> >().EndSessionRequestRedirectAsync(RouteBinding.ToUpParties.First(), GetLogoutRequest(party, saml2LogoutRequest)));

                case PartyTypes.Saml2:
                    return(await serviceProvider.GetService <SamlLogoutUpLogic>().LogoutRequestRedirectAsync(RouteBinding.ToUpParties.First(), GetSamlLogoutRequest(party, saml2LogoutRequest)));

                default:
                    throw new NotSupportedException($"Party type '{type}' not supported.");
                }
            }
            catch (SamlRequestException ex)
            {
                logger.Error(ex);
                return(await LogoutResponseAsync(party, samlConfig, saml2LogoutRequest.Id.Value, binding.RelayState, ex.Status));
            }
        }
예제 #6
0
        public Saml2Configuration GetSamlDownConfig(SamlDownParty party, bool includeSigningCertificate = false)
        {
            var samlConfig = new Saml2Configuration();

            if (!party.IdSIssuer.IsNullOrEmpty())
            {
                samlConfig.Issuer = party.IdSIssuer;
            }
            else
            {
                samlConfig.Issuer = UrlCombine.Combine(HttpContext.GetHost(), RouteBinding.TenantName, RouteBinding.TrackName);
            }

            if (party.Keys?.Count > 0)
            {
                foreach (var key in party.Keys)
                {
                    samlConfig.SignatureValidationCertificates.Add(key.ToSaml2X509Certificate());
                }
            }

            if (includeSigningCertificate)
            {
                samlConfig.SigningCertificate = trackKeyLogic.GetSaml2X509Certificate(RouteBinding.PrimaryKey);
            }
            samlConfig.SignatureAlgorithm = party.SignatureAlgorithm;

            samlConfig.CertificateValidationMode = party.CertificateValidationMode;
            samlConfig.RevocationMode            = party.RevocationMode;

            return(samlConfig);
        }
예제 #7
0
 private bool ValidatePartySingleLogoutSupport(SamlDownParty party)
 {
     if (party.LogoutBinding == null || party.SingleLogoutUrl.IsNullOrEmpty() || party.Keys?.Count <= 0)
     {
         return(false);
     }
     return(true);
 }
예제 #8
0
        private async Task <IActionResult> LogoutRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding)
        {
            var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party);

            var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig);

            binding.ReadSamlRequest(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest);
            logger.ScopeTrace($"SAML Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'.");

            try
            {
                ValidateLogoutRequest(party, saml2LogoutRequest);
                binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest);
                logger.ScopeTrace("Down, SAML Logout request accepted.", triggerEvent: true);

                await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData
                {
                    Id          = saml2LogoutRequest.Id.Value,
                    RelayState  = binding.RelayState,
                    ResponseUrl = party.LoggedOutUrl,
                });

                await formActionLogic.CreateFormActionByUrlAsync(party.LoggedOutUrl);

                var type = RouteBinding.ToUpParties.First().Type;
                logger.ScopeTrace($"Request, Up type '{type}'.");
                switch (type)
                {
                case PartyTypes.Login:
                    return(await serviceProvider.GetService <LogoutUpLogic>().LogoutRedirect(RouteBinding.ToUpParties.First(), new LogoutRequest
                    {
                        DownParty = party,
                        SessionId = saml2LogoutRequest.SessionIndex,
                        RequireLogoutConsent = false,
                        PostLogoutRedirect = true,
                    }));

                case PartyTypes.OAuth2:
                    throw new NotImplementedException();

                case PartyTypes.Oidc:
                    throw new NotImplementedException();

                case PartyTypes.Saml2:
                    return(await serviceProvider.GetService <SamlLogoutUpLogic>().LogoutAsync(RouteBinding.ToUpParties.First(), GetSamlUpLogoutRequest(saml2LogoutRequest, party)));

                default:
                    throw new NotSupportedException($"Party type '{type}' not supported.");
                }
            }
            catch (SamlRequestException ex)
            {
                logger.Error(ex);
                return(await LogoutResponseAsync(party.Id, samlConfig, saml2LogoutRequest.Id.Value, binding.RelayState, saml2LogoutRequest.Destination?.OriginalString, party.AuthnBinding.ResponseBinding, ex.Status));
            }
        }
예제 #9
0
 private void ValidatePartyLogoutSupport(SamlDownParty party)
 {
     if (party.LogoutBinding == null || party.LoggedOutUrl.IsNullOrEmpty() || party.Keys?.Count <= 0)
     {
         throw new EndpointException("Logout not configured.")
               {
                   RouteBinding = RouteBinding
               };
     }
 }
예제 #10
0
 private string GetAcsUrl(SamlDownParty party, Saml2AuthnRequest saml2AuthnRequest)
 {
     if (saml2AuthnRequest.AssertionConsumerServiceUrl != null)
     {
         return(saml2AuthnRequest.AssertionConsumerServiceUrl.OriginalString);
     }
     else
     {
         return(party.AcsUrls.First());
     }
 }
예제 #11
0
        public IEnumerable <Claim> GetSubjectClaims(SamlDownParty party, IEnumerable <Claim> claims)
        {
            var acceptAllClaims = party.Claims?.Where(c => c == "*")?.Count() > 0;

            if (!acceptAllClaims)
            {
                var acceptedClaims = Constants.DefaultClaims.SamlClaims.ConcatOnce(party.Claims);
                claims = claims.Where(c => acceptedClaims.Any(ic => ic == c.Type));
            }
            return(claims);
        }
예제 #12
0
 private LogoutRequest GetLogoutRequest(SamlDownParty party, Saml2LogoutRequest saml2LogoutRequest)
 {
     return(new LogoutRequest
     {
         DownPartyLink = new DownPartySessionLink {
             SupportSingleLogout = !string.IsNullOrWhiteSpace(party.SingleLogoutUrl), Id = party.Id, Type = party.Type
         },
         SessionId = saml2LogoutRequest.SessionIndex,
         RequireLogoutConsent = false,
         PostLogoutRedirect = true,
     });
 }
예제 #13
0
        private async Task <IActionResult> AuthnRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding)
        {
            var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party);
            var request    = HttpContext.Request;

            var saml2AuthnRequest = new Saml2AuthnRequest(samlConfig);

            binding.ReadSamlRequest(request.ToGenericHttpRequest(), saml2AuthnRequest);
            logger.ScopeTrace($"SAML Authn request '{saml2AuthnRequest.XmlDocument.OuterXml}'.");

            try
            {
                ValidateAuthnRequest(party, saml2AuthnRequest);
                binding.Unbind(request.ToGenericHttpRequest(), saml2AuthnRequest);
                logger.ScopeTrace("Down, SAML Auth request accepted.", triggerEvent: true);

                var responseUrl = GetAcsUrl(party, saml2AuthnRequest);
                await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData
                {
                    Id          = saml2AuthnRequest.Id.Value,
                    RelayState  = binding.RelayState,
                    ResponseUrl = responseUrl,
                });

                await formActionLogic.CreateFormActionByUrlAsync(responseUrl);

                var type = RouteBinding.ToUpParties.First().Type;
                logger.ScopeTrace($"Request, Up type '{type}'.");
                switch (type)
                {
                case PartyTypes.Login:
                    return(await serviceProvider.GetService <LoginUpLogic>().LoginRedirectAsync(RouteBinding.ToUpParties.First(), GetLoginRequestAsync(party, saml2AuthnRequest)));

                case PartyTypes.OAuth2:
                    throw new NotImplementedException();

                case PartyTypes.Oidc:
                    return(await serviceProvider.GetService <OidcAuthUpLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationRequestAsync(RouteBinding.ToUpParties.First()));

                case PartyTypes.Saml2:
                    return(await serviceProvider.GetService <SamlAuthnUpLogic>().AuthnRequestAsync(RouteBinding.ToUpParties.First(), GetLoginRequestAsync(party, saml2AuthnRequest)));

                default:
                    throw new NotSupportedException($"Party type '{type}' not supported.");
                }
            }
            catch (SamlRequestException ex)
            {
                logger.Error(ex);
                return(await AuthnResponseAsync(party.Id, samlConfig, saml2AuthnRequest.Id.Value, binding.RelayState, GetAcsUrl(party, saml2AuthnRequest), party.AuthnBinding.ResponseBinding, ex.Status));
            }
        }
예제 #14
0
        private async Task <IActionResult> AuthnResponseAsync <T>(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, string acsUrl, Saml2Binding <T> binding, Saml2StatusCodes status, IEnumerable <Claim> claims)
        {
            binding.RelayState = relayState;

            var saml2AuthnResponse = new FoxIDsSaml2AuthnResponse(settings, samlConfig)
            {
                InResponseTo = new Saml2Id(inResponseTo),
                Status       = status,
                Destination  = new Uri(acsUrl),
            };

            if (status == Saml2StatusCodes.Success && party != null && claims != null)
            {
                claims = await claimTransformationsLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims);

                saml2AuthnResponse.SessionIndex = samlClaimsDownLogic.GetSessionIndex(claims);

                saml2AuthnResponse.NameId = samlClaimsDownLogic.GetNameId(claims);

                var tokenIssueTime  = DateTimeOffset.UtcNow;
                var tokenDescriptor = saml2AuthnResponse.CreateTokenDescriptor(samlClaimsDownLogic.GetSubjectClaims(party, claims), party.Issuer, tokenIssueTime, party.IssuedTokenLifetime);

                var authnContext            = claims.FindFirstValue(c => c.Type == ClaimTypes.AuthenticationMethod);
                var authenticationInstant   = claims.FindFirstValue(c => c.Type == ClaimTypes.AuthenticationInstant);
                var authenticationStatement = saml2AuthnResponse.CreateAuthenticationStatement(authnContext, DateTime.Parse(authenticationInstant));

                var subjectConfirmation = saml2AuthnResponse.CreateSubjectConfirmation(tokenIssueTime, party.SubjectConfirmationLifetime);

                await saml2AuthnResponse.CreateSecurityTokenAsync(tokenDescriptor, authenticationStatement, subjectConfirmation);
            }

            binding.Bind(saml2AuthnResponse);
            logger.ScopeTrace($"SAML Authn response '{saml2AuthnResponse.XmlDocument.OuterXml}'.");
            logger.ScopeTrace($"Acs URL '{acsUrl}'.");
            logger.ScopeTrace("Down, SAML Authn response.", triggerEvent: true);

            await sequenceLogic.RemoveSequenceDataAsync <SamlDownSequenceData>();

            securityHeaderLogic.AddFormAction(acsUrl);
            if (binding is Saml2Binding <Saml2RedirectBinding> )
            {
                return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync());
            }
            else if (binding is Saml2Binding <Saml2PostBinding> )
            {
                return(await(binding as Saml2PostBinding).ToActionFormResultAsync());
            }
            else
            {
                throw new NotSupportedException();
            }
        }
예제 #15
0
        private async Task CreateAspNetCoreSamlSampleDownPartyAsync()
        {
            Func <string, Task> getAction = async(name) =>
            {
                _ = await foxIDsApiClient.GetSamlDownPartyAsync(name);
            };

            Func <string, Task> postAction = async(name) =>
            {
                var baseUrl = "https://localhost:44343";

                var samlUpParty = new SamlDownParty
                {
                    Name              = name,
                    Issuer            = "urn:itfoxtec:idservice:samples:aspnetcoresamlsample",
                    AllowUpPartyNames = new[] { loginName, aspNetCoreSamlIdPSampleUpPartyName, identityserverOidcOpUpPartyName /*, "foxids_oidcpkce", "adfs_saml_idp"*/ },
                    Keys              = new[]
                    {
                        new JsonWebKey
                        {
                            Kty = "RSA",
                            Kid = "3863A8A752E5D6B812AA8A78A656E2DE6C637D12",
                            X5c = new[] { "MIICzzCCAbegAwIBAgIJAOd44ujQLBp/MA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNVBAMTDnRlc3Qtc2lnbi1jZXJ0MB4XDTE4MTAwOTA4NTMxOFoXDTE5MTAxMDA4NTMxOFowGTEXMBUGA1UEAxMOdGVzdC1zaWduLWNlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCbttrAY2VkISBs/dQW9B38dvO1++Pcqqlj0darBfq8+1f+nRsn0OcQOYAhMvPhuS7qy5NLaTFm8RbH3veybYm7cJFU6xGu8SiLv6rPa5CBrSTgL/sJ+NwIDG3ZaZbayKTqgf31D1Gv8mIOWtEVHOn9ZPvfO6r0I9tLMZtJASHDTxe7niskT2PEfGe1KBTXVgJqY67KttzlydvH4zN+lwXFguBKLQqicw9iJ9BngxDAMLkOz6SIeF5WFGRPfiLD/MOZQ/skb+1H9Bl+5mbL/F0TiVs1HaQNEt3N9SO18dRyA2ZGtGfTzJbx3gQ7RwRjmNMnK8In9M0jxZZ1Rvji2XFAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3DQEBCwUAA4IBAQC5RtTJV7mONXWKFkmF8EnCfPbFemCZs7Usw4cicjWlPTPfneFTsSJ4NuFmpWYrf1Lr75cf9BjHZDVHDGrRTsou/wAuqSehRPlZyj35ysjrC1hNmFYKQU+WkulxE4BZIcD+3fKj+6WAPVGG0NMnKWrmie2XK0aM5nFrWST4xqk6V5+4DOT7lltmPs9eUDJ8wkIL1oP/mhsE7tKpqMk9qNCb5nZMwXhqoTnlqTw/DFDCPJV/CS20/PamGTVUUhW1I0r73QDv054ycFY0ijU3tUK2V4D3daFTBHVGlLsCUxSBJSWkTGieN+iyU5aNbCErBc0+cim79lXT6sZ8VPVJ+kdW" },
                            X5t = "OGOop1Ll1rgSqop4plbi3mxjfRI",
                            N   = "wm7bawGNlZCEgbP3UFvQd_Hbztfvj3KqpY9HWqwX6vPtX_p0bJ9DnEDmAITLz4bku6suTS2kxZvEWx973sm2Ju3CRVOsRrvEoi7-qz2uQga0k4C_7CfjcCAxt2WmW2sik6oH99Q9Rr_JiDlrRFRzp_WT73zuq9CPbSzGbSQEhw08Xu54rJE9jxHxntSgU11YCamOuyrbc5cnbx-MzfpcFxYLgSi0KonMPYifQZ4MQwDC5Ds-kiHheVhRkT34iw_zDmUP7JG_tR_QZfuZmy_xdE4lbNR2kDRLdzfUjtfHUcgNmRrRn08yW8d4EO0cEY5jTJyvCJ_TNI8WWdUb44tlxQ",
                            E   = "AQAB"
                        }
                    },
                    SignatureAlgorithm        = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
                    CertificateValidationMode = X509CertificateValidationMode.None,
                    RevocationMode            = X509RevocationMode.NoCheck,
                    AuthnBinding = new SamlBinding {
                        RequestBinding = SamlBindingTypes.Redirect, ResponseBinding = SamlBindingTypes.Post
                    },
                    AcsUrls       = new[] { UrlCombine.Combine(baseUrl, "saml/assertionconsumerservice") },
                    LogoutBinding = new SamlBinding {
                        RequestBinding = SamlBindingTypes.Post, ResponseBinding = SamlBindingTypes.Post
                    },
                    SingleLogoutUrl             = UrlCombine.Combine(baseUrl, "saml/singlelogout"),
                    LoggedOutUrl                = UrlCombine.Combine(baseUrl, "saml/loggedout"),
                    Claims                      = new string[] { ClaimTypes.Email, ClaimTypes.Name, ClaimTypes.GivenName, ClaimTypes.Surname, ClaimTypes.Role },
                    MetadataLifetime            = 1728000, // 20 days
                    SubjectConfirmationLifetime = 300,     // 5 minutes
                    IssuedTokenLifetime         = 36000    // 10 hours
                };

                await foxIDsApiClient.PostSamlDownPartyAsync(samlUpParty);
            };

            await CreateIfNotExistsAsync(aspNetCoreSamlSampleDownPartyName, getAction, postAction);
        }
예제 #16
0
        private void ValidateLogoutRequest(SamlDownParty party, Saml2LogoutRequest saml2LogoutRequest)
        {
            var requestIssuer = saml2LogoutRequest.Issuer;

            logger.SetScopeProperty("Issuer", requestIssuer);

            if (!party.Issuer.Equals(requestIssuer))
            {
                throw new SamlRequestException($"Invalid issuer '{requestIssuer}'.")
                      {
                          RouteBinding = RouteBinding, Status = Saml2StatusCodes.Responder
                      };
            }
        }
예제 #17
0
        private async Task <IActionResult> SingleLogoutResponseAsync <T>(SamlDownParty party, Saml2Binding <T> binding)
        {
            var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party);

            var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig);

            binding.ReadSamlResponse(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse);
            logger.ScopeTrace($"SAML Single Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'.");

            ValidateLogoutResponse(party, saml2LogoutResponse);
            await sequenceLogic.ValidateSequenceAsync(binding.RelayState);

            binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse);
            logger.ScopeTrace("Down, SAML Single Logout response accepted.", triggerEvent: true);

            return(await singleLogoutDownLogic.HandleSingleLogoutAsync());
        }
예제 #18
0
        private Task <IActionResult> AuthnResponseAsync(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, string acsUrl, Saml2StatusCodes status, IEnumerable <Claim> claims = null)
        {
            logger.ScopeTrace(() => $"Down, SAML Authn response{(status != Saml2StatusCodes.Success ? " error" : string.Empty)}, Status code '{status}'.");

            var binding = party.AuthnBinding.ResponseBinding;

            logger.ScopeTrace(() => $"Binding '{binding}'");
            switch (binding)
            {
            case SamlBindingTypes.Redirect:
                return(AuthnResponseAsync(party, samlConfig, inResponseTo, relayState, acsUrl, new Saml2RedirectBinding(), status, claims));

            case SamlBindingTypes.Post:
                return(AuthnResponseAsync(party, samlConfig, inResponseTo, relayState, acsUrl, new Saml2PostBinding(), status, claims));

            default:
                throw new NotSupportedException($"SAML binding '{binding}' not supported.");
            }
        }
예제 #19
0
        private Task <IActionResult> LogoutResponseAsync(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, Saml2StatusCodes status, string sessionIndex = null)
        {
            logger.ScopeTrace($"Down, SAML Logout response{(status != Saml2StatusCodes.Success ? " error" : string.Empty)}, Status code '{status}'.");

            var binding = party.LogoutBinding.ResponseBinding;

            logger.ScopeTrace($"Binding '{binding}'");
            switch (binding)
            {
            case SamlBindingTypes.Redirect:
                return(LogoutResponseAsync(samlConfig, inResponseTo, relayState, party.LoggedOutUrl, new Saml2RedirectBinding(), status, sessionIndex));

            case SamlBindingTypes.Post:
                return(LogoutResponseAsync(samlConfig, inResponseTo, relayState, party.LoggedOutUrl, new Saml2PostBinding(), status, sessionIndex));

            default:
                throw new NotSupportedException($"SAML binding '{binding}' not supported.");
            }
        }
예제 #20
0
        private LoginRequest GetLoginRequestAsync(SamlDownParty party, Saml2AuthnRequest saml2AuthnRequest)
        {
            var loginRequest = new LoginRequest {
                DownParty = party
            };

            if (saml2AuthnRequest.ForceAuthn.HasValue && saml2AuthnRequest.ForceAuthn.Value)
            {
                loginRequest.LoginAction = LoginAction.RequereLogin;
            }
            else if (saml2AuthnRequest.IsPassive.HasValue && saml2AuthnRequest.IsPassive.Value)
            {
                loginRequest.LoginAction = LoginAction.ReadSession;
            }
            else
            {
                loginRequest.LoginAction = LoginAction.ReadSessionOrLogin;
            }

            return(loginRequest);
        }
예제 #21
0
        private async Task <IActionResult> LogoutResponseAsync <T>(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, Saml2Binding <T> binding, Saml2StatusCodes status, string sessionIndex)
        {
            binding.RelayState = relayState;

            var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig)
            {
                InResponseTo = new Saml2Id(inResponseTo),
                Status       = status,
                Destination  = new Uri(party.LoggedOutUrl),
                SessionIndex = sessionIndex
            };

            binding.Bind(saml2LogoutResponse);
            logger.ScopeTrace(() => $"SAML Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message);
            logger.ScopeTrace(() => $"Logged out URL '{party.LoggedOutUrl}'.");
            logger.ScopeTrace(() => "Down, SAML Logout response.", triggerEvent: true);

            await sequenceLogic.RemoveSequenceDataAsync <SamlDownSequenceData>();

            if (party.RestrictFormAction)
            {
                securityHeaderLogic.AddFormAction(party.LoggedOutUrl);
            }
            else
            {
                securityHeaderLogic.AddFormActionAllowAll();
            }
            if (binding is Saml2Binding <Saml2RedirectBinding> )
            {
                return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync());
            }
            if (binding is Saml2Binding <Saml2PostBinding> )
            {
                return(await(binding as Saml2PostBinding).ToActionFormResultAsync());
            }
            else
            {
                throw new NotSupportedException();
            }
        }
예제 #22
0
        private void ValidateAuthnRequest(SamlDownParty party, Saml2AuthnRequest saml2AuthnRequest)
        {
            if (saml2AuthnRequest.AssertionConsumerServiceUrl != null && !party.AcsUrls.Any(u => u.Equals(saml2AuthnRequest.AssertionConsumerServiceUrl.OriginalString, StringComparison.InvariantCultureIgnoreCase)))
            {
                throw new EndpointException($"Invalid assertion consumer service url '{saml2AuthnRequest.AssertionConsumerServiceUrl.OriginalString}'.")
                      {
                          RouteBinding = RouteBinding
                      };
            }

            var requestIssuer = saml2AuthnRequest.Issuer;

            logger.SetScopeProperty("Issuer", requestIssuer);

            if (!party.Issuer.Equals(requestIssuer))
            {
                throw new SamlRequestException($"Invalid issuer '{requestIssuer}'.")
                      {
                          RouteBinding = RouteBinding, Status = Saml2StatusCodes.Responder
                      };
            }
        }
예제 #23
0
        public async Task <FoxIDsSaml2Configuration> GetSamlDownConfigAsync(SamlDownParty party, bool includeSigningCertificate = false, bool includeSignatureValidationCertificates = true)
        {
            var samlConfig = new FoxIDsSaml2Configuration();

            samlConfig.Issuer = !string.IsNullOrEmpty(party?.IdPIssuer) ? party.IdPIssuer : trackIssuerLogic.GetIssuer();

            if (party != null)
            {
                if (party.Keys?.Count > 0 && includeSignatureValidationCertificates)
                {
                    var partyCertificates = party.Keys.ToSaml2X509Certificates();
                    foreach (var partyCertificate in partyCertificates)
                    {
                        if (partyCertificate.IsValidLocalTime())
                        {
                            samlConfig.SignatureValidationCertificates.Add(partyCertificate);
                        }
                        else
                        {
                            samlConfig.InvalidSignatureValidationCertificates.Add(partyCertificate);
                        }
                    }
                }
            }

            if (includeSigningCertificate)
            {
                samlConfig.SigningCertificate = await trackKeyLogic.GetPrimarySaml2X509CertificateAsync(RouteBinding.Key);
            }
            if (party != null)
            {
                samlConfig.SignatureAlgorithm = party.SignatureAlgorithm;

                samlConfig.CertificateValidationMode = party.CertificateValidationMode;
                samlConfig.RevocationMode            = party.RevocationMode;
            }

            return(samlConfig);
        }
예제 #24
0
        private async Task CreateAspNetCoreSamlSampleDownPartyAsync()
        {
            Func <string, Task> getAction = async(name) =>
            {
                _ = await foxIDsApiClient.GetSamlDownPartyAsync(name);
            };

            Func <string, Task> postAction = async(name) =>
            {
                var baseUrl = "https://localhost:44343";

                var samlUpParty = new SamlDownParty
                {
                    Name                        = name,
                    Issuer                      = "urn:itfoxtec:idservice:samples:aspnetcoresamlsample",
                    AllowUpPartyNames           = new[] { loginName, aspNetCoreSamlIdPSampleUpPartyName, identityserverOidcOpUpPartyName /*, "foxids_oidcpkce", "adfs_saml_idp"*/ },
                    Keys                        = new[] { GetSamlCertificateKey("AspNetCoreSamlSample-test-sign-cert.crt") },
                    SignatureAlgorithm          = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
                    CertificateValidationMode   = X509CertificateValidationMode.None,
                    RevocationMode              = X509RevocationMode.NoCheck,
                    AuthnRequestBinding         = SamlBindingTypes.Redirect,
                    AuthnResponseBinding        = SamlBindingTypes.Post,
                    AcsUrls                     = new[] { UrlCombine.Combine(baseUrl, "saml/assertionconsumerservice") },
                    LogoutRequestBinding        = SamlBindingTypes.Post,
                    LogoutResponseBinding       = SamlBindingTypes.Post,
                    SingleLogoutUrl             = UrlCombine.Combine(baseUrl, "saml/singlelogout"),
                    LoggedOutUrl                = UrlCombine.Combine(baseUrl, "saml/loggedout"),
                    Claims                      = new string[] { ClaimTypes.Email, ClaimTypes.Name, ClaimTypes.GivenName, ClaimTypes.Surname, ClaimTypes.Role },
                    SubjectConfirmationLifetime = 300,  // 5 minutes
                    IssuedTokenLifetime         = 36000 // 10 hours
                };

                await foxIDsApiClient.PostSamlDownPartyAsync(samlUpParty);
            };

            await CreateIfNotExistsAsync(aspNetCoreSamlSampleDownPartyName, getAction, postAction);
        }
예제 #25
0
        private LogoutRequest GetSamlLogoutRequest(SamlDownParty party, Saml2LogoutRequest saml2LogoutRequest)
        {
            var samlClaims = new List <Claim>();

            if (saml2LogoutRequest.NameId != null)
            {
                samlClaims.AddClaim(Saml2ClaimTypes.NameId, saml2LogoutRequest.NameId.Value);

                if (saml2LogoutRequest.NameId.Format != null)
                {
                    samlClaims.AddClaim(Saml2ClaimTypes.NameIdFormat, saml2LogoutRequest.NameId.Format.OriginalString);
                }
            }
            return(new LogoutRequest
            {
                DownPartyLink = new DownPartySessionLink {
                    SupportSingleLogout = !string.IsNullOrWhiteSpace(party.SingleLogoutUrl), Id = party.Id, Type = party.Type
                },
                SessionId = saml2LogoutRequest.SessionIndex,
                RequireLogoutConsent = false,
                PostLogoutRedirect = true,
                Claims = samlClaims,
            });
        }
예제 #26
0
 public async Task UpdateSamlDownPartyAsync(SamlDownParty party) => await PutAsync(samlApiUri, party);
예제 #27
0
 public async Task CreateSamlDownPartyAsync(SamlDownParty party) => await PostAsync(samlApiUri, party);
예제 #28
0
 public async Task <SamlDownParty> UpdateSamlDownPartyAsync(SamlDownParty party) => await PutResponseAsync <SamlDownParty, SamlDownParty>(samlApiUri, party);
예제 #29
0
        private IEnumerable <Claim> GetSubjectClaims(SamlDownParty party, IEnumerable <Claim> claims)
        {
            IEnumerable <string> acceptedClaims = Constants.DefaultClaims.SamlClaims.ConcatOnce(party.Claims);

            return(claims.Where(c => acceptedClaims.Any(ic => ic == c.Type)));
        }
예제 #30
0
        private SamlDownPartyViewModel ToViewModel(GeneralSamlDownPartyViewModel generalSamlDownParty, SamlDownParty samlDownParty)
        {
            return(samlDownParty.Map <SamlDownPartyViewModel>(afterMap =>
            {
                generalSamlDownParty.KeyInfoList.Clear();
                if (afterMap.Keys?.Count() > 0)
                {
                    foreach (var key in afterMap.Keys)
                    {
                        generalSamlDownParty.KeyInfoList.Add(new KeyInfoViewModel
                        {
                            Subject = key.CertificateInfo.Subject,
                            ValidFrom = key.CertificateInfo.ValidFrom,
                            ValidTo = key.CertificateInfo.ValidTo,
                            Thumbprint = key.CertificateInfo.Thumbprint,
                            Key = key
                        });
                    }
                }

                if (afterMap.ClaimTransforms?.Count > 0)
                {
                    afterMap.ClaimTransforms = afterMap.ClaimTransforms.MapClaimTransforms();
                }
            }));
        }