How to issue API tokens

From docs/how-to/api-tokens.md in the framework repository.

The token guard of @samveys/auth gives each machine client its own token with a list of abilities.

ts
import { auth, ormUserProvider, requireAbility, tokenGuard } from '@samveys/auth'
import { User } from './app/models/user.ts'

const api = tokenGuard({ provider: ormUserProvider(User) })
export const authPlugin = auth({ guards: { api }, default: 'api' })

// Create one, for example from a console command. The plain text is shown once:
const { plainText } = await api.createToken(user, { name: 'ci-deploy', abilities: ['server:update'] })

// Protect a route: 401 without a valid token, 403 without the ability.
router.post('/servers/:id', { use: ['auth:api', requireAbility('server:update')] }, handler)
  • Clients send Authorization: Bearer svs_<id>.<secret>.
  • Only a SHA-256 of the secret is stored, in samveys_api_tokens. SHA-256 is correct here because the secrets are long and random, unlike passwords, and checking them stays fast on every request. The comparison runs in constant time.
  • A token without server:update is refused with 403 SVS_E094 on a route that requires it. An unknown, revoked, or expired token gets 401 SVS_E093.
  • revokeToken(id) revokes one token. revokeAllTokens(user) revokes every token of a user.

Policies.

  • An ability says what a token may do in general. A policy says whether this user may act on this record.
  • Wrap the handler with guarded(policy, 'update', loadPost, handler). The check then runs right before the handler, so it still holds when someone forgets a middleware.