Actualización: IdentityServer 4 ha cambiado y reemplazado IUserService con IResourceOwnerPasswordValidator e IProfileService
Usé mi UserRepository para obtener todos los datos del usuario de la base de datos. Esto se inyecta (DI) en los constructores y se define en Startup.cs. También creé las siguientes clases para el servidor de identidad (que también se inyecta):
Primero defina ResourceOwnerPasswordValidator.cs:
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
    
    private readonly IUserRepository _userRepository;
    public ResourceOwnerPasswordValidator(IUserRepository userRepository)
    {
        _userRepository = userRepository; 
    }
    
    public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        try
        {
            
            var user = await _userRepository.FindAsync(context.UserName);
            if (user != null)
            {
                
                if (user.Password == context.Password) {
                    
                    context.Result = new GrantValidationResult(
                        subject: user.UserId.ToString(),
                        authenticationMethod: "custom", 
                        claims: GetUserClaims(user));
                    return;
                } 
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password");
                return;
            }
            context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist.");
            return;
        }
        catch (Exception ex)
        {
            context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
        }
    }
    
    public static Claim[] GetUserClaims(User user)
    {
        return new Claim[]
        {
            new Claim("user_id", user.UserId.ToString() ?? ""),
            new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""),
            new Claim(JwtClaimTypes.GivenName, user.Firstname  ?? ""),
            new Claim(JwtClaimTypes.FamilyName, user.Lastname  ?? ""),
            new Claim(JwtClaimTypes.Email, user.Email  ?? ""),
            new Claim("some_claim_you_want_to_see", user.Some_Data_From_User ?? ""),
            
            new Claim(JwtClaimTypes.Role, user.Role)
        };
}
Y ProfileService.cs:
public class ProfileService : IProfileService
{
    
    private readonly IUserRepository _userRepository;
    public ProfileService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }
    
    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        try
        {
            
            if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
            {
                
                var user = await _userRepository.FindAsync(context.Subject.Identity.Name);
                if (user != null)
                {
                    var claims = GetUserClaims(user);
                    
                    context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
                }
            }
            else
            {
                
                
                var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");
                if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
                {
                    
                    var user = await _userRepository.FindAsync(long.Parse(userId.Value));
                    
                    if (user != null)
                    {
                        var claims = ResourceOwnerPasswordValidator.GetUserClaims(user);
                        context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            
        }
    }
    
    public async Task IsActiveAsync(IsActiveContext context)
    {
        try
        {
            
            var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id");
            if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
            {
                var user = await _userRepository.FindAsync(long.Parse(userId.Value));
                if (user != null)
                {
                    if (user.IsActive)
                    {
                        context.IsActive = user.IsActive;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            
        }
    }
}
Luego Startup.cs, hice lo siguiente:
public void ConfigureServices(IServiceCollection services)
{
    
    
    var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "idsrv4test.pfx"), "your_cert_password");
    
    services.AddScoped(_ => new YourDbContext(Configuration.GetConnectionString("DefaultConnection")));
    
    services.AddScoped<IUserRepository, UserRepository>();
    
    services.AddIdentityServer()
        .AddSigningCredential(cert)
        .AddInMemoryIdentityResources(Config.GetIdentityResources()) 
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddProfileService<ProfileService>();
    
    services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
    services.AddTransient<IProfileService, ProfileService>();
    
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    
    app.UseIdentityServer();
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
    {
        
        Authority = "http://localhost:50000/",
        ApiSecret = "secret",
        ApiName = "my.api.resource",
        AutomaticAuthenticate = true,
        SupportedTokens = SupportedTokens.Both,
        
        AutomaticChallenge = true,
        
        RequireHttpsMetadata = false
    };
    app.UseIdentityServerAuthentication(identityServerValidationOptions);
    
}
También necesitará Config.cscuál define sus clientes, api y recursos. Puede encontrar un ejemplo aquí: https://github.com/IdentityServer/IdentityServer4.Demo/blob/master/src/IdentityServer4Demo/Config.cs
Ahora debería poder llamar a IdentityServer / connect / token

Para obtener más información, consulte la documentación: https://media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf
Respuesta anterior (esto ya no funciona para IdentityServer4 más nuevo)
Es bastante simple una vez que comprendes el flujo de las cosas.
Configure su IdentityService así (en Startup.cs - ConfigureServices()):
var builder = services.AddIdentityServer(options =>
{
    options.SigningCertificate = cert;
});
builder.AddInMemoryClients(Clients.Get());
builder.AddInMemoryScopes(Scopes.Get());
builder.Services.AddTransient<IUserService, UserService>();
services.AddTransient<IUserRepository, UserRepository>();
Luego configure su UserService
public class UserService : IUserService
{
    
    private IUserRepository _userRepository;
    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }
    public Task AuthenticateLocalAsync(LocalAuthenticationContext context)
    {
        var user = _userRepository.Find(context.UserName);
        
        
        
        if (user.Password == context.Password)
        {
            context.AuthenticateResult = new AuthenticateResult(
                user.UserId.ToString(),
                user.Email,
                
                new Claim[]
                {
                    
                    new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
                    new Claim(Constants.ClaimTypes.Email, user.Email),
                    new Claim(Constants.ClaimTypes.Role, user.Role.ToString()),
                    
                    new Claim("company", user.Company)
                }
            );
        }
        return Task.FromResult(0);
    }
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        
        var user = _userRepository.Find(context.Subject.Identity.Name);
        if (user != null)
        {
            var claims = new Claim[]
                {
                    new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
                    new Claim(Constants.ClaimTypes.Email, user.Email),
                    new Claim(Constants.ClaimTypes.Role, user.Role.ToString(), ClaimValueTypes.Integer),
                    new Claim("company", user.Company)
            };
            context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type));
        }
        return Task.FromResult(0);
    }
    public Task IsActiveAsync(IsActiveContext context)
    {
        var user = _userRepository.Find(context.Subject.Identity.Name);
        return Task.FromResult(user != null);
    }
}
Básicamente al inyectar UserServiceen builder(de tipo IdentityServerBuilder) Services, le permite llamar al UserService en auth.
Espero que esto ayude a otros, ya que me tomó un par de horas poner esto en marcha.
     
                
IUserServiceen IdSvr4 (para ASP.NET Core 1.0) ya no existe. Ha sido reemplazado por dos interfaces / serviciosIProfileServiceyIResourceOwnerPasswordValidator.context.IssuedClaims = context.Subject.Claims.ToList();en GetProfileData, solo depende de si necesita ocultar algunas reclamaciones al público o si necesita hacer una lógica intermedia al ver los datos del perfil.En IdentityServer4.
IUserServiceya no está disponible, ahora debe usarIResourceOwnerPasswordValidatorpara realizar la autenticación y usarIProfileServicepara obtener las reclamaciones.En mi escenario, uso el tipo de concesión del propietario del recurso, y todo lo que necesito es obtener los reclamos de los usuarios para realizar una autorización basada en roles para mis API web de acuerdo con el nombre de usuario y la contraseña. Y asumí que el tema es único para cada usuario.
He publicado mis códigos a continuación y puede funcionar correctamente; ¿Alguien podría decirme si hay algún problema con mis códigos?
Registre estos dos servicios en startup.cs.
public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer(); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryScopes(Scopes.Get()); builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); builder.Services.AddTransient<IProfileService, ProfileService>(); }Implementa la
IResourceOwnerPasswordValidatorinterfaz.public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { public Task<customgrantvalidationresult> ValidateAsync(string userName, string password, ValidatedTokenRequest request) { // Check The UserName And Password In Database, Return The Subject If Correct, Return Null Otherwise // subject = ...... if (subject == null) { var result = new CustomGrantValidationResult("Username Or Password Incorrect"); return Task.FromResult(result); } else { var result = new CustomGrantValidationResult(subject, "password"); return Task.FromResult(result); } } }Implementa la
ProfileServiceinterfaz.public class ProfileService : IProfileService { public Task GetProfileDataAsync(ProfileDataRequestContext context) { string subject = context.Subject.Claims.ToList().Find(s => s.Type == "sub").Value; try { // Get Claims From Database, And Use Subject To Find The Related Claims, As A Subject Is An Unique Identity Of User //List<string> claimStringList = ...... if (claimStringList == null) { return Task.FromResult(0); } else { List<Claim> claimList = new List<Claim>(); for (int i = 0; i < claimStringList.Count; i++) { claimList.Add(new Claim("role", claimStringList[i])); } context.IssuedClaims = claimList.Where(x => context.RequestedClaimTypes.Contains(x.Type)); return Task.FromResult(0); } } catch { return Task.FromResult(0); } } public Task IsActiveAsync(IsActiveContext context) { return Task.FromResult(0); } }fuente
"IdentityServer4": "1.3.1"En IdentityServer4 1.0.0-rc5 ni IUserService ni CustomGrantValidationResult están disponibles.
Ahora, en lugar de devolver un CustomGrantValidationResult, deberá establecer el context.Result.
public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { private MyUserManager _myUserManager { get; set; } public ResourceOwnerPasswordValidator() { _myUserManager = new MyUserManager(); } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _myUserManager.FindByNameAsync(context.UserName); if (user != null && await _myUserManager.CheckPasswordAsync(user,context.Password)) { context.Result = new GrantValidationResult( subject: "2", authenticationMethod: "custom", claims: someClaimsList); } else { context.Result = new GrantValidationResult( TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return; }Validación de la contraseña del propietario del recurso
fuente