Scopes provide a mechanism to additionally constrain access decisions without modifying identity or resource policies. They achieve this by applying additional policy, typically based on the means of access such as a 'read-only' scoped OAuth token. Scopes are evaluated during Phase 4 (Scope Phase) of Policy Conjunction.
While identity policies determine what a principal can do based on their roles, scopes allow you to add an additional layer of constraint based on how the principal is accessing the system. This separation enables powerful access patterns where the same identity can have different effective permissions depending on the access context.
Like other entities in the PolicyEngine, scopes serve a dual purpose: they link to a policy for access evaluation, and they can carry annotations that parameterize policy decisions. Scope annotations have the highest precedence in the identity annotation hierarchy (after principal claims), allowing them to override annotations from roles and groups (see Parameterizing Policies).
Consider a user with roles A, B, and C that grant full read/write access. When that user creates a Personal Access Token for use in a CI/CD pipeline, they may want to limit what that token can do:
User Identity: Roles A, B, C → Full read/write access
PAT Scope: "read-only" → Constrains to read operations only
Effective Access: Read-only (intersection of identity and scope)
The PAT grants the caller the entitlements of Roles A, B, or C, but constrained by the additional "read-only" policy. This allows users to create limited-privilege tokens without modifying their underlying roles.
When integrating with external identity providers or federation systems, scopes can constrain what federated sessions can do:
Federated User: Full internal permissions
Federation Scope: "external-api-only" → Limits to specific API subset
Effective Access: Only external API operations
Internal services may need different access levels depending on the calling context:
Service Identity: Full service permissions
Request Scope: "batch-processing" → Limits to batch operations only
Effective Access: Batch operations only
While scopes are not tied to OAuth, they can be populated from OAuth token claims when using OAuth-based authentication:
OAuth Token Scopes: ["api:read", "api:write"]
MPE Scope Policies: Applied based on token claims
Effective Access: Constrained by OAuth scope policies
Scopes are evaluated in Phase 4 of Policy Conjunction. The scope phase runs in parallel with other phases but has unique behavior:
| Condition | Scope Phase Result |
|---|---|
| No scopes in PORC | (phase is skipped) |
| Scopes present, at least one GRANT | |
| Scopes present, all DENY |
When a request includes multiple scopes, each scope's policy is evaluated independently:
Request Scopes: ["read-only", "internal-api"]
Scope Evaluations:
├── read-only policy → GRANT (operation is a read)
├── internal-api policy → GRANT (target is internal)
Scope Phase Result: GRANT (at least one GRANT)
This OR relationship within the scope phase means that if any applicable scope policy grants access, the phase votes GRANT.
Understanding the difference between scopes and roles is crucial:
| Aspect | Roles (Identity Phase) | Scopes (Scope Phase) |
|---|---|---|
| Purpose | Define what a principal can do | Constrain how access is exercised |
| Assignment | Assigned to principals | Associated with access method |
| Effect | Grant permissions | Limit permissions |
| Mandatory | Yes (Phase 2) | No (Phase 4) |
| Default | DENY if missing | GRANT if missing |
Principal: admin role (can read, write, delete)
Access Token Scope: read-only
Identity Phase: GRANT (admin can delete)
Scope Phase: DENY (read-only forbids delete)
Final Decision: DENY (scope constrains identity)
Scopes are defined in the PolicyDomain under spec.scopes:
spec:
scopes:
- mrn: "mrn:iam:scope:read-only"
name: read-only
description: "Restricts to read operations only"
policy: "mrn:iam:policy:read-only-check"
- mrn: "mrn:iam:scope:internal"
name: internal
description: "Internal system access"
policy: "mrn:iam:policy:internal-only"
Each scope references a policy that determines whether the scope allows or denies the operation.
Scopes appear in the PORC expression based on how the request was authenticated:
principal:
sub: "user123"
mroles:
- "mrn:iam:role:editor"
scopes:
- "mrn:iam:scope:read-only"
operation: "api:documents:update"
resource:
id: "mrn:data:document:doc456"
The integration layer (PEP) is responsible for extracting scope information from the access token or authentication context and including it in the PORC.
Scope policies should focus on what to restrict, not what to allow:
# Good: Scope constrains to read operations
package authz
default allow = false
allow {
input.operation.method == "read"
}
# Avoid: Scope tries to grant broad access
# (This defeats the purpose of scopes as constraints)
Name scopes based on their constraint, not their source:
# Good: Describes the constraint
- name: read-only
- name: batch-processing
- name: external-api
# Avoid: Describes the source
- name: pat-scope
- name: oauth-scope
Clearly document which scopes are expected for different access methods:
| Access Method | Required Scopes | Notes |
|---|---|---|
| PAT | At least one scope | User selects at creation |
| OAuth | From token claims | Mapped from OAuth scopes |
| Internal API | None | Full access |
Track when scope policies deny access—this can indicate:
Scopes can carry annotations that flow to principals during evaluation. Scope annotations have higher precedence than both role and group annotations, making them useful for access-method-specific metadata:
scopes:
- mrn: "mrn:iam:scope:elevated-access"
name: elevated-access
description: "Elevated access for sensitive operations"
policy: "mrn:iam:policy:elevated-check"
annotations:
- name: "access_level"
value: "\"elevated\""
- name: "audit_required"
value: "true"
- name: "session_type"
value: "\"privileged\""
In the inheritance hierarchy (Roles → Groups → Scopes → Principal), scope annotations override any conflicting values from roles or groups. This allows you to apply context-specific metadata based on how the principal is accessing the system.
For schema details, see the Scopes Schema Reference.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |