# auth.access-token
The access-token rules of a small API, so its request handlers are only I/O:
```
# login, after the password checked out
token = issueAccessToken(str(user.id), user.name, user.email, user.token_version,
random_jti, now, 3600, secret)
# -> {accessToken, tokenType: "Bearer", expiresAt: "2026-09-26T13:00:00Z", expiresIn: 3600}
# every authenticated request
check = readAccessToken(headers.get("Authorization"), secret, now, 30)
if check.ok:
user = db.user(check.subject) # may be None
check = confirmAccessToken(check, user and user.token_version, db.is_revoked(check.jti))
if not check.ok: answer 401 # check.error says why, for the log
```
It is a group because the three share one token layout: `issueAccessToken`
writes the claims `sub`, `name`, `email`, `ver`, `jti`, `iat` and `exp`,
signed HS256 with `auth.jwt`, and `readAccessToken` insists on finding `sub`,
`jti`, `ver` and `exp` in anything it accepts. A token that is validly signed
but lacks them (or has no `exp`, so would never expire) is `invalid_claims`.
**Revocation** takes two forms, and `confirmAccessToken` applies both once
the caller has done the lookups a pure function cannot:
- **Log out** revokes one token: store its `jti` (until its `expiresAt`, after
which it is dead anyway) and pass `revoked = true` when it comes back.
- **Change password** revokes every token the user holds: increment the user's
token version in the database. Every token carries the version it was issued
at (`ver`), and one that no longer matches is refused.
- A user who no longer exists (`currentTokenVersion` null) is `user_not_found`.
A check that has already failed passes through `confirmAccessToken`
unchanged, so the three calls can be chained without branching.
Nothing here reads the clock or makes randomness: `now` is Unix seconds from
the caller and `jti` is random text from the caller (for instance
`secrets.token_urlsafe(16)`). `expiresAt` is written by `time.unix-to-iso`.
Errors from `readAccessToken` are `auth.jwt`'s codes plus `missing_token`
(no `Authorization: Bearer` header, read with `auth.bearer-token`); an API
should answer all of them with the same 401 and keep the code for its logs.
A secret shorter than 32 bytes, a negative leeway, an empty subject or jti,
a negative token version or a lifetime under one second are the caller's
mistakes and throw.
Sources: RFC 7519 sections 4.1.2 (sub), 4.1.4 (exp), 4.1.6 (iat), 4.1.7
(jti) (https://www.rfc-editor.org/rfc/rfc7519); RFC 6750 section 3, the
WWW-Authenticate response for 401 (https://www.rfc-editor.org/rfc/rfc6750).