Replies: 2 comments 1 reply
-
Suggested approach for #1616 - refinedAfter working through this with the entity model assumptions from #948 (final comment by @thiva-k, Model 1 or 2, no Model 2.1) in mind, I'd like to propose two options and recommend one. Option 1 - Subscription at the RS level + RBACWhere the relationship lives. The (App ↔ RS) relationship is stored at the Resource Server level, not on the Application. The RS owner is the authority over who may access the RS - querying "who can access RS001" should be a lookup against the RS, not a join across every Application's permission list. This also aligns with how RFC 8707 and RFC 9728 frame the RS as the access target, and handles dynamically-registered clients (MCP, etc.) cleanly. Scope ownership stays with the RS. Each RS defines its own scope namespace. Roles are composed of scopes from one specific RS. When a token is requested, the AS:
This plugs the #2198 M2M-accepts-anything gap directly: M2M tokens cap at the App's assigned roles for that RS, which start empty. Option 2 - Permission allowlist on the ApplicationThe list of permissions an App can access is stored on the Application side. Each App declares which RSs/scopes it is authorized to request, and the AS validates against this list. This is the model used by Microsoft Entra (API permissions on the client app registration) and AWS Cognito (allowed scopes per app client). The accessing App is the entity that carries the access list. RecommendationOption 1 is the recommended approach. Storing the relationship at the RS level keeps the resource owner as the authority, matches how RFC 8707/9728 frame the RS as the access target, and makes "who can access RS001" a single-RS lookup rather than a global scan across Applications. Two approaches under Option 1Within Option 1, there are two ways to express the subscription: 1a - RS-level subscription (binary gate). A subscription authorizes an App for the RS as a whole. If subscribed, the App may request any scope the RBAC layer permits. Token scopes = requested ∩ RBAC. 1b - Scope-level subscription (allowlist). A subscription authorizes an App for a specific set of scopes at the RS. Token scopes = requested ∩ RBAC ∩ subscription allowlist. The allowlist represents what the RS owner has authorized this App to channel, regardless of who the Subject is. Issues to consider with scope-level subscription (1b)
Open questions
|
Beta Was this translation helpful? Give feedback.
-
|
+1 for Option 1, since it aligns cleanly with our subject-actor-resource story. Regarding 1a and 1b, I think we can have both representations right? The resource server can dictate whether:
These can be considered as policies of the resource-server too |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
1. Current state
Thunder currently implements RBAC for users and groups. A role is a collection of permissions drawn from one or more registered resource servers. Roles are assigned to users or groups, and when a user authenticates, their roles are resolved, the associated permissions are collected, and those permissions are included in the issued token as scopes. (scopes == permissions)
This model works correctly for human principals but does not extend to non-human entities. As Thunder evolves toward a unified entity model, Applications and Agents will also share the same RBAC foundation.
2. Current limitations
audclaim and has no effect on what the app is permitted to request.3. Entity unification
The long-term direction for Thunder is a unified entity model where Users, Applications, and Agents share a common base with credentials, lifecycle state, and RBAC support. The RBAC system must accept any entity type as a principal — not just users and groups. (#948)
Agent authorization requirements share similar requirements to application authorization — both can use client credentials for autonomous M2M access and authorization code for delegated access on behalf of users. App RBAC is a prerequisite of entity unification; agent RBAC could follow as a direct extension of the same model without additional architectural changes.
4. How other vendors handle this
Auth0
Applications are granted an explicit scope list per API through a "Client Grant" object. User RBAC and application authorization are entirely separate systems using the same scope strings in different contexts. For M2M flows, every application must explicitly authorize each API — no Client Grant means no access. Auth code flows are open by default; the same grant mechanism can optionally enforce a per-application scope ceiling on delegated flows when required.
Keycloak
Every OAuth client can enable a service account — a synthetic user entity assigned roles exactly like a human user. Client credentials tokens are issued by resolving this service account's roles. For auth code flows, the token reflects the authenticating user's own roles — the service account's assignments have no effect on delegated tokens.
IS / Asgardeo
Applications authorize API resources via an "Authorized APIs" configuration, selecting a scope allowlist per (app, API) pair. Auth code tokens are the intersection of the user's RBAC permissions and the app's authorized scopes. M2M tokens use the same authorized scope list as the direct grant — no separate RBAC mechanism. Because the same scope list serves both flows, configuring auth code delegation automatically makes those scopes available for autonomous M2M access, requiring to explicitly restrict which grant types are permitted to prevent unintended access.
5. Proposed models
Model 1 — Unified RBAC
Applications are assigned roles, similar to users. Auth code tokens are the intersection of user RBAC and app RBAC permissions. M2M tokens use app RBAC directly.
Pros: Single RBAC system, consistent mental model for admins.
Cons: The same role assignment serves both delegation and autonomous purposes simultaneously. An app assigned
orders:readto participate in auth code flows also holdsorders:readfor autonomous M2M access. Permission bleed is structural, not the result of misconfiguration.Model 2 — Subscription Gate + RBAC (preferred)
A subscription table, managed by the resource server, gates which apps and agents can access it. The subscription is a pure access gate — it carries no permission strings. It just enforces whether an app/agent can access the specific resource server. Auth code permissions come from user RBAC; M2M permissions come from app RBAC. The two mechanisms are fully independent. Both the subscription lookup and RBAC scoping are keyed to the resource server identified by the RFC 8707
resourceparameter in the token request.Pros: Subscription carries no permission strings — zero shared data between auth code and M2M paths; no bleed by design. Resource server owner has full control over who can access it, at whatever granularity the RS requires.
Cons: M2M with role-based access requires both a subscription record in the RS-side and role assignments — two setup steps coordinated between the resource server owner and the role admin.
Model 3 — Permission Allowlist per App/Agent
The subscription record at resource server carries authorized permissions(/scopes) per App/Agent. Auth code tokens are the intersection of user RBAC permissions and the app's authorized permissions. M2M tokens use the permission allowlist as the direct grant.
Pros: Resource server level permission ceiling per app in auth code flows.
Cons: The same permission list is the ceiling for auth code AND the direct grant for M2M — structural bleed that can only be prevented by explicitly restricting the grant types permitted for application/agent.
Configuring Permission lists per app/agent pair do not scale well for complex permission hierarchies.
6. Preferred model: Model 2
Why Model 2
Authorization policy
Model 2 as described above requires RBAC for entities to access resource servers. However, not all resource servers require role assignments. A public catalog API, an MCP server accessible to any dynamically registered client, or any RS where authentication alone is sufficient should not require roles to be configured before access is granted.
An
authorization_policyfield addresses this without a separate mechanism or additional configuration surface. The policy can live at the resource server level (applying uniformly to all subscribers) or at the subscription level (per app/agent) — the semantics are the same either way:authorization_policyrbac(default)noneIn both cases, issued scopes are the intersection of what was requested and what the authorization engine resolves. The
authorization_policyfield is a policy selector — not an RBAC-specific construct — making it naturally extensible to other authorization models such as attribute-based or relationship-based access control, should Thunder adopt them in the future.Note that the subscription check can be also enforced as a ceiling of permissions on that RS for that app/agent, rather than a gate that either allows or denies access. The model is extensible if the requirement comes up to this level-of granularity.
UI/UX
Three admin actors interact with the model across distinct surfaces:
authorization_policyQuestions
Beta Was this translation helpful? Give feedback.
All reactions