@@ -230,7 +230,7 @@ Run the app from the Server project. When using Visual Studio, either:
230230
231231## Name and role claim with API authorization
232232
233- Identity Server can be configured to send ` name ` and ` role ` claims for authenticated users .
233+ ### Custom user factory
234234
235235In 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 .
236236
@@ -298,252 +298,56 @@ In the Client app, register the factory in `Program.Main` (*Program.cs*):
298298
299299```csharp
300300builder .Services .AddApiAuthorization ()
301- .AddAccountClaimsPrincipalFactory <RolesClaimsPrincipalFactory >();
301+ .AddAccountClaimsPrincipalFactory <CustomUserFactory >();
302302```
303303
304- * In the Server app , call < xref :Microsoft .AspNetCore .Identity .IdentityBuilder .AddRoles * > on the Identity builder , which adds role - related services :
305-
306- ```csharp
307- using Microsoft .AspNetCore .Identity ;
308-
309- .. .
310-
311- services .AddDefaultIdentity <ApplicationUser >(options =>
312- options .SignIn .RequireConfirmedAccount = true )
313- .AddRoles <IdentityRole >()
314- .AddEntityFrameworkStores <ApplicationDbContext >();
315- ```
316-
317- * In the Server app :
318-
319- * Configure Identity Server to put the `name ` and `role ` claims into the ID token and access token .
320- * Prevent the default mapping for roles in the JWT token handler .
321-
322- ```csharp
323- using System .IdentityModel .Tokens .Jwt ;
324- using System .Linq ;
325-
326- .. .
327-
328- services .AddIdentityServer ()
329- .AddApiAuthorization <ApplicationUser , ApplicationDbContext >(options => {
330- options .IdentityResources [" openid" ].UserClaims .Add (" name" );
331- options .ApiResources .Single ().UserClaims .Add (" name" );
332- options .IdentityResources [" openid" ].UserClaims .Add (" role" );
333- options .ApiResources .Single ().UserClaims .Add (" role" );
334- });
335-
336- JwtSecurityTokenHandler .DefaultInboundClaimTypeMap .Remove (" role" );
337- ```
338-
339- Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can use a role to authorize the user :
340-
341- * [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
342- * [`[Authorize ]`] attribute directive ](xref :security / blazor / index #authorize - attribute ) (< xref :Microsoft .AspNetCore .Authorization .AuthorizeAttribute > ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
343- * [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
344-
345- Multiple role tests are supported :
346-
347- ```csharp
348- if (user .IsInRole (" admin" ) && user .IsInRole (" developer" ))
349- {
350- .. .
351- }
352- ```
353-
354- `User .Identity .Name ` is populated in the Client app with the user 's username , which is usually their sign -in email address .
355-
356- ## Profile Service
357-
358- 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.
359-
360- * ProfileService .cs * :
361-
362- ```csharp
363- using IdentityModel ;
364- using IdentityServer4 .Models ;
365- using IdentityServer4 .Services ;
366- using System .Threading .Tasks ;
367-
368- public class ProfileService : IProfileService
369- {
370- public ProfileService ()
371- {
372- }
373-
374- public Task GetProfileDataAsync (ProfileDataRequestContext context )
375- {
376- var nameClaim = context .Subject .FindAll (JwtClaimTypes .Name );
377- context .IssuedClaims .AddRange (nameClaim );
378-
379- var roleClaims = context .Subject .FindAll (JwtClaimTypes .Role );
380- context .IssuedClaims .AddRange (roleClaims );
381-
382- return Task .CompletedTask ;
383- }
384-
385- public Task IsActiveAsync (IsActiveContext context )
386- {
387- return Task .CompletedTask ;
388- }
389- }
390- ```
391-
392- In the Server app , register the Profile Service in `Startup .ConfigureServices `:
304+ In the Server app , call < xref :Microsoft .AspNetCore .Identity .IdentityBuilder .AddRoles * > on the Identity builder , which adds role - related services :
393305
394306```csharp
395- using IdentityServer4 . Services ;
307+ using Microsoft . AspNetCore . Identity ;
396308
397309.. .
398310
399- services .AddTransient <IProfileService , ProfileService >();
311+ services .AddDefaultIdentity <ApplicationUser >(options =>
312+ options .SignIn .RequireConfirmedAccount = true )
313+ .AddRoles <IdentityRole >()
314+ .AddEntityFrameworkStores <ApplicationDbContext >();
400315```
401316
402- Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can a role to authorize the user :
317+ ### Configure Identity Server
403318
404- * [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
405- * [`[Authorize ]`] attribute directive ](xref :security / blazor / index #authorize - attribute ) (< xref :Microsoft .AspNetCore .Authorization .AuthorizeAttribute > ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
406- * [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
319+ Use ** one ** of the following approaches :
407320
408- Multiple role tests are supported :
321+ * [API authorization options ](#api - authorization - options )
322+ * [Profile Service ](#profile - service )
409323
410- ```csharp
411- if (user .IsInRole (" admin" ) && user .IsInRole (" developer" ))
412- {
413- .. .
414- }
415- ```
324+ #### API authorization options
416325
417- `User .Identity .Name ` is populated in the Client app with the user 's user name , which is usually their sign -in email address .
418-
419- ## Name and role claim with API authorization
420-
421- Identity Server can be configured to send `name ` and `role ` claims for authenticated users .
422-
423- 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 .
326+ In the Server app :
424327
425- *CustomUserFactory .cs *:
328+ * Configure Identity Server to put the `name ` and `role ` claims into the ID token and access token .
329+ * Prevent the default mapping for roles in the JWT token handler .
426330
427331```csharp
332+ using System .IdentityModel .Tokens .Jwt ;
428333using System .Linq ;
429- using System .Security .Claims ;
430- using System .Text .Json ;
431- using System .Threading .Tasks ;
432- using Microsoft .AspNetCore .Components .WebAssembly .Authentication ;
433- using Microsoft .AspNetCore .Components .WebAssembly .Authentication .Internal ;
434-
435- public class CustomUserFactory
436- : AccountClaimsPrincipalFactory < RemoteUserAccount >
437- {
438- public CustomUserFactory (IAccessTokenProviderAccessor accessor )
439- : base (accessor )
440- {
441- }
442-
443- public async override ValueTask < ClaimsPrincipal > CreateUserAsync (
444- RemoteUserAccount account ,
445- RemoteAuthenticationUserOptions options )
446- {
447- var user = await base .CreateUserAsync (account , options );
448-
449- if (user .Identity .IsAuthenticated )
450- {
451- var identity = (ClaimsIdentity )user .Identity ;
452- var roleClaims = identity .FindAll (identity .RoleClaimType );
453-
454- if (roleClaims != null && roleClaims .Any ())
455- {
456- foreach (var existingClaim in roleClaims )
457- {
458- identity .RemoveClaim (existingClaim );
459- }
460-
461- var rolesElem = account .AdditionalProperties [identity .RoleClaimType ];
462-
463- if (rolesElem is JsonElement roles )
464- {
465- if (roles .ValueKind == JsonValueKind .Array )
466- {
467- foreach (var role in roles .EnumerateArray ())
468- {
469- identity .AddClaim (new Claim (options .RoleClaim , role .GetString ()));
470- }
471- }
472- else
473- {
474- identity .AddClaim (new Claim (options .RoleClaim , roles .GetString ()));
475- }
476- }
477- }
478- }
479334
480- return user ;
481- }
482- }
483- ```
335+ .. .
484336
485- In the Client app , register the factory in `Program .Main ` (* Program .cs * ):
337+ services .AddIdentityServer ()
338+ .AddApiAuthorization <ApplicationUser , ApplicationDbContext >(options => {
339+ options .IdentityResources [" openid" ].UserClaims .Add (" name" );
340+ options .ApiResources .Single ().UserClaims .Add (" name" );
341+ options .IdentityResources [" openid" ].UserClaims .Add (" role" );
342+ options .ApiResources .Single ().UserClaims .Add (" role" );
343+ });
486344
487- ```csharp
488- builder .Services .AddApiAuthorization ()
489- .AddAccountClaimsPrincipalFactory <RolesClaimsPrincipalFactory >();
345+ JwtSecurityTokenHandler .DefaultInboundClaimTypeMap .Remove (" role" );
490346```
491347
492- * In the Server app , call < xref :Microsoft .AspNetCore .Identity .IdentityBuilder .AddRoles * > on the Identity builder , which adds role - related services :
493-
494- ```csharp
495- using Microsoft .AspNetCore .Identity ;
496-
497- .. .
498-
499- services .AddDefaultIdentity <ApplicationUser >(options =>
500- options .SignIn .RequireConfirmedAccount = true )
501- .AddRoles <IdentityRole >()
502- .AddEntityFrameworkStores <ApplicationDbContext >();
503- ```
504-
505- * In the Server app :
506-
507- * Configure Identity Server to put the `name ` and `role ` claims into the ID token and access token .
508- * Prevent the default mapping for roles in the JWT token handler .
509-
510- ```csharp
511- using System .IdentityModel .Tokens .Jwt ;
512- using System .Linq ;
513-
514- .. .
515-
516- services .AddIdentityServer ()
517- .AddApiAuthorization <ApplicationUser , ApplicationDbContext >(options => {
518- options .IdentityResources [" openid" ].UserClaims .Add (" name" );
519- options .ApiResources .Single ().UserClaims .Add (" name" );
520- options .IdentityResources [" openid" ].UserClaims .Add (" role" );
521- options .ApiResources .Single ().UserClaims .Add (" role" );
522- });
348+ #### Profile Service
523349
524- JwtSecurityTokenHandler .DefaultInboundClaimTypeMap .Remove (" role" );
525- ```
526-
527- Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can use a role to authorize the user :
528-
529- * [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
530- * [`[Authorize ]`] attribute directive ](xref :security / blazor / index #authorize - attribute ) (< xref :Microsoft .AspNetCore .Authorization .AuthorizeAttribute > ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
531- * [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
532-
533- Multiple role tests are supported :
534-
535- ```csharp
536- if (user .IsInRole (" admin" ) && user .IsInRole (" developer" ))
537- {
538- .. .
539- }
540- ```
541-
542- `User .Identity .Name ` is populated in the Client app with the user 's username , which is usually their sign -in email address .
543-
544- ## Profile Service
545-
546- 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.
350+ In the Server app , create a `ProfileService ` implementation .
547351
548352* ProfileService .cs * :
549353
@@ -587,10 +391,12 @@ using IdentityServer4.Services;
587391services .AddTransient <IProfileService , ProfileService >();
588392```
589393
590- Component authorization approaches are functional at this point . Any of the authorization mechanisms in components can a role to authorize the user :
394+ ### Use authorization mechanisms
395+
396+ In the Client app , component authorization approaches are functional at this point . Any of the authorization mechanisms in components can use a role to authorize the user :
591397
592398* [AuthorizeView component ](xref :security / blazor / index #authorizeview - component ) (Example : `< AuthorizeView Roles = " admin" > `)
593- * [`[Authorize ]`] attribute directive ](xref :security / blazor / index #authorize - attribute ) (< xref :Microsoft .AspNetCore .Authorization .AuthorizeAttribute > ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
399+ * [`[Authorize ]` attribute directive ](xref :security / blazor / index #authorize - attribute ) (< xref :Microsoft .AspNetCore .Authorization .AuthorizeAttribute > ) (Example : `@attribute [Authorize (Roles = " admin" )]`)
594400* [Procedural logic ](xref :security / blazor / index #procedural - logic ) (Example : `if (user .IsInRole (" admin" )) { .. . }`)
595401
596402 Multiple role tests are supported :
0 commit comments