diff --git a/IdentityServer/v7/docs/content/apis/aspnetcore/authorization.md b/IdentityServer/v7/docs/content/apis/aspnetcore/authorization.md
index 592eb1ad..6403e201 100644
--- a/IdentityServer/v7/docs/content/apis/aspnetcore/authorization.md
+++ b/IdentityServer/v7/docs/content/apis/aspnetcore/authorization.md
@@ -4,9 +4,9 @@ date: 2020-09-10T08:22:12+02:00
weight: 30
---
-The access token will include additional claims that can be used for authorization, e.g. the *scope* claim will reflect the scope the client requested (and was granted) during the token request.
+The access token will include additional claims that can be used for authorization, e.g. the _scope_ claim will reflect the scope the client requested (and was granted) during the token request.
-In ASP.NET core, the contents of the JWT payload get transformed into claims and packaged up in a *ClaimsPrincipal*. So you can always write custom validation or authorization logic in C#:
+In ASP.NET core, the contents of the JWT payload get transformed into claims and packaged up in a _ClaimsPrincipal_. So you can always write custom validation or authorization logic in C#:
```cs
public IActionResult Get()
@@ -22,23 +22,17 @@ For better encapsulation and re-use, consider using the ASP.NET Core [authorizat
With this approach, you would first turn the claim requirement(s) into a named policy:
```cs
-public void ConfigureServices(IServiceCollection services)
+builder.Services.AddAuthorization(options =>
{
- services.AddAuthorization(options =>
- {
- options.AddPolicy("read_access", policy =>
- policy.RequireClaim("scope", "read");
- });
-}
+ options.AddPolicy("read_access", policy =>
+ policy.RequireClaim("scope", "read");
+});
```
..and then enforce it, e.g. using the routing table:
```cs
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers().RequireAuthorization("read_access");
- });
+app.MapControllers().RequireAuthorization("read_access");
```
...or imperatively inside the controller:
@@ -78,47 +72,43 @@ public class DataController : ControllerBase
```
#### Scope claim format
-Historically, Duende IdentityServer emitted the *scope* claims as an array in the JWT. This works very well with the .NET deserialization logic, which turns every array item into a separate claim of type *scope*.
-The newer *JWT Profile for OAuth* [spec]({{< ref "/overview/specs" >}}) mandates that the scope claim is a single space delimited string. You can switch the format by setting the *EmitScopesAsSpaceDelimitedStringInJwt* on the [options]({{< ref "/reference/options" >}}). But this means that the code consuming access tokens might need to be adjusted. The following code can do a conversion to the *multiple claims* format that .NET prefers:
+Historically, Duende IdentityServer emitted the _scope_ claims as an array in the JWT. This works very well with the .NET deserialization logic, which turns every array item into a separate claim of type _scope_.
+
+The newer _JWT Profile for OAuth_ [spec]({{< ref "/overview/specs" >}}) mandates that the scope claim is a single space delimited string. You can switch the format by setting the _EmitScopesAsSpaceDelimitedStringInJwt_ on the [options]({{< ref "/reference/options" >}}). But this means that the code consuming access tokens might need to be adjusted. The following code can do a conversion to the _multiple claims_ format that .NET prefers:
```cs
-namespace IdentityModel.AspNetCore.AccessTokenValidation
+namespace IdentityModel.AspNetCore.AccessTokenValidation;
+
+///
+/// Logic for normalizing scope claims to separate claim types
+///
+public static class ScopeConverter
{
///
/// Logic for normalizing scope claims to separate claim types
///
- public static class ScopeConverter
+ ///
+ ///
+ public static ClaimsPrincipal NormalizeScopeClaims(this ClaimsPrincipal principal)
{
- ///
- /// Logic for normalizing scope claims to separate claim types
- ///
- ///
- ///
- public static ClaimsPrincipal NormalizeScopeClaims(this ClaimsPrincipal principal)
+ var identities = new List();
+
+ foreach (var id in principal.Identities)
{
- var identities = new List();
+ var identity = new ClaimsIdentity(id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
- foreach (var id in principal.Identities)
+ foreach (var claim in id.Claims)
{
- var identity = new ClaimsIdentity(id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
-
- foreach (var claim in id.Claims)
+ if (claim.Type == "scope")
{
- if (claim.Type == "scope")
+ if (claim.Value.Contains(' '))
{
- if (claim.Value.Contains(' '))
- {
- var scopes = claim.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ var scopes = claim.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
- foreach (var scope in scopes)
- {
- identity.AddClaim(new Claim("scope", scope, claim.ValueType, claim.Issuer));
- }
- }
- else
+ foreach (var scope in scopes)
{
- identity.AddClaim(claim);
+ identity.AddClaim(new Claim("scope", scope, claim.ValueType, claim.Issuer));
}
}
else
@@ -126,12 +116,16 @@ namespace IdentityModel.AspNetCore.AccessTokenValidation
identity.AddClaim(claim);
}
}
-
- identities.Add(identity);
+ else
+ {
+ identity.AddClaim(claim);
+ }
}
-
- return new ClaimsPrincipal(identities);
+
+ identities.Add(identity);
}
+
+ return new ClaimsPrincipal(identities);
}
}
```