Enable
Device is optional and needs DeviceCodeStorage as well. New returns ErrStorageMissingCapability when the storage lacks a required capability.
Model
- A token is
<prefix>_<43 random chars>. The secret is returned once. Only its SHA-256 is stored, plus a short hint for display. - Abilities are strings you define.
rootis reserved, implies every ability, and cannot be combined with others. - Every token expires. The default lifetime is 90 days, capped by
MaxTTL(365 days). - The owner is a user or a service account. A service account is an ID with no user row, created by an admin minting a token with
service_account: true. - Abilities are checked fresh on every request: a token’s effective abilities are its stored abilities intersected with what the owner currently holds. Demoting a user narrows their existing tokens at once.
- A token stops working when its owner row is deleted or
OwnerActivereports false. CallRevokeOwnerAPITokenswhen you delete a user or retire a service account.
UserAbilities. Without it an admin (IsAdmin, by default the system super_admin RBAC role) holds root and everyone else holds nothing.
Protect routes
RequireAbility accepts a full session cookie or Authorization: Bearer. A presented bearer token never falls back to the cookie. Handlers read the caller with theauth.PrincipalFromContext.
HTTP routes
Token management is session only, so a token cannot mint or revoke tokens.
Device grant
- The CLI calls
POST /auth/device/code(client_name,scopeas space separated abilities) and shows theuser_codeandverification_uri. - A signed-in user posts the code to
POST /auth/device/approvewithactionofinfo,approveordeny, optionally narrowingabilities. - The CLI polls
POST /auth/device/tokenwith the device grant type until it getsaccess_token.
- Polling faster than the interval returns
slow_downand widens the interval by 5 seconds. - Redeem is an atomic claim: of any number of concurrent polls, exactly one mints a token.
- The minted token expires (
DeviceConfig.TokenTTL, default 30 days) and its abilities are the requested set capped to the approver’s current abilities.rootis granted only when requested and the approver holds it, otherwise approval fails with 403. - Wrong user codes count against a per-approver and per-IP budget (5 per 15 minutes), after which approval returns 429.
- Show the requester IP and user agent from
action: infoon your approval page.
Approve from a pending list
A dashboard can show pending requests so a signed-in user approves or denies one without typing the code. These routes need a session (bearer tokens get 401) and use the same rate limit as/auth/device/approve:
GET /auth/device/requestsreturns{"requests": [...]}withid,clientName,requestedAbilities,requesterIp,requesterUserAgent,createdAtandexpiresAtfor each pending, unexpired request, newest first. It never includes the device code, its hash, or the user code. Show the IP and user agent so people recognize their own request.POST /auth/device/requests/{id}/approveandPOST /auth/device/requests/{id}/denydecide by request ID with the same rules as the code route: atomic, abilities capped to the approver’s,rootonly when requested and held. Approve accepts an optionalabilitieslist to narrow. Adevice.approvedordevice.deniedaudit event is recorded.
APITokensConfig.DeviceRequestsAbility (empty means root), checked against the user’s current abilities on every request; anyone else gets 403 auth.forbidden. Set it to an ability your app defines to delegate review. DeviceRequestsAnySignedInUser: true opens the routes to every signed-in user, but approving from a list skips the proof of holding the code shown on the device, which makes phishing easier in a multi-user app. POST /auth/device/approve (by user code) is not affected. The list needs the optional DeviceCodeLister storage extension (memory, SQLite, Postgres and MySQL implement it); without it the routes return 404.
Go callers can skip HTTP: StartDeviceAuth, LookupDeviceRequest, DecideDeviceRequest, ListDeviceRequests, DecideDeviceRequestByID, RedeemDeviceCode, MintAPIToken, AuthenticateAPIToken, ListAPITokens, RevokeAPIToken.
Custom storage
ImplementAPITokenStorage and DeviceCodeStorage and optionally DeviceCodeLister for the pending list, and run storagetest.RunAPITokens and storagetest.RunDeviceCodes. ClaimDeviceCode and DecideDeviceCode must be single compare-and-set statements. Do not add a foreign key from the token owner to users: service account owners have no user row.