# Tokens

How long tokens last, how to refresh them, and how to log people out.

## Access tokens

Access tokens are [JWTs](https://jwt.io) that last **one hour**. Send them with every API request:

```http
Authorization: Bearer eyJhbGciOi...
```

Treat them as opaque strings: don't rely on what's inside, as it may change. If you're curious, they include the person's ID (`sub`), your client ID (`client_id`) and the allowed scopes (`scope`).

When a token has run out, the API answers with `401`. Get a new one using the refresh token.

## Refresh tokens

Refresh tokens last **30 days** and keep people logged in without asking them again. `POST` to the token link:

```bash
curl -X POST https://streetartcities.com/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "Yh1c...",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

You get the same response as when swapping a code: a new access token **and a new refresh token**.

<Admonition kind="warning" title="Refresh tokens work once">
  Every refresh gives you a new refresh token, and the old one stops working.
  Always save the new one. If two parts of your app refresh at the same time,
  one of them will fail, so make sure only one refresh happens at once.
</Admonition>

If the refresh token has run out or was revoked, or the person's account has been blocked, you get an `invalid_grant` error. Send the person through the [OAuth flow](/authentication/oauth-flow/) again.

## Logging out

When someone logs out of your app, revoke their refresh token:

```bash
curl -X POST https://streetartcities.com/api/oauth/revoke \
  -H "Content-Type: application/json" \
  -d '{
    "token": "Yh1c...",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

This always answers with `200`, even if the token was already gone. Access tokens can't be revoked, but they run out within an hour.

## Token errors

Errors from the token and revoke links follow the OAuth spec:

```json
{
  "error": "invalid_grant",
  "error_description": "The code is invalid or has expired"
}
```

| Error                    | Status | Meaning                                                                                                                                            |
| ------------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_client`         | 401    | Unknown client ID, or a missing or wrong client secret                                                                                             |
| `invalid_grant`          | 400    | The code or refresh token is wrong, used, or has run out. Also when `redirect_uri` or `code_verifier` don't match, or the account has been blocked |
| `unsupported_grant_type` | 400    | Use `authorization_code` or `refresh_token`                                                                                                        |
