0

В катана Web API, я использую:Хороший алгоритм для загрузки и кэширования открытого ключа подписи для маркера проверки

appBuilder.UseIdentityServerBearerTokenAuthentication(
    new IdentityServerBearerTokenAuthenticationOptions 
    { 
     Authority = "https://...", 
     ValidationMode = ValidationMode.Local, 
     RequiredScopes = new[] { "..." }, 
    }); 

Это, как представляется, хорошо найти открытого ключа подписи (ов) от органа и (надеюсь,?) кешировать их и т. д. Хотя я и не пробовал, я понимаю, что есть эквивалент для ASP.NET Core.

Теперь мне нужно сделать то же самое, но не в промежуточном программном обеспечении web api. Поэтому я пытаюсь найти код, который IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware использует для этого. Все, что я вижу, это то, что он вызывает UseOAuthBearerAuthentication, который, похоже, находится в Microsoft.Owin.Security.OAuth. Я не смог найти версию этого исходного кода, которая, похоже, соответствует сигнатуре.

Мне кажется, что под обложками кто-то, вероятно, использует System.IdentityModel.Tokens.JwtSecurityTokenHandler и помещает хороший небольшой фрагмент кода в средство эмитентовSigningKeyResolver параметров TokenValidationParameters. Этот милый маленький фрагмент получает ключи подписи из адреса метаданных. Кто-нибудь знает, что это за код, или у него есть что-то хорошее? Очевидно, я мог бы написать это, но мне не нравится изобретать колесо, плюс мой не будет проверен.

ответ

0

Спасибо, leastprivilege. Глядя глубже на ваш класс DiscoverydocumentIssuerSecurityTokenProvider, я нашел ConfigurationManager<OpenIdConnectConfiguration>. Используя это, я получил следующий вспомогательный класс для проверки доступа к токенам вне промежуточного программного обеспечения OWIN.

Отзыв запросил!

using Microsoft.IdentityModel.Protocols; 
using System; 
using System.Collections.Generic; 
using System.IdentityModel.Tokens; 
using System.IO; 
using System.Linq; 
using System.Net.Http; 
using System.Security.Claims; 

namespace WpfClient 
{ 
    public class AccessTokenValidator 
    { 
     protected TokenValidationParameters _accessTokenValidationParameters; 

     public AccessTokenValidator(string stsRoot) 
     { 
      stsRoot = stsRoot.TrimEnd('/'); 
      var discoveryEndpoint = stsRoot + "/.well-known/openid-configuration"; 
      var webHandler = new WebRequestHandler(); 
      var httpClient = new HttpClient(webHandler); 
      var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(discoveryEndpoint, httpClient); 
      _accessTokenValidationParameters = new TokenValidationParameters 
      { 
       ValidateIssuer = true, 
       ValidIssuer = stsRoot, 
       RequireSignedTokens = true, 
       ValidateIssuerSigningKey = true, 
       IssuerSigningKeyResolver = (string token, SecurityToken securityToken, SecurityKeyIdentifier keyIdentifier, TokenValidationParameters validationParameters) => { 
        var signingTokens = configurationManager.GetConfigurationAsync().Result.JsonWebKeySet.GetSigningTokens(); 
        foreach (var signingToken in signingTokens) 
        { 
         foreach (var clause in keyIdentifier) 
         { 
          var key = signingToken.ResolveKeyIdentifierClause(clause); 
          if (key != null) 
          { 
           return key; 
          } 
         } 
        } 
        return null; 
       }, 
       RequireExpirationTime = true, 
       ValidateAudience = false, // See https://github.com/IdentityServer/IdentityServer3/issues/1365: "OAuth2 does not use the term 'audience' ... it instead uses the term 'scope' ... 'audience' and the 'aud' claim are JWT specific concepts." 
       ValidateLifetime = true, 
      }; 
     } 

     public void ValidateAccessToken(string accessToken, IEnumerable<string> requiredScopes, IEnumerable<string> requiredRoles) 
     { 
      ClaimsPrincipal claimsPrincipal; 
      SecurityToken securityToken; 

      var handler = new JwtSecurityTokenHandler(); 
      claimsPrincipal = handler.ValidateToken(accessToken, _accessTokenValidationParameters, out securityToken); 
      if (claimsPrincipal == null) 
      { 
       throw new NullReferenceException("ClaimsPrincipal object returned is null"); 
      } 

      RequireClaims("scope", requiredScopes, claimsPrincipal); 
      RequireClaims("role", requiredRoles, claimsPrincipal); 
     } 

     private static void RequireClaims(string type, IEnumerable<string> requiredValues, ClaimsPrincipal claimsPrincipal) 
     { 
      if (requiredValues != null) 
      { 
       var haveClaims = claimsPrincipal.FindAll(type); 

       var missingRequiredValues = requiredValues.Where(s => !haveClaims.Any(c => c.Value.Equals(s, StringComparison.InvariantCultureIgnoreCase))).ToArray(); 
       if (missingRequiredValues.Any()) 
       { 
        var list = string.Join(", ", missingRequiredValues); 
        throw new InvalidDataException($"Missing required {type} claims: {list}"); 
       } 
      } 
     } 
    } 
}