IdentityServer4 — Part 5 — Scopes and Resources — Digitteck
IdentityServer4 — Part 5 — Scopes and Resources
dotnet·2 November 2019·5 min read

IdentityServer4 — Part 5 — Scopes and Resources

A scope defines access to a specific set of information or resources. In IdentityServer4, scopes are divided into two categories:

  • Identity Scopes — request identity information about the user (claims in the id_token)
  • API Resource Scopes — request access to a protected API (represented as the aud claim in the access_token)

1. Custom Identity Resource

An identity resource must have at least one claim. Here we define a custom scope calledcustomIdentityScope that carries a claim named my_pet_name:

csharp
// Define a custom identity resource with a custom claim
public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),

        // Custom identity resource
        new IdentityResource
        {
            Name        = "customIdentityScope",
            DisplayName = "Custom Identity Scope",
            UserClaims  = { "my_pet_name" },  // must have at least one claim
        },
    };
}

The scope must also be listed in the client's AllowedScopes:

csharp
// Client must include the custom scope in AllowedScopes
new Client
{
    ClientId          = "my_client",
    AllowedGrantTypes = GrantTypes.Code,
    ClientSecrets     = { new Secret("my_secret".Sha256()) },
    RedirectUris      = { "https://myapp.com/callback" },
    AllowedScopes     =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "customIdentityScope",  // allow the client to request this scope
    },
};

Populate the custom claim in a custom IProfileService:

csharp
// Populate the custom claim in the user's profile service
public class CustomProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = context.Subject;

        // Add the custom claim when the customIdentityScope was requested
        if (context.RequestedClaimTypes.Contains("my_pet_name"))
        {
            context.IssuedClaims.Add(new Claim("my_pet_name", "Buddy"));
        }

        return Task.CompletedTask;
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        context.IsActive = true;
        return Task.CompletedTask;
    }
}

Using the Scope in the Authorization Flow

Include the custom scope in the authorization request and then call the userinfo endpoint to retrieve the claim from the access token:

csharp
// Client — request the custom scope in the authorization redirect
// Step 1: redirect to authorization endpoint with the custom scope
var authorizeUrl = 
quot;https://is4server.com/connect/authorize"
+
quot;?response_type=code"
+
quot;&client_id=my_client"
+
quot;&redirect_uri=https://myapp.com/callback"
+
quot;&scope=openid%20profile%20customIdentityScope"
+
quot;&state=xyz"
; // Step 2: exchange the code for tokens (back channel) var tokenResponse = await httpClient.RequestAuthorizationCodeTokenAsync( new AuthorizationCodeTokenRequest { Address = disco.TokenEndpoint, ClientId = "my_client", ClientSecret = "my_secret", Code = authorizationCode, RedirectUri = "https://myapp.com/callback", }); // Step 3: call the userinfo endpoint to get the custom claim var userInfo = await httpClient.GetUserInfoAsync( new UserInfoRequest { Address = disco.UserInfoEndpoint, Token = tokenResponse.AccessToken, }); var petName = userInfo.Claims.FirstOrDefault(c => c.Type == "my_pet_name")?.Value;

2. Built-In Identity Resources

IdentityServer4 ships with built-in identity resources. The scope names and claim mappings are defined in IdentityServerConstants.StandardScopes and JwtClaimTypes:

csharp
// IdentityServerConstants.StandardScopes (built-in scope names)
public static class StandardScopes
{
    public const string OpenId        = "openid";
    public const string Profile       = "profile";
    public const string Email         = "email";
    public const string Address       = "address";
    public const string Phone         = "phone";
    public const string OfflineAccess = "offline_access";
}

The built-in IdentityResources.OpenId() and IdentityResources.Profile() implementations show exactly which claims each scope includes:

csharp
// IdentityResources.OpenId() — maps openid scope to the sub claim
new IdentityResource
{
    Name        = "openid",
    DisplayName = "Your user identifier",
    Required    = true,
    UserClaims  = { JwtClaimTypes.Subject }  // "sub"
}

// IdentityResources.Profile() — maps profile scope to standard identity claims
new IdentityResource
{
    Name        = "profile",
    DisplayName = "User profile",
    UserClaims  =
    {
        JwtClaimTypes.Name,
        JwtClaimTypes.FamilyName,
        JwtClaimTypes.GivenName,
        JwtClaimTypes.MiddleName,
        JwtClaimTypes.NickName,
        JwtClaimTypes.PreferredUserName,
        JwtClaimTypes.Profile,
        JwtClaimTypes.Picture,
        JwtClaimTypes.WebSite,
        JwtClaimTypes.Gender,
        JwtClaimTypes.BirthDate,
        JwtClaimTypes.ZoneInfo,
        JwtClaimTypes.Locale,
        JwtClaimTypes.UpdatedAt,
    }
}

3. Custom API Resource

An API resource defines the audience and the granular scopes that control access to it. The API resource name becomes the aud claim in the access token, and the API validates that claim to confirm the token was issued for it:

csharp
// Custom API resource — defines an audience and the scopes that grant access
new ApiResource
{
    Name        = "mdsapi",  // audience ("aud" claim in the access token)
    DisplayName = "My Data Service API",
    Scopes      =
    {
        new Scope
        {
            Name         = "mdsapi.read",
            DisplayName  = "Read access to MDS API",
        },
        new Scope
        {
            Name         = "mdsapi.write",
            DisplayName  = "Write access to MDS API",
        },
    }
}

// Client allowed scopes reference the API scopes by name
new Client
{
    ClientId          = "console_client",
    AllowedGrantTypes = GrantTypes.ClientCredentials,
    ClientSecrets     = { new Secret("secret".Sha256()) },
    AllowedScopes     = { "mdsapi.read" },
}

Tags

.NETIdentityServer4OAuth2OpenID Connect
digitteck

© 2026 Digitteck