Scopes & Permissions
This document defines all OAuth scopes and their associated permissions in the CELINE platform.
Understanding the Authorization Model
Authorization in CELINE requires both of the following to pass:
- User must have sufficient group level (for user tokens)
- Client must have the required scope (for all tokens)
┌─────────────────────────────────────────────────────────────┐
│ │
│ User Token Service Token │
│ ══════════ ═════════════ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ User Groups │ │ Scopes │ │
│ │ (roles) │ │ only │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │Client Scopes│ │ │
│ └──────┬──────┘ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Authorization │ │
│ │ (intersection of both) │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
User Groups (Roles)
Users are assigned to groups in Keycloak. Groups form a hierarchy:
| Group | Level | Description |
|---|---|---|
admins |
4 | Full platform access, restricted data |
managers |
3 | Operational access, simulations, reports |
editors |
2 | Write access to internal resources |
viewers |
1 | Read-only access to internal resources |
| (none) | 0 | Anonymous, open resources only |
Inheritance: Higher levels include all permissions of lower levels.
Group Assignment in Keycloak
Groups can be assigned via:
- Keycloak Admin Console → Users → Groups
- realm_access.roles claim in JWT
- groups claim in JWT (with / prefix stripped)
OAuth Scopes
Dataset Scopes
| Scope | Description | Permissions |
|---|---|---|
dataset.query |
Query datasets | Read open and internal datasets |
dataset.admin |
Administer datasets | Read/write all datasets including restricted |
Access Matrix (with scope):
| Access Level | dataset.query |
dataset.admin |
|---|---|---|
open |
read | read/write |
internal |
read | read/write |
restricted |
❌ | read/write |
Digital Twin Scopes
| Scope | Description | Permissions |
|---|---|---|
dt.read |
Read digital twin data | Read twin state, history |
dt.write |
Write digital twin data | Update twin state |
dt.simulate |
Run simulations | Execute simulations on twins |
dt.admin |
Full digital twin access | All DT operations |
Required Group Level:
| Scope | User Group Required |
|---|---|
dt.read |
viewers+ |
dt.write |
editors+ |
dt.simulate |
managers+ |
dt.admin |
admins |
Pipeline Scopes
| Scope | Description | Permissions |
|---|---|---|
pipeline.execute |
Execute pipelines | Trigger state transitions |
pipeline.admin |
Administer pipelines | All pipeline operations |
MQTT Scopes
| Scope | Description | Permissions |
|---|---|---|
mqtt.read |
Subscribe and read | Subscribe to topics, read messages |
mqtt.write |
Publish messages | Publish to allowed topics |
mqtt.admin |
Full MQTT access | All topics, superuser status |
User Data Scopes
| Scope | Description | Permissions |
|---|---|---|
userdata.read |
Read user data | Read user's own data, shared data |
userdata.write |
Write user data | Modify user's own data |
userdata.admin |
Admin user data | Access any user's data |
Service Client Configuration
Each CELINE service should have its own OAuth client with minimal required scopes.
Recommended Client Configurations
digital-twin Service
client_id: svc-digital-twin
client_type: confidential
service_account_enabled: true
scopes:
- dt.read
- dt.write
- dt.simulate
- dataset.query
- mqtt.write
Rationale:
- Needs to read/write twin state (dt.read, dt.write)
- Runs simulations (dt.simulate)
- Queries datasets for twin configuration (dataset.query)
- Publishes twin events to MQTT (mqtt.write)
pipelines Service
client_id: svc-pipelines
client_type: confidential
service_account_enabled: true
scopes:
- pipeline.execute
- dataset.query
- dataset.admin
- mqtt.write
Rationale:
- Manages pipeline state transitions (pipeline.execute)
- Reads input datasets (dataset.query)
- Writes output datasets (dataset.admin)
- Publishes pipeline status events (mqtt.write)
rec-registry Service
client_id: svc-rec-registry
client_type: confidential
service_account_enabled: true
scopes:
- dataset.query
- dataset.admin
Rationale:
- Queries REC certificate data (dataset.query)
- Writes registry updates (dataset.admin)
nudging Service
client_id: svc-nudging
client_type: confidential
service_account_enabled: true
scopes:
- dt.read
- userdata.read
- mqtt.write
Rationale:
- Reads twin state for context (dt.read)
- Reads user preferences (userdata.read)
- Sends nudge notifications via MQTT (mqtt.write)
Frontend Application
client_id: celine-frontend
client_type: public
scopes:
- dt.read
- dataset.query
- userdata.read
- userdata.write
Rationale: - Public client (no secret, PKCE required) - Read-only for twins and datasets - Full access to user's own data
Creating Scopes in Keycloak
1. Create Client Scopes
Navigate to: Keycloak Admin → Client Scopes → Create
{
"name": "dataset.query",
"description": "Query datasets (read internal)",
"protocol": "openid-connect",
"attributes": {
"include.in.token.scope": "true",
"display.on.consent.screen": "true"
}
}
Repeat for each scope.
2. Create Service Clients
Navigate to: Keycloak Admin → Clients → Create
{
"clientId": "svc-digital-twin",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"serviceAccountsEnabled": true,
"standardFlowEnabled": false,
"directAccessGrantsEnabled": false,
"publicClient": false
}
3. Assign Scopes to Clients
Navigate to: Client → Client Scopes → Add client scope
Select the scopes required for this client (default or optional).
Scope Validation Examples
Example 1: User Reading Internal Dataset
JWT Claims:
{
"sub": "user-123",
"realm_access": {"roles": ["viewers"]},
"scope": "openid dataset.query"
}
Policy Check:
1. User is in viewers group (level 1) ✅
2. Client has dataset.query scope ✅
3. Dataset is internal, action is read ✅
Result: ALLOWED
Example 2: User Writing to Internal Dataset
JWT Claims:
{
"sub": "user-456",
"realm_access": {"roles": ["editors"]},
"scope": "openid dataset.query"
}
Policy Check:
1. User is in editors group (level 2) ✅
2. Client has dataset.query scope (not dataset.admin) ❌
Result: DENIED — "missing dataset.admin scope for write"
Example 3: Service Accessing Dataset
JWT Claims (client credentials):
{
"client_id": "svc-pipelines",
"scope": "dataset.query dataset.admin"
}
Policy Check:
1. Subject type is service
2. Service has dataset.admin scope ✅
Result: ALLOWED
Example 4: Low-Privilege Client with Admin User
JWT Claims:
{
"sub": "admin-user",
"realm_access": {"roles": ["admins"]},
"scope": "openid dataset.query"
}
Request: Write to restricted dataset
Policy Check:
1. User is in admins group (level 4) ✅
2. Client has dataset.query scope (not dataset.admin) ❌
Result: DENIED — Client scope limits what the user can do through this application.
Best Practices
For Service Developers
- Request minimal scopes — Only request what your service actually needs
- Use service accounts — For backend services, use client credentials flow
- Don't share clients — Each service should have its own OAuth client
- Rotate secrets — Regularly rotate client secrets
For Platform Administrators
- Audit scope usage — Review which clients use which scopes
- Principle of least privilege — Don't grant unnecessary scopes
- Separate environments — Different clients for dev/staging/prod
- Monitor denials — Track authorization failures for anomalies
Scope Naming Convention
{resource}.{action}
Examples:
- dataset.query — Query (read) datasets
- dataset.admin — Administer (full control) datasets
- dt.read — Read digital twins
- mqtt.write — Write (publish) to MQTT
Troubleshooting
"missing scope" Error
The calling client doesn't have the required scope.
Solution: Add the scope to the client in Keycloak.
"insufficient group privileges" Error
The user isn't in a high enough group.
Solution: Add the user to the appropriate group in Keycloak.
Service Account Can't Authenticate
The client doesn't have service accounts enabled.
Solution: Enable "Service Accounts Enabled" in Keycloak client settings.
Scope Not in Token
The scope was added to the client but isn't appearing in tokens.
Checklist: 1. Is the scope in "Default Client Scopes" or "Optional Client Scopes"? 2. Is "Include in Token Scope" enabled on the client scope? 3. Is the scope being requested in the token request?