4

トークンに追加するクレームを追加するにはどうすればよいですか?IdentityServer4でASP.Net IDを使用してaccess_tokenに追加するクレームを追加する方法

APIがベアラトークンを受信するとすぐに、User.Identityオブジェクトには次のような主張が設定されます。

[ 
    { 
    "key": "nbf", 
    "value": "1484614344" 
    }, 
    { 
    "key": "exp", 
    "value": "1484615244" 
    }, 
    { 
    "key": "iss", 
    "value": "http://localhost:85" 
    }, 
    { 
    "key": "aud", 
    "value": "http://localhost:85/resources" 
    }, 
    { 
    "key": "aud", 
    "value": "WebAPI" 
    }, 
    { 
    "key": "client_id", 
    "value": "MyClient" 
    }, 
    { 
    "key": "sub", 
    "value": "d74c815a-7ed3-4671-b4e4-faceb0854bf6" 
    }, 
    { 
    "key": "auth_time", 
    "value": "1484611732" 
    }, 
    { 
    "key": "idp", 
    "value": "local" 
    }, 
    { 
    "key": "role", 
    "value": "AccountsManager" 
    }, 
    { 
    "key": "scope", 
    "value": "openid" 
    }, 
    { 
    "key": "scope", 
    "value": "profile" 
    }, 
    { 
    "key": "scope", 
    "value": "roles" 
    }, 
    { 
    "key": "scope", 
    "value": "WebAPI" 
    }, 
    { 
    "key": "scope", 
    "value": "offline_access" 
    }, 
    { 
    "key": "amr", 
    "value": "pwd" 
    } 
] 

私はusername, email, legacySystemUserIdのような追加の請求をしたい、などが挙げられる。これらのフィールドは、すでにAspNetUsers表に存在し(と繰り返しAspNetUserClaimsテーブルに存在しない)と私のApplicationUserオブジェクト内のASP。ネットCoreアプリケーションで利用できます。

ユーザー名とパスワードで認証した後に返されるアクセストークンにそれらを含める必要があります。アイデンティティ・サーバー・データベースへのアクセス権を持たないWebAPIアプリケーションでも同じものを使用したいと思います。独自のデータベースには、UserIdではなくユーザーの電子メール・アドレスに基づいてデータが格納されています(ASP .NET IDで生成され、 SUBクレーム)。

答えて

11

私はこの同じ問題を数時間にわたって戦い、最終的に解決策を導いていました。このarticleは大きな助けとなりましたが、私の実装を要約して共有するには:

ユーザーにクレームを割り当ててアクセストークンに添付するには、IDサーバーに2つのインターフェイス、つまりIResourceOwnerPasswordValidatorを実装する必要があります。 IProfileService。以下は私の2つのクラスの実装であり、原案ですが、動作します。

**この時点で最新のIdentityServer4 - 1.0.2を取得してください。

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator 
{ 
    private readonly UserManager<ApplicationUser> _userManager; 

    public ResourceOwnerPasswordValidator(UserManager<ApplicationUser> userManager) 
    { 
     _userManager = userManager; 
    } 

    public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) 
    { 
     var userTask = _userManager.FindByNameAsync(context.UserName); 
     var user = userTask.Result; 

     context.Result = new GrantValidationResult(user.Id, "password", null, "local", null); 
     return Task.FromResult(context.Result); 
    } 
} 

public class AspNetIdentityProfileService : IProfileService 
{ 
    private readonly UserManager<ApplicationUser> _userManager; 

    public AspNetIdentityProfileService(UserManager<ApplicationUser> userManager) 
    { 
     _userManager = userManager; 
    } 

    public async Task GetProfileDataAsync(ProfileDataRequestContext context) 
    { 
     var subject = context.Subject; 
     if (subject == null) throw new ArgumentNullException(nameof(context.Subject)); 

     var subjectId = subject.GetSubjectId(); 

     var user = await _userManager.FindByIdAsync(subjectId); 
     if (user == null) 
      throw new ArgumentException("Invalid subject identifier"); 

     var claims = await GetClaimsFromUser(user); 

     var siteIdClaim = claims.SingleOrDefault(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"); 
     context.IssuedClaims.Add(new Claim(JwtClaimTypes.Email, user.Email)); 
     context.IssuedClaims.Add(new Claim("siteid", siteIdClaim.Value)); 
     context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, "User")); 

     var roleClaims = claims.Where(x => x.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"); 
     foreach (var roleClaim in roleClaims) 
     { 
      context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, roleClaim.Value)); 
     } 
    } 

    public async Task IsActiveAsync(IsActiveContext context) 
    { 
     var subject = context.Subject; 
     if (subject == null) throw new ArgumentNullException(nameof(context.Subject)); 

     var subjectId = subject.GetSubjectId(); 
     var user = await _userManager.FindByIdAsync(subjectId); 

     context.IsActive = false; 

     if (user != null) 
     { 
      if (_userManager.SupportsUserSecurityStamp) 
      { 
       var security_stamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault(); 
       if (security_stamp != null) 
       { 
        var db_security_stamp = await _userManager.GetSecurityStampAsync(user); 
        if (db_security_stamp != security_stamp) 
         return; 
       } 
      } 

      context.IsActive = 
       !user.LockoutEnabled || 
       !user.LockoutEnd.HasValue || 
       user.LockoutEnd <= DateTime.Now; 
     } 
    } 

    private async Task<IEnumerable<Claim>> GetClaimsFromUser(ApplicationUser user) 
    { 
     var claims = new List<Claim> 
     { 
      new Claim(JwtClaimTypes.Subject, user.Id), 
      new Claim(JwtClaimTypes.PreferredUserName, user.UserName) 
     }; 

     if (_userManager.SupportsUserEmail) 
     { 
      claims.AddRange(new[] 
      { 
       new Claim(JwtClaimTypes.Email, user.Email), 
       new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean) 
      }); 
     } 

     if (_userManager.SupportsUserPhoneNumber && !string.IsNullOrWhiteSpace(user.PhoneNumber)) 
     { 
      claims.AddRange(new[] 
      { 
       new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber), 
       new Claim(JwtClaimTypes.PhoneNumberVerified, user.PhoneNumberConfirmed ? "true" : "false", ClaimValueTypes.Boolean) 
      }); 
     } 

     if (_userManager.SupportsUserClaim) 
     { 
      claims.AddRange(await _userManager.GetClaimsAsync(user)); 
     } 

     if (_userManager.SupportsUserRole) 
     { 
      var roles = await _userManager.GetRolesAsync(user); 
      claims.AddRange(roles.Select(role => new Claim(JwtClaimTypes.Role, role))); 
     } 

     return claims; 
    } 
} 

あなたがそれらを持っていたら、彼らはstartup.csであなたのサービスに追加する必要があります。ここでは

services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); 
services.AddTransient<IProfileService, AspNetIdentityProfileService>(); 

は、私の設定で簡単に見ています:

public static IEnumerable<IdentityResource> GetIdentityResources() 
{ 
    return new List<IdentityResource> 
    { 
     new IdentityResources.OpenId() 
    }; 
} 

public static IEnumerable<ApiResource> GetApiResources() 
{ 
    return new List<ApiResource> 
    { 
     new ApiResource 
     { 
      Name = "api1", 
      Description = "My Api", 
      Scopes = 
      { 
       new Scope() 
       { 
        Name = "api1", 
        DisplayName = "Full access to Api" 
       } 
      } 
     } 
    }; 
} 

public static IEnumerable<Client> GetClients() 
{ 
    return new List<Client> 
    { 
     new Client 
     { 
      ClientId = "apiClient", 
      ClientName = "Api Angular2 Client", 
      AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, 
      AlwaysSendClientClaims = true, 
      AlwaysIncludeUserClaimsInIdToken = true, 
      ClientSecrets = 
      { 
       new Secret("secret".Sha256()) 
      }, 

      AllowedScopes = 
      { 
       "api1" 
      } 
     } 
    }; 
} 
その後

、クライアントからIDサーバへの呼び出し:

var discoTask = DiscoveryClient.GetAsync("http://localhost:5000"); 
var disco = discoTask.Result; 

var tokenClient = new TokenClient(disco.TokenEndpoint, "apiClient", "secret"); 
var tokenResponseTask = tokenClient.RequestResourceOwnerPasswordAsync("[email protected]", "my-password", "api1"); 

var tokenResponse = tokenResponseTask.Result; 
var accessToken = tokenResponse.AccessToken; 

if (tokenResponse.IsError) 
{ 
    Console.WriteLine(tokenResponse.Error); 
    return; 
} 

がjwt.ioでトークンを点検し、あなたの結果を参照してください...

関連する問題