How to add custom claims to Jwt Token in OpenIdConnect

Ashutosh Arun Pareek 76 Reputation points
2021-04-27T10:11:46.18+00:00

Just like AzureAD we have our own custom Firm ActiveDirectory which we are connecting from UI as well as API for Authentication in .NetCore using OpenIdConnect (AddOpenIdConnect extension method).
In my use case after authentication on UI side, I need additional application specific claims from my custom database which I am adding "OnTokenValidated" - this is needed for hiding or exposing the UI elements based on Roles and Claims.

OnTokenValidated = async ctx =>
            {
                //Get user's immutable object id from claims that came from Azure AD
                string oid = ctx.Principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");

                //Get EF context
                var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();

                //Check is user a super admin
                bool isSuperAdmin = await db.SuperAdmins.AnyAsync(a => a.ObjectId == oid);
                if (isSuperAdmin)
                {
                    //Add claim if they are
                    var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Role, "superadmin")
                    };
                    var appIdentity = new ClaimsIdentity(claims);

                    ctx.Principal.AddIdentity(appIdentity);
                }
            }

Now after token validation on API side again I have to call the custom database to fetch application specific roles. Is it possible to include these roles in JWT token itself so on API side all roles and claims(AD + Custom DB) are present. Or any other way by which I don't have to call CustomDB again in API.

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,059 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,202 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,053 questions
{count} votes

Accepted answer
  1. mobiletonster 116 Reputation points
    2021-04-29T16:18:17.297+00:00

    Hi @Ashutosh Arun Pareek
    I'm not sure what is meant by this statement: "custom Firm ActiveDirectory". If you mean that your organization has your own instance of ActiveDirectory and you have control of it, I recommend that you consider using ActiveDirectory as your "database" to describe roles for a user. It is well designed to have attributes added that describe a user's permissions/roles. Then allow it to include those permissions/roles in the JWT token that it creates.

    If you don't have control of the Active Directory instance, or for some reason this just isn't possible due to organizational policy, etc. then I would ask if it is possible to use Cookie based authentication? Is your UI and your API being hosted together? separately? from the same domain? If they are being hosted on the same domain (no CORS, etc.) and the API is designed specifically to support the UI then I see no reason to convert from JWT to Cookie based authentication instead.

    I have put together a video that explains how to use Cookie based authentication in ASP.NET Core and why you would want to use it:

    Authentication/Authorization in ASP.NET Core - Part 1

    In part 2 of the series I demonstrate how to combine using Cookie based auth with OpenId Connect providers:

    Authentication in ASP.NET Core - Part 2 (openid)

    If your API is truly unrelated to the UI and receiving a JWT is your only option, then I would consider using an In memory cache to reduce hits to the database for the same information.

    using Microsoft.Extensions.Caching.Memory;  
    using System;  
    using System.Threading.Tasks;  
      
    namespace YourApp.Services  
    {  
        public class AppUserService  
        {  
            private IMemoryCache _cache;  
            private AuthorizationDbContext _db;  
            public AppUserService(IMemoryCache cache, AuthorizationDbContext db)  
            {  
                _cache = cache;  
                _db = db;  
            }  
            public async Task<bool> IsSuperAdminAsync(string oid)  
            {  
                if (_cache.TryGetValue(oid, out bool isSuperAdmin))  
                {  
                    return isSuperAdmin;  
                }  
                else  
                {  
                    isSuperAdmin = await _db.SuperAdmins.AnyAsync(a => a.ObjectId == oid);  
                    _cache.Set(oid, isSuperAdmin, DateTimeOffset.Now.AddMinutes(30)); // keep it around for 30 mins.  
                    return isSuperAdmin;  
                }  
            }  
        }  
    }  
    

    If you application is running on multiple instances, you may want to use a shared memory cache like a rediscache or something similar. In this sample code I set the cache for a specific user to be limited to 30 mins of life....so at most a user would hit the database for his role once every 30 mins. You can adjust depending on your use case.

    This will use memory, so if your application has thousands of simultaneous users, you may not want to use IMemoryCache, but rather back with a shared cache provider instead. If you have relatively few users, IMemoryCache should work fine.

    Additionally, I don't have a video for how to manage custom attributes in Active Directory, but I do demonstrate something similar with Okta as a provider in part 3 of the Authentication series: Authentication in ASP.NET Core - part 3 (IDaaS) and multiproviders

    I hope that helps.

    2 people found this answer helpful.

0 additional answers

Sort by: Most helpful