You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(requirements): update roles-and-acl with clarified decisions
- UOA system roles simplified to owner + admin only
- No cap on custom roles per team/org
- Custom roles are labels only; consuming app owns all gating
- Custom roles are orthogonal to UOA system roles
- Owner non-removable, transferable; admin has full power
- Reduced outstanding decisions to 3 open items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: Docs/Requirements/roles-and-acl.md
+67-70Lines changed: 67 additions & 70 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,69 +6,70 @@ There are two completely separate concepts that must not be conflated.
6
6
7
7
### 1. UOA system roles (internal)
8
8
9
-
These control who can administer the UOA backend itself. They are fixed and managed only by system admins via the admin panel. End users and developers never see or configure these.
9
+
These control who can administer the UOA backend itself — the org and team structure, billing, membership. There are exactly two UOA system roles:
10
10
11
-
| Role | Scope |Can do|
11
+
| Role | Scope |Rules|
12
12
|---|---|---|
13
-
|`system_admin`| Global | Full admin panel access, all orgs/teams/domains |
14
-
|`org_owner`| Organisation | Manage their org, teams, members, domains |
15
-
|`org_admin`| Organisation | Manage teams and members, not delete org |
16
-
|`org_member`| Organisation | Read-only on org structure |
17
-
|`team_owner`| Team | Manage team members and domain rules for that team |
18
-
|`team_admin`| Team | Manage team members |
19
-
|`team_member`| Team | Basic membership |
13
+
|`owner`| Org or Team | Created with the org/team. Cannot be removed. Can transfer ownership to any other user. Implicitly has all admin capabilities. |
14
+
|`admin`| Org or Team | Full power: delete teams, manage billing, manage members, manage domains. Multiple admins allowed. Any user can be granted or revoked admin. |
20
15
21
-
These roles are stored and managed by UOA. They drive access to the UOA admin panel and to management API endpoints (`/internal/...`).
16
+
Users who are neither `owner` nor `admin` have no named UOA system role — they are plain members whose significance is defined entirely by their custom role.
17
+
18
+
`system_admin` is a separate global role for UOA's own admin panel operators and is not visible to org/team users.
22
19
23
20
### 2. Consumer-defined roles (external, custom)
24
21
25
-
These are the roles that a developer registering a domain/team defines for their own product. UOA does not know or care what they mean — it stores and returns them. The developer decides what `editor`, `viewer`, `staff`, `bartender`, or anything else means inside their own application.
22
+
These are roles that a developer or org defines for their own product. UOA stores only the **label** — a string reference. UOA has no opinion on what the role permits. The consuming application owns all gating logic.
26
23
27
-
UOA's responsibility:
28
-
- Store the custom role definitions per team
29
-
- Assign custom roles to users per team
30
-
- Return the user's custom roles in the access token and via API
31
-
- Enforce nothing — the consuming app enforces meaning
24
+
- Any number of custom roles per team or organisation — no cap, no gating
25
+
- Custom roles can share names with UOA system roles (different namespaces, no conflict)
26
+
- A user's UOA role and their custom role are completely orthogonal: an `owner` in UOA can be a `viewer` in the app; a plain user in UOA can be a `superadmin` in the app
32
27
33
-
These are entirely separate from UOA system roles.
28
+
UOA's only responsibilities for custom roles:
29
+
- Store the role definitions per team/org
30
+
- Validate that a role assigned to a user exists in the team/org's defined list
31
+
- Return the role label in the access token and via API
32
+
- Enforce nothing beyond that
34
33
35
34
---
36
35
37
36
## Hierarchy model
38
37
39
38
```
40
39
Organisation
41
-
├── has many Domains (multiple — one org may run hundreds of services)
42
-
├── has many Teams
43
-
│ ├── Team has one or more Domains
44
-
│ ├── Team has custom Role definitions
45
-
│ └── Team has Members (with custom roles assigned)
46
-
└── has system-level role assignments per member
40
+
├── has many Domains (multiple — one org can serve hundreds of services)
41
+
├── has UOA role assignments (owner, admin) per member
42
+
├── has custom role definitions
43
+
└── has many Teams
44
+
├── references one or more Domains from the org's domain pool
45
+
├── has UOA role assignments (owner, admin) per member
46
+
├── has custom role definitions (can differ from org-level)
47
+
└── has Members with assigned custom roles
47
48
```
48
49
49
50
### Auto-organisation rule
50
51
51
-
Not every user of UOA needs an enterprise org setup. Teams are the primary registration unit for simpler setups.
52
+
Teams are the primary registration unit. Not every setup needs a full enterprise org structure.
52
53
53
-
**Rule:** When a team is created and no organisation is specified, an organisation is automatically created with the same name/slug and the team placed under it. That org is allowed to contain only one team (the simple/non-enterprise case). If the org later needs to expand to multiple teams, it is promoted to a full enterprise org by adding a second team.
54
+
**Rule:** When a team is created without specifying an organisation, UOA automatically creates an organisation with the same name and slug and places the team under it. That org starts in single-team mode.
54
55
55
-
This means:
56
-
- Teams are always under an org, but the org can be implicit/auto-created
57
-
- Single-team orgs are the default, lightweight path
58
-
- Multi-team orgs are the enterprise path
56
+
- Single-team org = the default, lightweight path (most small integrations)
57
+
- Multi-team org = enterprise path, unlocked simply by adding a second team
58
+
- Teams are always under an org — the org may just be implicit and auto-created
59
59
60
60
### Domain assignment
61
61
62
-
- Domains live at **organisation level** — an org can have multiple domains (e.g. `api.acme.com`, `app.acme.com`, `admin.acme.com`, `mobile.acme.com`, ...)
63
-
- A **team** references one or more domains from its org's domain pool — this is what gets registered; the team is the entity that "uses" a domain
64
-
- A domain can only belong to one organisation
65
-
- Authentication requests come in on a domain → resolve to the org → resolve to the team registered for that domain → determine user's membership and roles for that team
62
+
- Domains are registered at **organisation level**
63
+
- An org can have any number of domains (`api.acme.com`, `app.acme.com`, `admin.acme.com`, etc.)
64
+
- A domain belongs to exactly one organisation
65
+
- Teams reference domains from their org's pool — the team is what gets registered against a domain
66
+
- Inbound auth request flow: domain → org → team registered for that domain → user's membership and roles for that team
66
67
67
68
---
68
69
69
70
## Custom role definitions
70
71
71
-
Each team defines its own set of role names. These are free-form strings stored per team.
72
+
Each team (and optionally org) defines its own role names as a simple list of strings:
72
73
73
74
```json
74
75
{
@@ -77,17 +78,13 @@ Each team defines its own set of role names. These are free-form strings stored
77
78
}
78
79
```
79
80
80
-
When a user is added to a team, they are assigned one of those custom roles.
81
-
82
-
UOA validates only:
83
-
- The role name assigned to a user must exist in the team's `customRoles` list
84
-
- Role names must be non-empty strings, no spaces, reasonable length
81
+
No limit on the number of roles. Role names are validated only for being non-empty strings. Format is up to the defining org — UOA imposes no casing or character rules beyond that.
85
82
86
-
UOA does **not** validate meaning or enforce permissions based on custom roles — that is the consuming app's responsibility.
83
+
What a role *permits* inside the consuming application is entirely that application's concern. UOA returns only the label.
87
84
88
-
### Token output
85
+
---
89
86
90
-
The access token issued by UOA includes:
87
+
## Token output
91
88
92
89
```json
93
90
{
@@ -97,60 +94,60 @@ The access token issued by UOA includes:
97
94
{
98
95
"id": "org_abc",
99
96
"slug": "acme",
100
-
"uoaRole": "org_member",
97
+
"uoaRole": "admin",
98
+
"customRoles": ["manager"],
101
99
"teams": [
102
100
{
103
101
"id": "team_xyz",
104
102
"name": "Backend",
105
103
"domain": "api.acme.com",
106
-
"customRole": "editor",
107
-
"uoaRole": "team_member",
108
-
"uoaRoleInherited": true
104
+
"uoaRole": "admin",
105
+
"uoaRoleInherited": true,
106
+
"customRoles": ["editor"]
109
107
}
110
108
]
111
109
}
112
110
]
113
111
}
114
112
```
115
113
116
-
`uoaRole`= UOA system role (internal, for UOA management use)
117
-
`customRole` = developer-defined role (for the consuming app to use)
118
-
`uoaRoleInherited` = true if the team-level UOA role was not set explicitly but inherited from the org
114
+
-`uoaRole`— `owner`, `admin`, or omitted if neither. For UOA management use only.
115
+
-`uoaRoleInherited` — true if the team-level UOA role was derived from the org-level role, not set explicitly
116
+
-`customRoles` — array of the consuming app's role labels for that scope. UOA stores and returns them; the app interprets them.
119
117
120
-
---
118
+
Note: `customRoles` is an array because a user may hold multiple custom roles at the same scope (e.g. `["editor", "billing"]`).
121
119
122
-
## Inheritance rules (UOA system roles only)
120
+
---
123
121
124
-
Custom roles do not inherit — the consuming app defines its own hierarchy if it wants one.
122
+
## UOA system role inheritance
125
123
126
-
UOA system role inheritance:
124
+
Inheritance applies only to UOA system roles, not to custom roles.
127
125
128
-
1.`org_owner` → effective `team_owner` on every team in the org
129
-
2.`org_admin` → effective `team_admin` on every team in the org
130
-
3.`org_member` → effective `team_member` on every team (read access only)
131
-
4.A user with an explicit team role at a higher level than their org role keeps the higher team role
126
+
1.Org `owner` → effective `owner` on every team in the org
127
+
2.Org `admin` → effective `admin` on every team in the org
128
+
3.A user with an explicitly higher team role than their org role keeps the higher team role
129
+
4.Inheritance is computed at request time — not stored as duplicate records
132
130
133
-
Inheritance is computed at access time, not stored as duplicate records.
131
+
Custom roles do not inherit. If a consuming app wants role inheritance, it implements that in its own gating layer.
134
132
135
133
---
136
134
137
135
## Email domain auto-enrolment
138
136
139
-
Orgs (and by extension teams) can define rules: any user who authenticates with a verified email from `@acme.com`is automatically added to the org as `org_member` and to the default team with a specified custom role.
137
+
Orgs can define rules so that any user who authenticates with a verified email from a given domain is automatically granted membership on first login.
140
138
141
-
Rules are per-org and specify:
139
+
Each rule specifies:
142
140
- Email domain (e.g. `acme.com`)
143
-
- UOA role granted (`org_member` or `org_admin` — never `org_owner`)
144
-
- Custom role granted on the default team (must exist in that team's `customRoles`)
`owner` can never be granted via auto-enrolment — ownership is always explicit.
146
146
147
147
---
148
148
149
-
## Outstanding decisions needed
149
+
## Outstanding decisions
150
150
151
-
1.**Can a custom role be the same string as a UOA system role?** (e.g. can a developer name a role `admin`?) — recommend: yes, no conflict since they live in different namespaces
152
-
2.**Role name format** — allow any string, or enforce lowercase/alphanumeric/hyphen?
153
-
3.**Max custom roles per team** — suggest: 20
154
-
4.**Who can manage custom role definitions?** — suggest: `team_owner` and `org_owner` only
155
-
5.**Default custom role on auto-enrolment** — must be pre-configured per rule, or fallback to first role in the list?
156
-
6.**SCIM provisioning** — out of scope for now but the custom role model needs to be SCIM-group-compatible for future enterprise IdP sync
151
+
1.**Default custom role on auto-enrolment** — must the rule explicitly name a custom role, or fall back to the first role in the team's list if none specified?
152
+
2.**Multiple custom roles per user per team** — the token uses an array; confirm this is intentional (a user can hold `editor` and `billing` simultaneously at the same team scope)
0 commit comments