API (Bearer) auth¶
Use Bearer token authentication for API clients, mobile apps, or SPAs that manage tokens in JavaScript memory.
Login¶
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "my-password"}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer"
}
Store both tokens in your client. The access token is short-lived (30 min default), the refresh token is long-lived (7 days default).
Accessing protected routes¶
Send the access token in the Authorization header:
No CSRF token is needed for Bearer requests.
Refreshing tokens¶
When the access token expires, use the refresh token to get a new pair:
curl -X POST http://localhost:8000/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJhbGciOiJIUzI1NiIs..."}'
Refresh tokens are single-use
Each refresh token can only be used once. After refreshing, discard the old tokens and use the new pair. Replaying an old refresh token will revoke the entire token family.
Logout¶
curl -X POST http://localhost:8000/auth/logout \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJhbGciOiJIUzI1NiIs..."}'
This revokes all tokens in the family. The access token will continue to work until it expires (it's stateless), but the refresh token is immediately invalidated.
JavaScript example¶
class AuthClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.accessToken = null;
this.refreshToken = null;
}
async login(email, password) {
const res = await fetch(`${this.baseUrl}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
}
async fetchWithAuth(url, options = {}) {
const res = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${this.accessToken}`,
},
});
if (res.status === 401) {
await this.refresh();
return fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${this.accessToken}`,
},
});
}
return res;
}
async refresh() {
const res = await fetch(`${this.baseUrl}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: this.refreshToken }),
});
const data = await res.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
}
}