0

In einem katana web api, ich verwende:Guter Algorithmus für die Herunterladen und das Caching öffentlichen Signaturschlüssels für Token-Validierung

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

Dies scheint den öffentlichen Signaturschlüssel (n) von der Behörde schön zu finden und (hoffentlich?) cache sie, etc. Obwohl ich es nicht ausprobiert habe, verstehe ich, dass es ein Äquivalent für ASP.NET Core gibt.

Jetzt muss ich das gleiche tun, aber nicht in einer Web-API-Middleware. Ich versuche also, den Code zu finden, der von IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware verwendet wird. Alles, was ich sehen kann, ist, dass es UseOAuthBearerAuthentication aufruft, was in Microsoft.Owin.Security.OAuth zu sein scheint. Ich konnte keine Version dieses Quellcodes finden, die mit der Signatur übereinstimmt.

Es scheint mir, dass unter den Abdeckungen jemand wahrscheinlich System.IdentityModel.Tokens.JwtSecurityTokenHandler verwendet und ein nettes kleines Code-Snippet in den IssuerSigningKeyResolver der TokenValidationParameters steckt. Dieses nette kleine Snippet erhält die Signaturschlüssel von der Metadatenadresse. Wer weiß, was dieser Code ist, oder ein Stück, das gut funktioniert? Natürlich könnte ich es schreiben, aber ich hasse es, das Rad neu zu erfinden, und meins wäre nicht getestet.

Antwort

0

Danke, leastprivilege. Wenn Sie Ihre Klasse DiscoverydocumentIssuerSecurityTokenProvider genauer betrachten, habe ich ConfigurationManager<OpenIdConnectConfiguration> gefunden. Damit habe ich die folgende Hilfsklasse für die Validierung von Zugriffstoken außerhalb der OWIN-Middleware entwickelt.

Rückmeldung erbeten!

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}"); 
       } 
      } 
     } 
    } 
} 
Verwandte Themen