Search code examples
asp.net-coreclaims-based-identityasp.net-identity-2identityserver4

IdentityServer4 and .net Core Identity - Passing/ accessing claims


I have a working IdentityServer and MVC Client in seperate projects, I've also got claims stored against roles in my asp.net identity tables, this is my seed data code for these which are then assigned to users:

  if (await _roleManager.FindByNameAsync("Trainer") == null)
        {
            //Add Traininer Role
            var trainerRole = new IdentityRole("Trainer");

            await _roleManager.CreateAsync(trainerRole);

            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.viewrelated"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.updatestatus"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "contacts.viewrelated"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "locations.viewrelated"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.delete"));
        }

        if (await _roleManager.FindByNameAsync("Booking Management") == null)
        {
            //Add Traininer Role
            var trainerRole = new IdentityRole("Booking Management");

            await _roleManager.CreateAsync(trainerRole);

            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "companies.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "companies.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "companies.edit"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "companies.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "locations.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "locations.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "locations.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "locations.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "contacts.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "contacts.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "contacts.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "contacts.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.updatestatus"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.cancel"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "bookings.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "attendee.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "courses.view"));
        }

        if (await _roleManager.FindByNameAsync("Management") == null)
        {
            //Add Traininer Role
            var trainerRole = new IdentityRole("Management");

            await _roleManager.CreateAsync(trainerRole);

            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "reporting.finance"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "reporting.customers"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "users.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "users.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "users.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "users.delete"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "config.view"));


        }

        if (await _roleManager.FindByNameAsync("Course Management") == null)
        {
            //Add Traininer Role
            var trainerRole = new IdentityRole("Course Management");

            await _roleManager.CreateAsync(trainerRole);

            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "courses.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "courses.create"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "courses.update"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "courses.delete"));

        }

        if (await _roleManager.FindByNameAsync("Admin") == null)
        {
            //Add Traininer Role
            var trainerRole = new IdentityRole("Admin");

            await _roleManager.CreateAsync(trainerRole);

            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "config.view"));
            await _roleManager.AddClaimAsync(trainerRole, new Claim(CustomClaimTypes.Permission, "config.update"));
        }

When I log into identity server my permissions are listIdentity Server User Permissions List

When I login using my MVC client I don't get the permissions in the user Identity Server Client

In my MVC client i want to be able to do something like this in my navigation

  @if (user.HasClaim(CustomClaimTypes.Permission, "config.view"))
        {
        <li>
            <a href="#"><i class="fa fa-cogs"></i> <span class="nav-label" data-i18n="nav.layouts">Configuration Settings</span></a>
            <ul class="nav nav-second-level collapse">
                <li>
                    <a href="/configuration#!/awardingbodies/"> <span class="nav-label" data-i18n="nav.layouts">Awarding Bodies</span></a>
                </li>
            </ul>
        </li>
        }

Ideally I assume checking permissions you don't want to constantly querying the database, so I assume its better to get the permissions with the token but I'm lost how to do this?

EDIT 1: Config of Client in Identity Server (Config.cs)

new Client
            {
                ClientId = "webclientmvc",
                ClientName = "CRM MVC Client",
                AllowedGrantTypes = GrantTypes.Hybrid,
                AlwaysSendClientClaims = true,


                RequireConsent = true,

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris = { "http://localhost:5009/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5009" },

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    StandardScopes.OfflineAccess.Name,
                    StandardScopes.Roles.Name,
                    StandardScopes.AllClaims.Name,
                    "api1",
                    "claims"
                }
            },

Config in MVC Client (startup.cs)

public void Configure(IApplicationBuilder app, IHostingEnvironment env,     ILoggerFactory loggerFactory)
    {
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies",
            AutomaticChallenge = true,
            AutomaticAuthenticate = true,
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = "http://localhost:5000",
            RequireHttpsMetadata = false,

            ClientId = "webclientmvc",
            ClientSecret = "secret",

            ResponseType = "code id_token",
            Scope = { "profile", "api1", "offline_access", "roles" },


            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true


        });


        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
    }

Solution

  • I've got this working now, but wonder if anyone can comment on if its correct. I Implemented my own IProfileService from an example I found online, but then modified the roles part to this:

     if (_userManager.SupportsUserRole)
            {
                var roles = await _userManager.GetRolesAsync(user);
                claims.AddRange(roles.Select(role => new Claim(JwtClaimTypes.Role, role)));
    
                foreach (var item in roles)
                {
                    var role = await _roleManager.FindByNameAsync(item);
                    if (!(role == null))
                    {
                        claims.AddRange(await _roleManager.GetClaimsAsync(role));
                    }
                }
            }
    

    The default implementation doesn't seem to include claims when they are assigned to roles so my solution was to loop through the roles and get the claims and add them, this works and I can now use them in my MVC website.