@@ -422,6 +422,194 @@ Component authorization approaches are functional at this point. Any of the auth
422422
423423`User .Identity .Name ` is populated in the Client app with the user 's user name , which is usually their sign -in email address .
424424
425+ ## Name and role claim with API authorization
426+
427+ Identity Server can be configured to send `name ` and `role ` claims for authenticated users .
428+
429+ In the Client app , create a custom user factory . Identity Server sends multiple roles as a JSON array in a single `role ` claim . A single role is sent as a string value in the claim . The factory creates an individual `role ` claim for each of the user 's roles .
430+
431+ *CustomUserFactory .cs *:
432+
433+ ```csharp
434+ using System .Linq ;
435+ using System .Security .Claims ;
436+ using System .Text .Json ;
437+ using System .Threading .Tasks ;
438+ using Microsoft .AspNetCore .Components .WebAssembly .Authentication ;
439+ using Microsoft .AspNetCore .Components .WebAssembly .Authentication .Internal ;
440+
441+ public class CustomUserFactory
442+ : AccountClaimsPrincipalFactory < RemoteUserAccount >
443+ {
444+ public CustomUserFactory (IAccessTokenProviderAccessor accessor )
445+ : base (accessor )
446+ {
447+ }
448+
449+ public async override ValueTask < ClaimsPrincipal > CreateUserAsync (
450+ RemoteUserAccount account ,
451+ RemoteAuthenticationUserOptions options )
452+ {
453+ var user = await base .CreateUserAsync (account , options );
454+
455+ if (user .Identity .IsAuthenticated )
456+ {
457+ var identity = (ClaimsIdentity )user .Identity ;
458+ var roleClaims = identity .FindAll (identity .RoleClaimType );
459+
460+ if (roleClaims != null && roleClaims .Any ())
461+ {
462+ foreach (var existingClaim in roleClaims )
463+ {
464+ identity .RemoveClaim (existingClaim );
465+ }
466+
467+ var rolesElem = account .AdditionalProperties [identity .RoleClaimType ];
468+
469+ if (rolesElem is JsonElement roles )
470+ {
471+ if (roles .ValueKind == JsonValueKind .Array )
472+ {
473+ foreach (var role in roles .EnumerateArray ())
474+ {
475+ identity .AddClaim (new Claim (options .RoleClaim , role .GetString ()));
476+ }
477+ }
478+ else
479+ {
480+ identity .AddClaim (new Claim (options .RoleClaim , roles .GetString ()));
481+ }
482+ }
483+ }
484+ }
485+
486+ return user ;
487+ }
488+ }
489+ ```
490+
491+ In the Client app , register the factory in `Program .Main ` (* Program .cs * ):
492+
493+ ```csharp
494+ builder .Services .AddApiAuthorization ()
495+ .AddAccountClaimsPrincipalFactory <RolesClaimsPrincipalFactory >();
496+ ```
497+
498+ * In the Server app , call < xref :Microsoft .AspNetCore .Identity .IdentityBuilder .AddRoles * > on the Identity builder , which adds role - related services :
499+
500+ ```csharp
501+ using Microsoft .AspNetCore .Identity ;
502+
503+ .. .
504+
505+ services .AddDefaultIdentity <ApplicationUser >(options =>
506+ options .SignIn .RequireConfirmedAccount = true )
507+ .AddRoles <IdentityRole >()
508+ .AddEntityFrameworkStores <ApplicationDbContext >();
509+ ```
510+
511+ * In the Server app :
512+
513+ * Configure Identity Server to put the `name ` and `role ` claims into the ID token and access token .
514+ * Prevent the default mapping for roles in the JWT token handler .
515+
516+ ```csharp
517+ using System .IdentityModel .Tokens .Jwt ;
518+ using System .Linq ;
519+
520+ .. .
521+
522+ services .AddIdentityServer ()
523+ .AddApiAuthorization <ApplicationUser , ApplicationDbContext >(options => {
524+ options .IdentityResources [" openid" ].UserClaims .Add (" name" );
525+ options .ApiResources .Single ().UserClaims .Add (" name" );
526+ options .IdentityResources [" openid" ].UserClaims .Add (" role" );
527+ options .ApiResources .Single ().UserClaims .Add (" role" );
528+ });
529+
530+ JwtSecurityTokenHandler .DefaultInboundClaimTypeMap .Remove (" role" );
531+ ```
532+
533+ Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can use a role to authorize the user :
534+
535+ * [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
536+ * [`[Authorize ]` attribute directive ](xref :security / blazor / index #authorize - attribute ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
537+ * [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
538+
539+ Multiple role tests are supported :
540+
541+ ```csharp
542+ if (user .IsInRole (" admin" ) && user .IsInRole (" developer" ))
543+ {
544+ .. .
545+ }
546+ ```
547+
548+ `User .Identity .Name ` is populated in the Client app with the user 's username , which is usually their sign -in email address .
549+
550+ ## Profile Service
551+
552+ In the Server app , create a `ProfileService ` implementation . The Profile Service example in this section creates `name ` and `role ` claims for users similar to the scenario shown in the [Name and role claim with API authorization ](#name - and - role - claim - with - api - authorization ) section . The value of the `role ` claim represents the user 's assigned roles.
553+
554+ * ProfileService .cs * :
555+
556+ ```csharp
557+ using IdentityModel ;
558+ using IdentityServer4 .Models ;
559+ using IdentityServer4 .Services ;
560+ using System .Threading .Tasks ;
561+
562+ public class ProfileService : IProfileService
563+ {
564+ public ProfileService ()
565+ {
566+ }
567+
568+ public Task GetProfileDataAsync (ProfileDataRequestContext context )
569+ {
570+ var nameClaim = context .Subject .FindAll (JwtClaimTypes .Name );
571+ context .IssuedClaims .AddRange (nameClaim );
572+
573+ var roleClaims = context .Subject .FindAll (JwtClaimTypes .Role );
574+ context .IssuedClaims .AddRange (roleClaims );
575+
576+ return Task .CompletedTask ;
577+ }
578+
579+ public Task IsActiveAsync (IsActiveContext context )
580+ {
581+ return Task .CompletedTask ;
582+ }
583+ }
584+ ```
585+
586+ In the Server app , register the Profile Service in `Startup .ConfigureServices `:
587+
588+ ```csharp
589+ using IdentityServer4 .Services ;
590+
591+ .. .
592+
593+ services .AddTransient <IProfileService , ProfileService >();
594+ ```
595+
596+ Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can a role to authorize the user :
597+
598+ * [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
599+ * [`[Authorize ]` attribute directive ](xref :security / blazor / index #authorize - attribute ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
600+ * [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
601+
602+ Multiple role tests are supported :
603+
604+ ```csharp
605+ if (user .IsInRole (" admin" ) && user .IsInRole (" developer" ))
606+ {
607+ .. .
608+ }
609+ ```
610+
611+ `User .Identity .Name ` is populated in the Client app with the user 's user name , which is usually their sign -in email address .
612+
425613[!INCLUDE [](~ / includes / blazor - security / usermanager - signinmanager .md )]
426614
427615[! INCLUDE [](~ / includes / blazor - security / troubleshoot .md )]
0 commit comments