Exemple #1
0
        /// <summary>
        /// Get the ordered JWT configurations for validating tokens
        /// </summary>
        /// <returns></returns>
        private static List <JwtConfig> GetJwtConfigs()
        {
            // JWT configs are stored as defined values. The value is the OpenID configuration URL
            var definedTypeCache = DefinedTypeCache.Get(SystemGuid.DefinedType.JWT_CONFIGURATION);

            if (definedTypeCache == null || !definedTypeCache.IsActive || definedTypeCache.DefinedValues == null)
            {
                return(null);
            }

            // Additional JWT configuration properties are stored as attributes of the defined value
            var issuerAttributeCache    = AttributeCache.Get(SystemGuid.Attribute.DEFINED_VALUE_JWT_ISSUER);
            var audienceAttributeCache  = AttributeCache.Get(SystemGuid.Attribute.DEFINED_VALUE_JWT_AUDIENCE);
            var searchKeyAttributeCache = AttributeCache.Get(SystemGuid.Attribute.DEFINED_VALUE_JWT_SEARCH_KEY);

            // The configs should be ordered since they will be attempted in the order given here. The Rock admin may want them
            // tried in a specific order.
            return(definedTypeCache.DefinedValues.OrderBy(dv => dv.Order).ThenBy(dv => dv.Id).Select(dv =>
            {
                var config = new JwtConfig
                {
                    OpenIdConfigurationUrl = dv.Value
                };

                if (dv.AttributeValues != null)
                {
                    if (issuerAttributeCache != null && dv.AttributeValues.ContainsKey(issuerAttributeCache.Key))
                    {
                        config.Issuer = dv.AttributeValues[issuerAttributeCache.Key]?.Value;
                    }

                    if (audienceAttributeCache != null && dv.AttributeValues.ContainsKey(audienceAttributeCache.Key))
                    {
                        config.Audience = dv.AttributeValues[audienceAttributeCache.Key]?.Value;
                    }

                    if (searchKeyAttributeCache != null && dv.AttributeValues.ContainsKey(searchKeyAttributeCache.Key))
                    {
                        var searchKeyGuid = dv.AttributeValues[searchKeyAttributeCache.Key]?.Value;
                        config.SearchTypeValueId = DefinedValueCache.Get(searchKeyGuid)?.Id;
                    }
                }

                return config;
            }).ToList());
        }
Exemple #2
0
        /// <summary>
        /// Validates the JWT using the provided configuration and returns the parsed token if valid
        /// </summary>
        /// <param name="jwtString">The token.</param>
        /// <param name="jwtConfig">The configuration on how to validate the token</param>
        /// <returns></returns>
        private static JwtSecurityToken ValidateToken(JwtConfig jwtConfig, string jwtString)
        {
            if (jwtString.IsNullOrWhiteSpace())
            {
                return(null);
            }

            if (jwtConfig == null)
            {
                return(null);
            }

            // It is standard to prefix JWTs with "Bearer ", but JwtSecurityTokenHandler.ValidateToken will
            // say the token is malformed if the prefix is not removed
            if (jwtString.StartsWith(HeaderTokens.JwtPrefix))
            {
                jwtString = jwtString.Substring(HeaderTokens.JwtPrefix.Length);
            }

            // Retrieve the configuration manager, which is cached according to the jwtConfig.JwksJsonFileUrl
            var configurationManager = GetConfigurationManager(jwtConfig.OpenIdConfigurationUrl);

            if (configurationManager == null)
            {
                return(null);
            }

            // The configuration manager handles caching the configuration documents and keys, which are from another
            // server or provider like Auth0.
            var openIdConnectConfiguration = AsyncHelper.RunSync(() => configurationManager.GetConfigurationAsync());

            if (openIdConnectConfiguration == null || openIdConnectConfiguration.SigningKeys == null || !openIdConnectConfiguration.SigningKeys.Any())
            {
                return(null);
            }

            // Validate the items that are configured to be validated
            var validateAudience = !jwtConfig.Audience.IsNullOrWhiteSpace();
            var validateIssuer   = !jwtConfig.Issuer.IsNullOrWhiteSpace();

            var validationParameters = new TokenValidationParameters
            {
                ValidateAudience         = validateAudience,
                ValidAudience            = validateAudience ? jwtConfig.Audience : null,
                ValidateIssuer           = validateIssuer,
                ValidIssuer              = validateIssuer ? jwtConfig.Issuer : null,
                RequireExpirationTime    = true,
                RequireSignedTokens      = true,
                ValidateIssuerSigningKey = true,
                IssuerSigningKeys        = openIdConnectConfiguration.SigningKeys,
                ValidateLifetime         = true,
                ClockSkew = TimeSpan.FromMinutes(1)   // Allow a minute of play in server times since we're dealing with a third party key provider
            };

            try
            {
                var principal = new JwtSecurityTokenHandler().ValidateToken(jwtString, validationParameters, out var validatedToken);

                // If the principal identity is null, we should not accept this as a validated token
                if (principal == null || principal.Identity == null)
                {
                    return(null);
                }

                var jwtToken = validatedToken as JwtSecurityToken;
                return(jwtToken);
            }
            catch (Exception ex)
            {
                // The JWT was not well formed or did not validate in some other way
                ExceptionLogService.LogException(ex);
                Debug.WriteLine(ex.Message);
                return(null);
            }
        }