13

I want to use IdentityServer4 with my custom database. I've separate tables for admin and students and both entities have separate rights.

I want to know how to configure IdentityServer EF database to work with my existing DB?

Any help would highly appreciated.

Thanks.

Liam
  • 27,717
  • 28
  • 128
  • 190
Obaid
  • 2,563
  • 17
  • 15

1 Answers1

17

I started with the IdentityServer4 Quick Start sample 7_JavaScriptClient.

First I created a UserManager class that could connect to my data source and validate a user by user name and password and return that user object.

public class UserManager
{
    private SecurityContext _context;

    public UserManager(SecurityContext context)
    {
        _context = context;
    }

    public  Task<User> Find(string username, string password)
    {
         ...Logic to query your custom user table(s)...
    }

    public Task<List<Claim>> GetClaimsAsync(User user)
    {
        var claims = new List<Claim>();

        //custom database call here to where you store your claims.
        var myClaims = ...Your Database call here...
        var claimGroupName = "SomeCustomName";

        if (security != null && security.Count() > 0)
        {
            foreach (var claim in security)
            {
                //Add the value from the field Security_Id from the database to the claim group "SomeCustomName".
                claims.Add(new Claim(claimGroupName , claim.SECURITY_ID));
            }
        }

        return Task.FromResult(claims);


    }
 }

User Object

public class User
{
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public string EMAIL { get; set; }

    ...Other attributes/properties...
}

Second I used the UserManager Object from the AccountController in the sample.

private readonly UserManager _userManager;

public AccountController(
        IIdentityServerInteractionService interaction,
        IClientStore clientStore,
        IHttpContextAccessor httpContextAccessor,
        UserManager userManager
        )
{
    _userManager = userManager;
 }

Third in the AccountController.Login() HTTP Post method I called the UserManager.Find(username, password) to return a user.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
     // validate username/password
     var user = await _userManager.Find(model.Username, model.Password);

     //sign the user in with a subject[user_id] and name[web_id]
     await HttpContext.Authentication.SignInAsync(user.USER_ID, user.WEB_ID, props);
}

Fourth I implemented the IProfileService. [I used this article as a resource.]

public class ProfileService : IProfileService
{
     UserManager _myUserManager;
     private readonly ILogger<ProfileService> _logger;

     public ProfileService(ILogger<ProfileService> logger)
    {
        _logger = logger;
        _myUserManager = new UserManager(new SecurityContext());
    }

    //Called by IdentityServer Middleware.
     public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var sub = context.Subject.FindFirst("sub")?.Value;
        if (sub != null)
        {
            var user = await _myUserManager.FindByNameAsync(sub);

            //Call custom function to get the claims from the custom database.
            var cp = await   getClaims(user);

            var claims = cp.Claims;

            ...Optionaly remove any claims that don't need to be sent...

            context.IssuedClaims = claims.ToList();
        }
     }

     //Called by IdentityServer Middleware.
     public async Task IsActiveAsync(IsActiveContext context)
    {
        var sub = context.Subject.GetSubjectId();
        var user = await _myUserManager.FindByNameAsync(sub);
        context.IsActive = user != null;
        return;
    }

    //Custom function to get claims from database via the UserManager.GetClaimsAsync() method.
    private async Task<ClaimsPrincipal> getClaims(User user)
    {
       var id = new ClaimsIdentity();
       //set any standard claims
       id.AddClaim(new Claim(JwtClaimTypes.PreferredUserName, user.USER_ID));
       //get custom claims from database or other source.
       id.AddClaims(await _myUserManager.GetClaimsAsync(user));

       return new ClaimsPrincipal(id);
    }
}

Lastly in the Startup.cs setup any Objects for Dependency Injection.

public void ConfigureServices(IServiceCollection services)
{
   builder.Services.AddTransient<IProfileService, ProfileService>();

   //This is the DbContext to our Database where the users and their claims are stored.
   services.AddTransient<SecurityContext>();
   services.AddTransient<UserManager>();

 }

Here is a similar question and anser as well.

Note that they also reference the IResourceOwnerPasswordValidator interface and implementation which is for authenticating OAuth 2.0 resource owner password credential grant (aka password). Used with GrantTypes.ResourceOwnerPasswordAndClientCredentials or GrantTypes.ResourceOwnerPassword.

Community
  • 1
  • 1
aaronR
  • 1,557
  • 2
  • 16
  • 26
  • Thanks a lot for your detailed reply :-) I wanted to know how can I build signup screen or login screen using AngularJS and don't redirect the users to IdentityServer. Any help will be highly appreciated. Thanks. – Obaid Feb 23 '17 at 05:04
  • @OBA That appears to be a separate question. I would recommend that you post that one to the site. When you say you don't want to redirect the users to the IdentityServer does that mean just for the login screen where you want your application to 'own' the login screen? – aaronR Feb 23 '17 at 15:27
  • @OBA Look [here](https://damienbod.com/2016/10/01/identityserver4-webapi-and-angular2-in-a-single-asp-net-core-project/) for a sample project using Angular2. You may want to become more familiar with the IdentityServer4 Flows, which allow for different types of sign in processes. – aaronR Feb 23 '17 at 15:48
  • Thanks for your reply. I found a github project that uses Resource Owner Password Credentials (ROPC) Identity Server Grant type to achieve what I wanted. Here is the link for anyone interested https://github.com/robisim74/AngularSPAWebAPI Thanks. – Obaid Feb 24 '17 at 04:40
  • 2
    Please note that that process flow is not recommended and should be avoided if at all possible. – aaronR Feb 24 '17 at 05:30