Esempio n. 1
0
        public JwtTokenExtractor(TokenValidationParameters tokenValidationParameters, string metadataUrl, string[] allowedSigningAlgorithms, EndorsementsValidator validator)
        {
            // Make our own copy so we can edit it
            _tokenValidationParameters = tokenValidationParameters.Clone();
            _tokenValidationParameters.RequireSignedTokens = true;
            _allowedSigningAlgorithms = allowedSigningAlgorithms;
            _validator = validator;

            _openIdMetadata = _openIdMetadataCache.GetOrAdd(metadataUrl, key =>
            {
                return(new ConfigurationManager <OpenIdConnectConfiguration>(metadataUrl, new OpenIdConnectConfigurationRetriever()));
            });

            _endorsementsData = _endorsementsCache.GetOrAdd(metadataUrl, key =>
            {
                var retriever = new EndorsementsRetriever();
                return(new ConfigurationManager <IDictionary <string, string[]> >(metadataUrl, retriever, retriever));
            });
        }
        private async Task <ClaimsPrincipal> ValidateTokenAsync(string jwtToken, string channelId, string[] requiredEndorsements)
        {
            if (requiredEndorsements == null)
            {
                throw new ArgumentNullException(nameof(requiredEndorsements));
            }

            // _openIdMetadata only does a full refresh when the cache expires every 5 days
            OpenIdConnectConfiguration config = null;

            try
            {
                config = await _openIdMetadata.GetConfigurationAsync().ConfigureAwait(false);
            }
            catch (Exception e)
            {
                Trace.TraceError($"Error refreshing OpenId configuration: {e}");

                // No config? We can't continue
                if (config == null)
                {
                    throw;
                }
            }

            // Update the signing tokens from the last refresh
            _tokenValidationParameters.IssuerSigningKeys = config.SigningKeys;
            var tokenHandler = new JwtSecurityTokenHandler();

            try
            {
                var principal      = tokenHandler.ValidateToken(jwtToken, _tokenValidationParameters, out SecurityToken parsedToken);
                var parsedJwtToken = parsedToken as JwtSecurityToken;

                // Validate Channel / Token Endorsements. For this, the channelID present on the Activity
                // needs to be matched by an endorsement.
                var keyId        = (string)parsedJwtToken?.Header?[AuthenticationConstants.KeyIdHeader];
                var endorsements = await _endorsementsData.GetConfigurationAsync();

                // Note: On the Emulator Code Path, the endorsements collection is empty so the validation code
                // below won't run. This is normal.
                if (!string.IsNullOrEmpty(keyId) && endorsements.TryGetValue(keyId, out var endorsementsForKey))
                {
                    // Verify that channelId is included in endorsements
                    var isEndorsed = EndorsementsValidator.Validate(channelId, endorsementsForKey);

                    if (!isEndorsed)
                    {
                        throw new UnauthorizedAccessException($"Could not validate endorsement for key: {keyId} with endorsements: {string.Join(",", endorsementsForKey)}");
                    }

                    // Verify that additional endorsements are satisfied. If no additional endorsements are expected, the requirement is satisfied as well
                    var additionalEndorsementsSatisfied = requiredEndorsements.All(
                        endorsement => EndorsementsValidator.Validate(endorsement, endorsementsForKey));

                    if (!additionalEndorsementsSatisfied)
                    {
                        throw new UnauthorizedAccessException($"Could not validate additional endorsement for key: {keyId} with endorsements: {string.Join(",", endorsementsForKey)}. Expected endorsements: {string.Join(",", requiredEndorsements)}");
                    }
                }

                if (_allowedSigningAlgorithms != null)
                {
                    var algorithm = parsedJwtToken?.Header?.Alg;
                    if (!_allowedSigningAlgorithms.Contains(algorithm))
                    {
                        throw new UnauthorizedAccessException($"Token signing algorithm '{algorithm}' not in allowed list");
                    }
                }

                return(principal);
            }
            catch (SecurityTokenSignatureKeyNotFoundException)
            {
                var keys = string.Join(", ", (config?.SigningKeys ?? Enumerable.Empty <SecurityKey>()).Select(t => t.KeyId));
                Trace.TraceError("Error finding key for token. Available keys: " + keys);
                throw;
            }
        }