Context
We need a scope layer for OAuth tokens without duplicating the permission system.
We map scopes to existing RBAC permissions.
A new OAuthScopePermission class checks whether the token's scopes cover the view's required RBAC permission. Unmapped permissions are denied by default
Enforcement rule
Effective permissions is the intersection between the user permissions and scopes granted
What to do
- Define the
SCOPE_PERMISSION_MAP:
SCOPE_PERMISSION_MAP = {
"flag:read": {VIEW_PROJECT, VIEW_FEATURE_STATE, VIEW_ENVIRONMENT},
"flag:write": {CREATE_FEATURE, UPDATE_FEATURE_STATE, DELETE_FEATURE},
# ...
}
- Build
OAuthScopePermission class:
- If request is not an OAuth token → return
True (no-op for session users)
- If the view has no declared RBAC permission → deny (default deny)
- Look up which scopes cover the required RBAC permission → check the token has one
- Add
OAuthScopePermission to the permission classes:
permission_classes = [IsAuthenticated, OAuthScopePermission, ProjectPermission]
- Define scope list in DOT settings (
OAUTH2_PROVIDER["SCOPES"])
Scope-to-permission mapping
| Scope |
RBAC Permissions |
MCP Endpoints |
organisation:read |
(org membership) |
GET /organisations/, /organisations/{id}/groups/, /organisations/{id}/invites/ |
organisation:write |
CREATE_PROJECT, MANAGE_USER_GROUPS |
POST /organisations/{id}/invites/ |
project:read |
VIEW_PROJECT, MANAGE_TAGS |
GET /organisations/{id}/projects/, /projects/{id}/, /projects/{id}/environments/ |
project:write |
CREATE_ENVIRONMENT |
PUT /projects/{id}/ |
environment:read |
VIEW_ENVIRONMENT |
GET /environments/ |
environment:write |
MANAGE_SEGMENT_OVERRIDES |
|
flag:read |
VIEW_PROJECT, VIEW_ENVIRONMENT |
GET /features/, /features/{id}/, /evaluation-data/, /code-references/, /feature-external-resources/, /mv-options/, /versions/, /featurestates/, /change-requests/, /list-change-requests/ |
flag:write |
CREATE_FEATURE, EDIT_FEATURE, DELETE_FEATURE, UPDATE_FEATURE_STATE, CREATE_CHANGE_REQUEST, APPROVE_CHANGE_REQUEST, MANAGE_PROJECT_LEVEL_CHANGE_REQUESTS, APPROVE_PROJECT_LEVEL_CHANGE_REQUESTS, CREATE_PROJECT_LEVEL_CHANGE_REQUESTS |
POST/PUT/DELETE on features, MV options, versions, feature states, change requests |
segment:read |
VIEW_PROJECT |
GET /segments/, /segments/{id}/ |
segment:write |
MANAGE_SEGMENTS |
POST /segments/, PUT /segments/{id}/ |
identity:read |
VIEW_IDENTITIES |
|
identity:write |
MANAGE_IDENTITIES |
|
release_pipeline:read |
VIEW_PROJECT |
GET /release-pipelines/, /release-pipelines/{id}/ |
release_pipeline:write |
(project admin) |
POST /release-pipelines/{id}/add-feature/ |
feature_health:read |
VIEW_PROJECT |
GET /feature-health/events/ |
audit_log:read |
VIEW_AUDIT_LOG |
|
webhook:read |
(env/project membership) |
|
webhook:write |
(env/project admin) |
|
role:read |
(org admin) |
|
role:write |
(org admin) |
|
integration:read |
(project/env membership) |
|
integration:write |
(project/env admin) |
|
SCOPE_PERMISSION_MAP = {
# Organisation
"organisation:read": set(), # org membership check
"organisation:write": {CREATE_PROJECT, MANAGE_USER_GROUPS},
# Project
"project:read": {VIEW_PROJECT, MANAGE_TAGS},
"project:write": {CREATE_ENVIRONMENT},
# Environment
"environment:read": {VIEW_ENVIRONMENT},
"environment:write": {MANAGE_SEGMENT_OVERRIDES},
# Flags (includes change requests)
"flag:read": {VIEW_PROJECT, VIEW_ENVIRONMENT},
"flag:write": {
CREATE_FEATURE, EDIT_FEATURE, DELETE_FEATURE, UPDATE_FEATURE_STATE,
CREATE_CHANGE_REQUEST, APPROVE_CHANGE_REQUEST,
MANAGE_PROJECT_LEVEL_CHANGE_REQUESTS, APPROVE_PROJECT_LEVEL_CHANGE_REQUESTS,
CREATE_PROJECT_LEVEL_CHANGE_REQUESTS,
},
# Segments
"segment:read": {VIEW_PROJECT},
"segment:write": {MANAGE_SEGMENTS},
# Identities
"identity:read": {VIEW_IDENTITIES},
"identity:write": {MANAGE_IDENTITIES},
# Release pipelines
"release_pipeline:read": {VIEW_PROJECT},
"release_pipeline:write": set(), # project admin check
# Feature health
"feature_health:read": {VIEW_PROJECT},
# Audit log
"audit_log:read": {VIEW_AUDIT_LOG},
# Webhooks
"webhook:read": set(), # env/project membership check
"webhook:write": set(), # env/project admin check
# Roles
"role:read": set(), # org admin check
"role:write": set(), # org admin check
# Integrations
"integration:read": set(), # project/env membership check
"integration:write": set(), # project/env admin check
}
Definition of done
Context
We need a scope layer for OAuth tokens without duplicating the permission system.
We map scopes to existing RBAC permissions.
A new
OAuthScopePermissionclass checks whether the token's scopes cover the view's required RBAC permission. Unmapped permissions are denied by defaultEnforcement rule
Effective permissions is the intersection between the user permissions and scopes granted
What to do
SCOPE_PERMISSION_MAP:OAuthScopePermissionclass:True(no-op for session users)OAuthScopePermissionto the permission classes:OAUTH2_PROVIDER["SCOPES"])Scope-to-permission mapping
organisation:readGET /organisations/,/organisations/{id}/groups/,/organisations/{id}/invites/organisation:writeCREATE_PROJECT,MANAGE_USER_GROUPSPOST /organisations/{id}/invites/project:readVIEW_PROJECT,MANAGE_TAGSGET /organisations/{id}/projects/,/projects/{id}/,/projects/{id}/environments/project:writeCREATE_ENVIRONMENTPUT /projects/{id}/environment:readVIEW_ENVIRONMENTGET /environments/environment:writeMANAGE_SEGMENT_OVERRIDESflag:readVIEW_PROJECT,VIEW_ENVIRONMENTGET /features/,/features/{id}/,/evaluation-data/,/code-references/,/feature-external-resources/,/mv-options/,/versions/,/featurestates/,/change-requests/,/list-change-requests/flag:writeCREATE_FEATURE,EDIT_FEATURE,DELETE_FEATURE,UPDATE_FEATURE_STATE,CREATE_CHANGE_REQUEST,APPROVE_CHANGE_REQUEST,MANAGE_PROJECT_LEVEL_CHANGE_REQUESTS,APPROVE_PROJECT_LEVEL_CHANGE_REQUESTS,CREATE_PROJECT_LEVEL_CHANGE_REQUESTSPOST/PUT/DELETEon features, MV options, versions, feature states, change requestssegment:readVIEW_PROJECTGET /segments/,/segments/{id}/segment:writeMANAGE_SEGMENTSPOST /segments/,PUT /segments/{id}/identity:readVIEW_IDENTITIESidentity:writeMANAGE_IDENTITIESrelease_pipeline:readVIEW_PROJECTGET /release-pipelines/,/release-pipelines/{id}/release_pipeline:writePOST /release-pipelines/{id}/add-feature/feature_health:readVIEW_PROJECTGET /feature-health/events/audit_log:readVIEW_AUDIT_LOGwebhook:readwebhook:writerole:readrole:writeintegration:readintegration:writeDefinition of done
flag:readcan read features but not create them)403 insufficient_scope