Scopes in OAuth2/OIDC
OAuth2 and OpenID Connect scopes provide a standardized mechanism to define granular permission boundaries for resource access. Scopes act as declarative permissions that clients request and servers validate, enabling fine-grained control over what actions a client can perform on behalf of a user or service. In OpenID Connect, scopes also appear in ID tokens and access tokens, often carrying associated claims or metadata.
Scope Hierarchy and Granularity¶
Scopes can be organized hierarchically to represent logical relationships between permissions. For example:
- user.read (base scope)
- user.read.email (child scope of user.read)
- user.read.profile (another child of user.read)
This hierarchy allows for scoped inheritance, where a parent scope may implicitly grant access to its child scopes (depending on implementation). However, OAuth2 does not enforce strict hierarchy enforcement by default—scopes are typically treated as independent unless explicitly designed to have dependencies.
Example Use Cases¶
- A
document.readscope might include sub-scopes likedocument.read.commentsanddocument.read.history. - A
payment.readscope could be paired withpayment.read.transactionsfor layered access control.
Scope Assignment in Keycloak¶
Keycloak (an OpenID Connect provider) allows scopes to be defined at the client level, enabling dynamic permission management. Scopes are tied to clients and can be used to control access to protected resources.
1. Creating a Scope in Keycloak¶
Use the Keycloak Admin REST API to define a custom scope:
curl -X POST \
http://KEYCLOAK_HOST/auth/admin/realms/REALM_NAME/clients/{CLIENT_ID}/scope-templates \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "user.read",
"description": "Read user profile information"
}'
user.read with a description. Scopes can also be defined via the Keycloak UI under Clients > [Client] > Scopes.
2. Assigning Scopes to Clients¶
When a client requests a scope, it must be explicitly enabled:
curl -X POST \
http://KEYCLOAK_HOST/auth/admin/realms/REALM_NAME/clients/{CLIENT_ID}/scope-mappings \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scopeMappings": {
"clientScopes": ["user.read"]
}
}'
user.read scope with the client, allowing it to request and use the scope in access tokens.
3. Using Scopes in Resource Servers¶
A resource server (e.g., a Spring Boot app using Keycloak) validates scopes in access tokens:
@RestController
public class ResourceController {
@GetMapping("/user/profile")
public ResponseEntity<String> getUserProfile(@AuthenticationPrincipal OAuth2Authentication authentication) {
if (authentication.getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("user.read"))) {
return ResponseEntity.ok("User profile data");
}
return ResponseEntity.status(403).body("Forbidden");
}
}
user.read scope is present in the token before granting access.
Diagram: Scope Hierarchy in Keycloak¶
This structure allows for modular scope management, where child scopes inherit context from their parent.Key takeaways¶
- Scopes define permission boundaries in OAuth2 and OpenID Connect, enabling granular access control.
- Scope hierarchies allow logical grouping of related permissions but require explicit design for inheritance.
- Keycloak scopes are client-centric, enabling dynamic assignment and validation in resource servers.