Fine-grained authorization
RFC 9396 — use authorization_details to express "which object, which action".
Scope can only express "whether a class of permission exists", not "which object, which action". authorization_details (RFC 9396) adds that layer, and MuoCeon uses it to replace the proprietary "elevated action" string.
Not applicable to third-party applications
All three supported authorization detail types map to account self-management capabilities (managing verification methods, sessions and grant relationships), and only MuoCeon's own admin console requests them. Third-party applications should use scopes — see Scopes and audiences. This page documents the mechanism so you can understand token structure.
Request format
authorization_details is a JSON array, passed as a string in the authorization request:
&authorization_details=[{"type":"muoceon_session_management","actions":["session:revoke"]}]Common fields of a single element:
| Field | Required | Notes |
|---|---|---|
type | ✅ | The authorization detail type; must be on the server allowlist |
actions | Optional | Allowed action list; omitting it means all actions of that type |
locations | Optional | Restrict the location scope |
identifier | Optional | Restrict the object identifier |
Up to 10 elements; unknown fields beyond type are preserved verbatim so downstream can read them per its own semantics.
Three authorization detail types
type | Meaning | Allowed actions |
|---|---|---|
muoceon_credential_management | Verification method management | passkey:add, passkey:delete, authenticator:add, authenticator:delete, password:update, recovery_code:delete, oauth:unlink |
muoceon_session_management | Login session management | session:revoke |
muoceon_grant_management | OAuth grant management | grant:revoke |
This allowlist is the sole authority for "what a client may request". Letting arbitrary action strings into authorization codes or tokens would degrade action checks into "the client authorizing itself".
Server-side validation
Every submitted detail is checked item by item:
typenot on the allowlist →400 OAUTH_INVALID_AUTHORIZATION_DETAILS- An action in
actionsnot permitted for that type → same error - Structurally invalid (not an array, more than 10 elements, empty
type) → same error
The consent page expands validated details into human-readable permission entries for the user.
authorization_details in the token
After successful authorization, the details enter the access token:
{
"authorization_details": [
{ "type": "muoceon_session_management", "actions": ["session:revoke"] }
]
}Resource servers decide actions as follows:
- Find the detail with the matching
type; actionsomitted → all actions of that type are allowed;actionsexplicitly listed → the action must match exactly.
Details that fail to parse are treated as "no fine-grained authorization" and never turn a request into a 500.
Two easy-to-miss boundaries
- Refresh does not restore details:
authorization_detailshold only for the initial authorization and are not carried in refreshed tokens. To get new fine-grained authorization, the user must go through the authorization flow again. - Details and scopes are separate gates: details without sufficient scopes still get blocked by
insufficient_scope, and vice versa.