# Authorized Apps

These endpoints manage the authenticated consumer account's OAuth consent.
They are separate from registration ownership, organization invitations,
app-to-app connections, and [root SSO session revocation](https://m7.org/docs/api/api.user.m7.org/sessions.md).

Use JSON `POST` requests at `https://api.user.m7.org/api/v2` and the standard
[API response envelope](https://m7.org/docs/api/api.user.m7.org/README.md#request-and-response-format). Responses are
non-cacheable. Send the consumer access token and required binding headers. Each operation
requires its corresponding `account.me.authorized_apps.list`, `.view`, or
`.revoke` scope. Machine and organization credentials cannot select another
user; `uid`, `user`, `user_id`, `tenant`, `org`, and `owner` inputs are rejected.

| Method | Route under `/api/v2` | Input |
| --- | --- | --- |
| POST | `/account/me/authorized-apps/list` | None |
| POST | `/account/me/authorized-apps/view` | `application_id` |
| POST | `/account/me/authorized-apps/revoke` | `application_id`, optional boolean `revoke_sessions`; `revocation_request_id` when true |

`application_id` is the internal registration UUID returned by list. View shows
individual immutable permission snapshots. The grouped scope/resource union in
list is a display summary, never an authorization grant. List returns
`comment: "OK"` with `data.items` (an unpaginated array, empty when there is
no saved consent or pending revocation). Each item contains `application_id`,
`client_id`, `name`, `last_consented_at`, `grant_count`, `scopes`, `audiences`,
`session_revocation`, and nullable `revocation_request_id`. Results sort by
latest consent first, then application name. An unavailable registration can
retain its consent summary; it does not become an active application.

View returns `comment: "OK"` with the application summary and `grants`: each
grant has `id`, `consented_at`, `scopes`, `audiences`, and `flow` (`browser`,
`browser_direct`, `device`, or null). Pending revocation can include withdrawn
snapshots so its original work remains visible. A missing account/app consent
returns HTTP 404 with the normal failure envelope.

For example, list the signed-in consumer’s authorizations:

```bash
curl --fail-with-body --silent --show-error \
  --request POST 'https://api.user.m7.org/api/v2/account/me/authorized-apps/list' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{}'
```

For a bound token, add the [required sender proof](https://m7.org/docs/api/api.user.m7.org/authorization.md).

## Revocation choices

With `revoke_sessions: false` (the default), revoke removes saved consent and
leaves issued tokens under their normal expiry and rotation rules. The response
includes `revoked_count`, `session_revocation: "not_requested"`, and
`tokens_revoked: 0`.

To withdraw current token access as well, send:

```json
{
  "application_id": "APPLICATION_UUID_FROM_LIST",
  "revoke_sessions": true,
  "revocation_request_id": "FRESH_REQUEST_UUID"
}
```

Generate a new `revocation_request_id` UUID for each new action. Reuse it for
every retry of that action, including an ambiguous/lost response. The server
freezes the account/app's original grant IDs in a durable request before remote
work. A request ID cannot be reused by another account or for another app.
Retries use that snapshot even after completion and cannot revoke a newer grant.

`session_revocation` is `"complete"` only after Identity confirms every grant's
access and refresh sessions revoked. `"pending"` means saved consent is already
revoked but remote termination still requires retry. The request ID is echoed;
`tokens_revoked` counts confirmed token-session changes. List retains pending
work with its `revocation_request_id`, so users can retry after reloading.
`remaining_application`, when present, describes a newer active authorization
that must stay visible after the old request completes.

Revocation affects the selected account/app's grant-bound access and refresh
records, including sibling tokens, descendants, grace predecessors, and pending
ACK candidates. The shared SSO root, unrelated apps and other users remain
active. Issuance and revocation serialize on an immutable Identity grant binding;
revoking an old grant never bans later approval with a new grant ID.

Online Identity validation rejects revoked tokens. Offline JWT verification
cannot observe server-side revocation until expiry. This operation does not
delete application-owned browser cookies or broadcast root-session logout.

After deploying grant/session linkage to this pre-release system, obtain fresh
tokens once. Earlier issued tokens have no durable consent ID: refresh requires
fresh authorization, and unlinked access tokens retain their existing expiry.
They are never guessed from a shared root or broadly revoked across apps.

## Errors and recovery

Revoke returns `comment: "REVOKED"` with the `application_id` and the result
fields above. Check the envelope’s `status` and `session_revocation`; HTTP
success alone does not mean remote token termination completed. Invalid
application/request UUIDs, a nonboolean `revoke_sessions`, or a principal
selector fail through the ordinary API failure envelope. A request UUID owned
by another account or app returns HTTP 403 with `Revocation request is
unavailable`.

When a session revocation is already pending, do not initiate a consent-only
withdrawal or invent a new request UUID. Recover the stored request UUID from
list and repeat the same `revoke` request with `revoke_sessions: true`. A new
approval requires a new authorization transaction. See the
[SSO consent contract](https://m7.org/docs/api/sso.user.m7.org/authorization-requests.md#consent-behavior).
