A cloud-based identity and access management service for securing user authentication and resource access
Security groups and a custom authorization policy handler are valid and supported, but relying only on groups/app roles in the token and skipping scopes/app roles at the API surface loses important separation of concerns and flexibility.
Key points from the Microsoft identity platform guidance:
- Web APIs are expected to expose scopes and app roles
- Web APIs should publish at least one scope (delegated permission) and at least one app role (application permission) so client apps can obtain access tokens with appropriate permissions.
- Example from the tutorial: delegated permissions like
ToDoList.Read/ToDoList.ReadWriteand application permissions likeToDoList.Read.All/ToDoList.ReadWrite.Allare defined and then enforced by the API. - When a client calls the API, only tokens with those scopes or app roles are authorized to access protected endpoints.
- Scopes vs app permissions map directly to your two client types
- Scopes (delegated permissions) are for interactive/public clients acting on behalf of a user. They appear in
scp/scopeclaims and are validated by the API. - App permissions (application permissions) are for daemon/service apps using client credentials. They appear as
rolesclaims. - Microsoft.Identity.Web explicitly validates scopes and app permissions as part of authorization, in addition to signature, audience, and expiration.
- Scopes (delegated permissions) are for interactive/public clients acting on behalf of a user. They appear in
- Groups, admin roles, and app roles are first-class for policy-based authorization
- ASP.NET Core authorization policies can use
groups,wids(administrator roles), androleclaims to authorize access to server APIs. - Example: a policy that requires a specific admin role via
wids, then applied with[Authorize(Policy = "BillingAdministrator")]on a controller. - This is exactly the pattern used for group/role-based fine-grained authorization.
- ASP.NET Core authorization policies can use
- Best practice is to combine
[Authorize]with scope/role validation and policies- Guidance recommends always pairing
[Authorize]with explicit scope validation (for delegated access) and using policies for more complex rules. - Policies can combine scopes, roles, and arbitrary claims:
- Example: a policy that requires a scope and a claim like
department = IT.
- Example: a policy that requires a scope and a claim like
- Default policies can be set so that every
[Authorize]implicitly requires a given scope.
- First enforce that the client has the right API-level permission (scope/app role).
- Then apply fine-grained rules via groups/claims in policies.
- Guidance recommends always pairing
- Least-privilege design is easier with scopes/app roles
- Zero Trust guidance recommends defining multiple least-privilege delegated permissions instead of a single broad “access as user” permission.
- Separate read vs write, standard vs full access, and create specific permissions per core scenario.
- This helps prevent over-privileged clients and makes it easier to reason about what each client can do.
- What is lost by avoiding scopes/app roles entirely If only security groups and a custom policy handler are used:
- There is no explicit API permission surface exposed in Entra ID for client app registrations. Client owners cannot easily see or request specific API permissions.
- Delegated vs application access is not clearly modeled at the token level (beyond generic group membership), which can complicate future tooling and consent flows.
- Standard patterns and tooling that expect scopes/app roles (e.g., consent UX, admin consent, least-privilege reviews) are not leveraged.
- For public clients, the delegated permission story (scopes in
scp) becomes non-standard, which can make future integration and governance harder.
- Recommended model for the described scenario For the mix of public clients and daemon apps, and the desire for fine-grained group-based authorization:
- Define a small set of scopes for user-delegated access (e.g.,
Api.Read,Api.Write,Api.Admin) and app roles for daemon access (e.g.,Api.Read.All,Api.Write.All). - Use Microsoft.Identity.Web to validate scopes/app roles on incoming tokens.
- Implement authorization policies that:
- Require appropriate scopes/app roles, and
- Check security group membership or other claims for fine-grained access.
- For changing, fine-grained permissions, continue to use security groups and policies; for stable, coarse-grained capabilities, use scopes/app roles.
- Define a small set of scopes for user-delegated access (e.g.,
References:
- Implement authorization in web APIs with Microsoft.Identity.Web
- Tutorial: Build and secure an ASP.NET Core web API with the Microsoft identity platform
- Microsoft Entra (ME-ID) groups, Administrator Roles, and App Roles (.NET 5 to .NET 7) (graph-sdk-5)
- Microsoft Entra (ME-ID) groups, Administrator Roles, and App Roles (.NET 5 to .NET 7) (graph-sdk-4)
- Expose scopes in a protected web API
- API Protection