私はこの同じ問題を数時間にわたって戦い、最終的に解決策を導いていました。この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でトークンを点検し、あなたの結果を参照してください...