예제 #1
0
        private string ValidateSid(HttpRequest request)
        {
            var sidCookie = _sessionCookie.GetSessionId();

            if (sidCookie != null)
            {
                //TODO: update sid to OidcConstants when idmodel released
                var sid = request.Query["sid"].FirstOrDefault();
                if (sid != null)
                {
                    if (TimeConstantComparer.IsEqual(sid, sidCookie))
                    {
                        _logger.LogDebug("sid validation successful");
                        return(sid);
                    }
                    else
                    {
                        _logger.LogError("sid in query string does not match sid from cookie");
                    }
                }
                else
                {
                    _logger.LogError("No sid in query string");
                }
            }
            else
            {
                _logger.LogError("No sid in cookie");
            }

            return(null);
        }
        private string ValidateSid(NameValueCollection parameters)
        {
            var sidCookie = _sessionId.GetCookieValue();

            if (sidCookie != null)
            {
                var sid = parameters[OidcConstants.EndSessionRequest.Sid];
                if (sid != null)
                {
                    if (TimeConstantComparer.IsEqual(sid, sidCookie))
                    {
                        _logger.LogDebug("sid validation successful");
                        return(sid);
                    }
                    else
                    {
                        _logger.LogError("sid in query string does not match sid from cookie");
                    }
                }
                else
                {
                    _logger.LogError("No sid in query string");
                }
            }
            else
            {
                _logger.LogError("No sid in cookie");
            }

            return(null);
        }
예제 #3
0
        protected static bool ValidateCodeVerifierAgainstCodeChallenge(string codeVerifier, string codeChallenge)
        {
            var codeVerifierBytes       = Encoding.ASCII.GetBytes(codeVerifier);
            var hashedBytes             = codeVerifierBytes.Sha256();
            var transformedCodeVerifier = Base64Url.Encode(hashedBytes);

            // https://github.com/IdentityModel/IdentityModel/blob/main/src/TimeConstantComparer.cs
            return(TimeConstantComparer.IsEqual(transformedCodeVerifier.Sha256(), codeChallenge));
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentException">id or credential is missing.</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != Constants.ParsedSecretTypes.SharedSecret)
            {
                Logger.Debug(string.Format("Parsed secret should not be of type: {0}", parsedSecret.Type ?? "null"));
                return(fail);
            }

            var sharedSecret = parsedSecret.Credential as string;

            if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
            {
                throw new ArgumentException("Id or Credential is missing.");
            }

            foreach (var secret in secrets)
            {
                var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description;

                // this validator is only applicable to shared secrets
                if (secret.Type != Constants.SecretTypes.SharedSecret)
                {
                    Logger.Debug(string.Format("Skipping secret: {0}, secret is not of type {1}.", secretDescription, Constants.SecretTypes.SharedSecret));
                    continue;
                }

                // check if client secret is still valid
                if (secret.Expiration.HasExpired())
                {
                    Logger.Debug(string.Format("Skipping secret: {0}, secret is expired.", secretDescription));
                    continue;
                }

                // use time constant string comparison
                var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value);

                if (isValid)
                {
                    return(success);
                }
            }

            Logger.Debug("No matching plain text secret found.");
            return(fail);
        }
        private bool ValidateCodeVerifierAgainstCodeChallenge(string codeVerifier, string codeChallenge, string codeChallengeMethod)
        {
            if (codeChallengeMethod == OidcConstants.CodeChallengeMethods.Plain)
            {
                return(TimeConstantComparer.IsEqual(codeVerifier.Sha256(), codeChallenge));
            }

            var codeVerifierBytes       = Encoding.ASCII.GetBytes(codeVerifier);
            var hashedBytes             = codeVerifierBytes.Sha256();
            var transformedCodeVerifier = Base64Url.Encode(hashedBytes);

            return(TimeConstantComparer.IsEqual(transformedCodeVerifier.Sha256(), codeChallenge));
        }
예제 #6
0
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentException">id or credential is missing.</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret)
            {
                _logger.LogError("Parsed secret should not be of type: {type}", parsedSecret.Type ?? "null");
                return(fail);
            }

            var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret);

            if (!sharedSecrets.Any())
            {
                _logger.LogDebug("No shared secret configured for client.");
                return(fail);
            }

            var sharedSecret = parsedSecret.Credential as string;

            if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
            {
                throw new ArgumentException("Id or Credential is missing.");
            }

            foreach (var secret in sharedSecrets)
            {
                var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description;

                // use time constant string comparison
                var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value);

                if (isValid)
                {
                    return(success);
                }
            }

            _logger.LogDebug("No matching plain text secret found.");
            return(fail);
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentException">id or credential is missing.</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type == Constants.ParsedSecretTypes.SharedSecret)
            {
                var sharedSecret = parsedSecret.Credential as string;

                if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
                {
                    throw new ArgumentException("id or credential is missing.");
                }

                foreach (var secret in secrets)
                {
                    // this validator is only applicable to shared secrets
                    if (secret.Type != Constants.SecretTypes.SharedSecret)
                    {
                        continue;
                    }

                    // check if client secret is still valid
                    if (secret.Expiration.HasExpired())
                    {
                        continue;
                    }

                    // use time constant string comparison
                    var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value);

                    if (isValid)
                    {
                        return(success);
                    }
                }
            }

            return(fail);
        }
예제 #8
0
        private async Task ProcessResponseAsync(HttpContext context)
        {
            _logger.LogDebug("Federated signout path requested");

            var user = await _userSession.GetIdentityServerUserAsync();

            if (user != null)
            {
                var upstreamSid = user.FindFirst(OidcConstants.EndSessionRequest.Sid)?.Value;
                if (upstreamSid != null)
                {
                    var sidParam = await GetSidRequestParamAsync(context.Request);

                    if (TimeConstantComparer.IsEqual(upstreamSid, sidParam))
                    {
                        _logger.LogDebug("sid parameter matches external idp sid claim for current user");

                        // todo - default method does not pick up our auhtN scheme
                        await context.SignOutAsync();

                        var iframeUrl = await context.GetIdentityServerSignoutFrameCallbackUrlAsync();

                        if (iframeUrl != null)
                        {
                            _logger.LogDebug("Rendering signout callback iframe");
                            await RenderResponseAsync(context, iframeUrl);
                        }
                        else
                        {
                            _logger.LogDebug("No signout callback iframe to render");
                        }
                    }
                }
                else
                {
                    _logger.LogDebug("no sid param passed");
                }
            }
            else
            {
                _logger.LogDebug("no authenticated user");
            }
        }
예제 #9
0
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            logger.LogDebug("X509 Validation start");
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != IdentityServer4.IdentityServerConstants.ParsedSecretTypes.X509Certificate)
            {
                return(fail);
            }

            var cert = parsedSecret.Credential as X509Certificate2;

            if (cert == null)
            {
                throw new ArgumentException("ParsedSecret.Credential is not an X509 Certificate");
            }

            string thumbprint = cert.Thumbprint;

            if (string.IsNullOrWhiteSpace(thumbprint))
            {
                throw new ArgumentException("ParsedSecret.Credential.Thumbprint is empty");
            }

            foreach (var secret in secrets)
            {
                if (secret.Type == IdentityServer4.IdentityServerConstants.SecretTypes.X509CertificateThumbprint)
                {
                    if (TimeConstantComparer.IsEqual(thumbprint.ToLowerInvariant(), secret.Value.ToLowerInvariant()))
                    {
                        return(success);
                    }
                }
            }

            return(fail);
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not an X509 Certificate</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != Constants.ParsedSecretTypes.X509Certificate)
            {
                return(fail);
            }

            var cert = parsedSecret.Credential as X509Certificate2;

            if (cert == null)
            {
                throw new ArgumentException("ParsedSecret.Credential is not an X509 Certificate");
            }

            var thumbprint = cert.Thumbprint;

            foreach (var secret in secrets)
            {
                // check if client secret is still valid
                if (secret.Expiration.HasExpired())
                {
                    continue;
                }

                if (secret.Type == Constants.SecretTypes.X509CertificateThumbprint)
                {
                    if (TimeConstantComparer.IsEqual(thumbprint.ToLowerInvariant(), secret.Value.ToLowerInvariant()))
                    {
                        return(success);
                    }
                }
            }

            return(fail);
        }
예제 #11
0
        private static bool EmbeddedCertificateIsTrusted(X509Certificate2 certificate, IReadOnlyCollection <Secret> secrets)
        {
            if (certificate == null || certificate.Thumbprint == null)
            {
                return(false);
            }

            if (secrets.Any(s => s.Type == Constants.SecretTypes.X509CertificateThumbprint &&
                            TimeConstantComparer.IsEqual(s.Value.ToLowerInvariant(), certificate.Thumbprint.ToLowerInvariant())))
            {
                return(true);
            }

            if (secrets.Any(s => s.Type == Constants.SecretTypes.X509CertificateBase64 &&
                            Equals(certificate, GetCertificateFromString(s.Value))))
            {
                return(true);
            }

            return(false);
        }
예제 #12
0
        private async Task ProcessResponseAsync(HttpContext context)
        {
            _logger.LogDebug("Federated signout path requested");

            var user = await context.GetIdentityServerUserAsync();

            if (user != null)
            {
                var sid = user.FindFirst(OidcConstants.EndSessionRequest.Sid)?.Value;
                if (sid != null)
                {
                    var sidParam = await GetSidRequestParamAsync(context.Request);

                    if (TimeConstantComparer.IsEqual(sid, sidParam))
                    {
                        _logger.LogDebug("sid parameter matches current sid");

                        var iframeUrl = await context.GetIdentityServerSignoutFrameCallbackUrlAsync();

                        if (iframeUrl != null)
                        {
                            _logger.LogDebug("Rendering signout callback iframe");
                            await RenderResponseAsync(context, iframeUrl, sid);
                        }
                        else
                        {
                            _logger.LogDebug("No signout callback iframe to render");
                        }
                    }
                }
                else
                {
                    _logger.LogDebug("no sid param passed");
                }
            }
            else
            {
                _logger.LogDebug("no authenticated user");
            }
        }
예제 #13
0
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Id or cedential</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret)
            {
                _logger.LogDebug("Hashed shared secret validator cannot process {type}", parsedSecret.Type ?? "null");
                return(fail);
            }

            var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret);

            if (!sharedSecrets.Any())
            {
                _logger.LogDebug("No shared secret configured for client.");
                return(fail);
            }

            var sharedSecret = parsedSecret.Credential as string;



            var secretSha256 = sharedSecret.Sha256();
            var secretSha512 = sharedSecret.Sha512();

            foreach (var secret in sharedSecrets)
            {
                var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description;

                bool   isValid = false;
                byte[] secretBytes;

                try
                {
                    secretBytes = Convert.FromBase64String(secret.Value);
                }
                catch (FormatException)
                {
                    _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription);
                    return(fail);
                }
                catch (ArgumentNullException)
                {
                    _logger.LogInformation("Secret: {description} is null.", secretDescription);
                    return(fail);
                }

                if (secretBytes.Length == 32)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256);
                }
                else if (secretBytes.Length == 64)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512);
                }
                else
                {
                    _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription);
                    return(fail);
                }

                if (isValid)
                {
                    return(success);
                }
            }

            _logger.LogDebug("No matching hashed secret found.");
            return(fail);
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Id or cedential</exception>
        public Task<SecretValidationResult> ValidateAsync(IEnumerable<Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult { Success = false });
            var success = Task.FromResult(new SecretValidationResult { Success = true });

            if (parsedSecret.Type != Constants.ParsedSecretTypes.SharedSecret)
            {
                Logger.Debug(string.Format("Parsed secret should not be of type {0}", parsedSecret.Type ?? "null"));
                return fail;
            }

            var sharedSecret = parsedSecret.Credential as string;

            if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
            {
                throw new ArgumentException("Id or Credential is missing.");
            }

            var secretSha256 = sharedSecret.Sha256();
            var secretSha512 = sharedSecret.Sha512();

            foreach (var secret in secrets)
            {
                var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description;

                // this validator is only applicable to shared secrets
                if (secret.Type != Constants.SecretTypes.SharedSecret)
                {
                    Logger.Debug(string.Format("Skipping secret: {0}, secret is not of type {1}.", secretDescription, Constants.SecretTypes.SharedSecret));
                    continue;
                }

                // check if client secret is still valid
                if (secret.Expiration.HasExpired())
                {
                    Logger.Debug(string.Format("Skipping secret: {0}, secret is expired.", secretDescription));
                    continue;
                }

                bool isValid = false;
                byte[] secretBytes;

                try
                {
                    secretBytes = Convert.FromBase64String(secret.Value);
                }
                catch (FormatException)
                {
                    Logger.Error(string.Format("Secret: {0} uses invalid hashing algorithm.", secretDescription));
                    return fail;
                }

                if (secretBytes.Length == 32)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256);
                }
                else if (secretBytes.Length == 64)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512);
                }
                else
                {
                    Logger.Error(string.Format("Secret: {0} uses invalid hashing algorithm.", secretDescription));
                    return fail;
                }

                if (isValid)
                {
                    return success;
                }
            }

            Logger.Debug("No matching hashed secret found.");
            return fail;
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Id or cedential</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret)
            {
                _logger.LogDebug("Hashed shared secret validator cannot process {type}", parsedSecret.Type ?? "null");
                return(fail);
            }

            var sharedSecret = parsedSecret.Credential as string;

            if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
            {
                throw new ArgumentException("Id or Credential is missing.");
            }

            var secretSha256 = sharedSecret.Sha256();
            var secretSha512 = sharedSecret.Sha512();

            foreach (var secret in secrets)
            {
                var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description;

                // this validator is only applicable to shared secrets
                if (secret.Type != IdentityServerConstants.SecretTypes.SharedSecret)
                {
                    _logger.LogDebug("Skipping secret: {description}, secret is not of type {type}.", secretDescription, IdentityServerConstants.SecretTypes.SharedSecret);
                    continue;
                }

                bool   isValid = false;
                byte[] secretBytes;

                try
                {
                    secretBytes = Convert.FromBase64String(secret.Value);
                }
                catch (FormatException)
                {
                    _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription);
                    return(fail);
                }

                if (secretBytes.Length == 32)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256);
                }
                else if (secretBytes.Length == 64)
                {
                    isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512);
                }
                else
                {
                    _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription);
                    return(fail);
                }

                if (isValid)
                {
                    return(success);
                }
            }

            _logger.LogDebug("No matching hashed secret found.");
            return(fail);
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Id or cedential</exception>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });
            var success = Task.FromResult(new SecretValidationResult {
                Success = true
            });

            if (parsedSecret.Type == Constants.ParsedSecretTypes.SharedSecret)
            {
                var sharedSecret = parsedSecret.Credential as string;

                if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing())
                {
                    throw new ArgumentNullException("Id or cedential");
                }

                var secretSha256 = sharedSecret.Sha256();
                var secretSha512 = sharedSecret.Sha512();

                foreach (var secret in secrets)
                {
                    // this validator is only applicable to shared secrets
                    if (secret.Type != Constants.SecretTypes.SharedSecret)
                    {
                        continue;
                    }

                    bool   isValid = false;
                    byte[] secretBytes;

                    // check if client secret is still valid
                    if (secret.Expiration.HasExpired())
                    {
                        continue;
                    }

                    try
                    {
                        secretBytes = Convert.FromBase64String(secret.Value);
                    }
                    catch (FormatException)
                    {
                        Logger.Error("Secret uses invalid hashing algorithm");
                        return(fail);
                    }

                    if (secretBytes.Length == 32)
                    {
                        isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256);
                    }
                    else if (secretBytes.Length == 64)
                    {
                        isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512);
                    }
                    else
                    {
                        Logger.Error("Secret uses invalid hashing algorithm");
                        return(fail);
                    }

                    if (isValid)
                    {
                        return(success);
                    }
                }
            }

            return(fail);
        }