Skip to Content
MuoCeon Docs
Third-party OAuth IntegrationPermissions and security

Scopes and audiences

How permissions are named, which ones you may request, and what the token's aud actually decides.

Scope answers "what may be done", audience (aud) answers "to whom". They are two independent gates: a request can pass the scope check and still be rejected for the wrong audience.

Scope naming convention

Self-management scopes are always three segments:

<resource audience>:<object>:<action>

For example auth:sessions:read means "perform read on the sessions object of the auth resource server". The benefit is that authorization can be derived directly from the name without a second mapping table, and logs immediately reveal which resource a scope belongs to.

OIDC standard scopes (openid, profile, email, offline_access) have no prefix — they belong to no resource audience and are classified separately.

Scopes you may request

Delegable scopes

ScopeMeaningConsent page label
openidRequest OIDC identity: an id_token is issued at token exchange, and UserInfo becomes available"Verify your identity"
profileRead nickname and display name (preferred_username, name)"Read your basic profile"
emailRead email address and verification status (email, email_verified)"Read your email address"
offline_accessAccess continues while the user is away: a refresh token is issued"Continue accessing while you are away"

Self-management scopes (not available to third parties)

ScopeAudienceMeaning
auth:credentials:readauthList the user's verification methods
auth:credentials:writeauthAdd or remove verification methods
auth:sessions:readauthList active sessions
auth:sessions:revokeauthRevoke sessions
oauth:grants:readoauthList authorized applications
oauth:grants:revokeoauthRevoke granted authorizations
account:profile:readaccountRead the account's extended profile
account:profile:writeaccountUpdate the account's extended profile

These scopes express "the user's authority over their own account". They appear only in MuoCeon's own management UI and are never delegated to third-party applications.

Audiences (aud)

AudienceResource identifierEndpoints it covers
auth (default){origin}/v1/authSession state, verification methods, sessions, UserInfo
oauth{origin}/v1/oauthAuthorized applications and grant management
account{origin}/v1/accountAccount extended profile

Tokens from third-party authorization carry the auth audience — because the only thing you can do is read the user's identity. There is neither a scope to request nor a grant type to use for obtaining oauth or account audience tokens.

The token's aud contains both the shorthand and the full resource identifier:

{
  "aud": ["auth", "https://id.example.com/v1/auth"]
}
  • The shorthand is used by internal routing for allowlist comparison;
  • The full URI is used by standard clients to compare against the RFC 9728 metadata's resource.

If your library performs strict string array matching, compare against the full URI and the protected resource metadata — do not match on the shorthand alone.

Filtering: requesting is not receiving

The server filters scopes by client capability in both the code exchange and the refresh:

  1. Delegable scopes: allowed.
  2. Self-management scopes: allowed only for clients granted the corresponding audience at registration; filtered out for third parties.
  3. Filtering is a silent drop, not an error — the scope in the response is the effective list.

Always read the scope in the response

Requesting openid profile email auth:sessions:read may actually return only openid profile email. If you build downstream logic on "I requested it, so I have it", you will get a 403 insufficient_scope when calling management endpoints instead of a clear error at token exchange.

Two more boundaries:

  • On refresh, scope may only narrow; values outside the original grant yield invalid_scope.
  • client_credentials (machine clients) cannot request offline_access — no user means no session to refresh.

Practical advice

  • Request least privilege: if you only read identity, ask for openid profile email and do not add offline_access reflexively.
  • offline_access means you can still access data while the user is away; it is listed separately on the consent page, and overusing it measurably reduces consent rates.
  • Do not treat anything other than sub as a stable identifier: email changes, nicknames change, sub does not.
  • Update your local record with the scope returned on every refresh; never carry the old value forward.

On this page