How to Customize Authentication in Identity Server 4

by sunil ravulapalli /2. September 2016 06:18 /aspnet-core /Comments (0)
In the configuration folder create a class called MyUser

public class MyUser
{
	public string UserName { get; set; }
	public string Password { get; set; }
}
Also, create a class called MyUserManager

using System.Collections.Generic;
using System.Security.Claims;
using System.Linq;
using System.Threading.Tasks;

namespace IdTest.Configuration
{
    public class MyUserManager
    {
        public Task<MyUser> FindByNameAsync(string username)
        {
            //This is where you would do a database call in real world scenario
            
            //var context = new ApplicationContext();
            //var user = context.MyUsers.SingleOrDefaultAsync(x => x.Username == username)
            var user = GetUsers().SingleOrDefault(x => x.UserName == username);

            return Task.FromResult(user);
        }

        public Task<bool> CheckPasswordAsync(MyUser user, string password)
        {
            //This is where you call a hashing method to verify password
            //var isPasswordMatch = MyPasswordHasher.VerifyHashedPassword(user.PasswordHash, password);

            if (user.Password == password)
                return Task.FromResult(true);

            return Task.FromResult(false);
        }

        private List<MyUser> GetUsers()
        {
            var users = new List<MyUser>();

            users.Add(new MyUser { UserName = "alice", Password = "Bunny11!" });
            users.Add(new MyUser { UserName = "bob", Password = "Bunny11!" });
            users.Add(new MyUser { UserName = "eve", Password = "Bunny11!" });

            return users;
        }
		
		public Task<List<Claim>> GetClaimsAsync(MyUser user)
        {
            //Database call to get calims if needed
            var calims = new List<Claim>();
            calims.Add(new Claim("accountnumber", "12345"));

            return Task.FromResult(calims);
        }

    }
}
Next, add a class called ResourceOwnerPasswordValidator

using IdentityServer4.Validation;
using System.Threading.Tasks;

namespace IdTest.Configuration
{
    public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        private MyUserManager _myUserManager { get; set; }
        public ResourceOwnerPasswordValidator()
        {
            _myUserManager = new MyUserManager();
        }

        public async Task<CustomGrantValidationResult> ValidateAsync(string userName, string password, ValidatedTokenRequest request)
        {
            var user = await _myUserManager.FindByNameAsync(userName);
            if (user != null && await _myUserManager.CheckPasswordAsync(user, password))
            {
                return new CustomGrantValidationResult(user.UserName, "password");
            }
            return new CustomGrantValidationResult("Invalid username or password");
        }
    }
}
Next, add a class called ProfileService

using IdentityServer4.Services;
using System;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Models;
using System.Security.Claims;
using IdentityModel;

namespace IdTest.Configuration
{
    public class ProfileService : IProfileService
    {
        MyUserManager _myUserManager;
        public ProfileService()
        {
            _myUserManager = new MyUserManager();
        }

        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var sub = context.Subject.FindFirst("sub")?.Value;
            if (sub != null)
            {
                var user = await _myUserManager.FindByNameAsync(sub);
                var cp = await getClaims(user);

                var claims = cp.Claims;
                if (context.AllClaimsRequested == false ||
                    (context.RequestedClaimTypes != null && context.RequestedClaimTypes.Any()))
                {
                    claims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToArray().AsEnumerable();
                }

                context.IssuedClaims = claims;
            }
        }

        public Task IsActiveAsync(IsActiveContext context)
        {
            return Task.FromResult(0);
        }

        private async Task<ClaimsPrincipal> getClaims(MyUser user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
                        
            var id = new ClaimsIdentity();            
            id.AddClaim(new Claim(JwtClaimTypes.PreferredUserName, user.UserName));

            id.AddClaims(await _myUserManager.GetClaimsAsync(user));

            return new ClaimsPrincipal(id);
        }

    }
}

Finally, make the following changes to the Startup.cs file in the ConfigureServices method

var builder = services.AddIdentityServer()
                                  .SetSigningCredential(cert)
                                  .AddInMemoryClients(Clients.Get())
                                  .AddInMemoryScopes(Scopes.Get());

builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
builder.Services.AddTransient<IProfileService, ProfileService>();

blog comments powered by Disqus