Supermail
Concepts

Authorization

Scopes and grants — what an access token is allowed to do, and how that is decided on every call.

What your application may do is decided by grants and scopes. A grant is minted when a user consents during the authorization flow; scopes are the capabilities it carries.

Scopes

Scopes are a closed set. They are what your app requests at /oauth/authorize, and they map one-to-one to the capabilities enforced on the Resource API.

ScopeGrants
openidOIDC identity — returns an id_token. Carries no Resource API capability.
mail.readThe read surface: event list / get, mailbox listing, and explicit history synchronizations (AccountSynchronizations/*). A history synchronization re-fetches history from the provider — a read that appends locally and changes nothing upstream, so it stays in this scope.
mail.modifyMessage flags, moves, archive/trash/spam/delete, draft writes, mailbox create/rename/delete, and modify-purpose staged uploads.
mail.sendSend-purpose staged uploads plus durable submission send/get.
accounts.readRead the user's mailboxes.
accounts.updateChange a mailbox's personal presentation (Accounts/update).
accounts.deletePurge a mailbox (Accounts/purge).
bindings.updateDisconnect a mailbox (Accounts/disconnect) and attach or reconnect service bindings.
accounts.createConnect a new mailbox. Global-only — automatically carries global accounts.{read,update,delete}.

accounts.create makes no sense scoped to one existing mailbox, so it requires the "all my mailboxes" selector and auto-carries the other accounts.* capabilities, letting your app manage the mailboxes it creates.

An application's registered scopes are its ceiling: an authorization request may ask for any subset, but never for a scope outside that set. The app owner also chooses a required_scopes subset. Every other registered scope is recommended; the consent screen labels these Optional for the user.

On consent, all requested scopes start selected:

  • Required scopes stay selected and cannot be unchecked.
  • Recommended scopes, labeled Optional, start selected, but the user may remove them.

Required scopes are a protocol rule, not just a disabled checkbox. Supermail enforces this relationship when the app is created or updated, when authorization starts, when consent is approved, and again when tokens are issued:

required ⊆ granted ⊆ requested ⊆ registered

An authorization request that omits one of the app's required scopes fails with invalid_scope. To present every recommended capability as the default, request the app's full registered scope set.

Grants

A grant records (user, app, account-selector, scopes). When the user consents, they choose which mailboxes your app reaches:

  • A single mailbox — the grant covers exactly that account.
  • All of the user's mailboxes — the grant covers every account they own, now and in the future.

There are no mailbox groups. To reach several specific mailboxes, an app holds several single-account grants, so one (user, app) pair may hold multiple grants at once.

Grants are revoked, never edited:

  • Widening (more scopes or more mailboxes) is re-consent on the difference — it supersedes the old grant with a new one.
  • Narrowing or revoking is unilateral, needs no involvement from your app, and takes effect on the next API call.

Live enforcement

Every Resource API call runs the same check:

  1. Verify the bearer token (signature, expiry, issuer).
  2. Re-resolve the live grant named by the token's grant_id.
  3. Confirm the grant covers the target account with the required capability.
  4. For writes, confirm the account's working capability also admits the verb.
  5. Perform the request.

Because step 2 reads the current grant — not the token's scope claim — a revoke or narrowing is enforced immediately, inside the access token's remaining lifetime. A widening waits for the user to re-consent.

Delegated login grants

Delegated portal login uses a separate kind of grant carrying the internal delegated_login capability. It proves that one trusted confidential app may sign one app-local subject into the portal. It does not grant Resource API access, does not appear in an OAuth access token, and is not part of the scope set requested at /oauth/authorize.

The delegated grant is projected as a login method in the user's profile. Removing that method revokes the grant and its outstanding login tickets.

The service-binding axis (attaching, disconnecting, reconnecting) is independent of a grant's scopes. Disconnecting a mailbox does not revoke a grant, and revoking a grant does not disconnect a mailbox. An authenticated external resource has one durable reservation owner. Conflicting attachment fails without identifying that owner; disconnect keeps the reservation and account purge releases it.

On this page