App authorization
Optional app role, business permission, assignment and library catalog routes.
App authorization
WithAppPermissions(AppPermissionsConfig{Enable:true}) exposes self access.
EnableManagementHTTP:true additionally exposes the management routes below.
All require a valid session. Management writes also use the configured CSRF
token and origin checks. Every route is wrapped with rate limiting.
Account, role and permission IDs supplied to app authorization operations must
be lowercase, hyphenated UUIDs, exactly as returned by the API. Uppercase,
compact, brace-wrapped and urn:uuid: forms return 400 invalid_input.
The same rule applies to administrative appRoleId bodies and filters, and
to direct Go service inputs.
| Method and path | Access | Request |
|---|---|---|
GET /auth/access | Authenticated account with an enabled current role | None |
GET /admin/authorization/library-permissions | Protected admin plus required MFA | None; fixed {permissions:[{key,name,description}]} without installation status |
PATCH /admin/authorization/library-permissions | Protected admin plus required MFA | {create:[keys],delete:[keys]}; 1–100 total, atomic |
GET /admin/authorization/permissions | goauth.app.permissions.read | Optional limit, offset query; {permissions:[...]} |
POST /admin/authorization/permissions | goauth.app.permissions.create | {key,name,description}; returns definition, 201 |
PATCH /admin/authorization/permissions/{permissionID} | goauth.app.permissions.update | If-Match header; {name?,description?,isEnabled?}; business only |
DELETE /admin/authorization/permissions/{permissionID} | goauth.app.permissions.delete | Required If-Match header; 204 |
GET /admin/authorization/roles | goauth.app.roles.read | Optional limit, offset query; {roles:[...]} with explicit keys |
POST /admin/authorization/roles | goauth.app.roles.create | {slug,name,description?,permissionKeys?}; returns role, 201 |
PATCH /admin/authorization/roles/{roleID} | goauth.app.roles.update | If-Match header; {name?,description?,isEnabled?} |
PUT /admin/authorization/roles/{roleID}/permissions | goauth.app.roles.update | If-Match header; {permissionKeys:[...]}; replaces entire grant set |
DELETE /admin/authorization/roles/{roleID} | goauth.app.roles.delete | Required If-Match header; 204 |
GET /admin/authorization/users/{id}/role | goauth.app.roles.read | None; {role,assignmentRevision} |
GET /admin/authorization/users/{id}/access | goauth.app.roles.read | None; target account's {role,isFullAccess,permissionKeys,assignmentRevision,revision} |
PUT /admin/authorization/users/{id}/role | goauth.app.roles.assign | {roleId,expectedRoleRevision,expectedAssignmentRevision}; returns assignment |
Guarded permission/role PATCH, grant-replacement PUT and DELETE requests all
require one strong revision tag in If-Match, for example If-Match: "3", from
that resource's revision. Do not send expectedRevision in JSON or the query
string; these former transports are rejected. Missing/malformed headers return
400 invalid_input; weak tags, wildcards and tag lists are unsupported. Stale
preconditions return 412 with the existing authorization conflict code. Other
business conflicts still return 409. Role assignment retains the two JSON fields
expectedRoleRevision and expectedAssignmentRevision, because it checks two
separate records. Direct Go service inputs retain ExpectedRevision.
All other successful reads/updates return 200. Optional description defaults to empty. Management JSON rejects unknown fields and trailing documents and is limited to 64 KiB. Actor identity comes from the session. Lists default to 20, accept limits 1–100 (0 selects default), and require nonnegative offsets.
Permission and role responses contain IDs, metadata, enabled state, revision
and timestamps. Definitions also contain key and isSystem. Roles contain
slug, optional systemKey and explicit permissionKeys. Protected admin's
unrestricted policy is reported through access responses' isFullAccess, rather
than an invented wildcard grant.
{
"role": {"id":"ROLE_ID","slug":"support","name":"Support","description":"","isEnabled":true,"revision":2,"permissionKeys":[],"createdAt":"2026-10-07T12:00:00Z","updatedAt":"2026-10-07T12:00:00Z"},
"isFullAccess": false,
"permissionKeys": ["goauth.app.sessions.revoke"],
"assignmentRevision": 2,
"revision": 4
}Both access routes use this response shape. The role field supplies identity
metadata; effective keys are in the top-level permissionKeys and exclude
disabled definitions. These responses describe application access; organization
membership and roles remain separate. Treat them as UI information. Use
server-side RequirePermission or RequireAppPermission for enforcement.
GET /auth/access is the canonical self-service route, alongside /auth/me
and /auth/sessions. The target-access route belongs to authorization management
and is exposed only with EnableManagementHTTP:true. It requires the current
goauth.app.roles.read grant or protected-admin access, plus required MFA, even
when {id} is the caller's own ID. The caller's session supplies actor identity;
{id} selects only the account being inspected. It does not impersonate that
account or check assurance against that account's sessions.
Malformed target IDs return 400. A missing, banned, unassigned, or disabled-role target returns 403; inspecting it does not return an effective access snapshot. Backend failures remain errors. All access checks still reload current state.
Permission-key checks, ownership protection, delegation limits and revision checks also run inside the services. Installation/removal of library keys never becomes a delegable permission. See the implementation and usage guide.
Assigning the user's current role returns 200 without changing the assignment or global revision or recording a role-change audit event. The actor must still pass authorization and required MFA, and both expected revisions must be current.