Protects image upload, delete, and tag-update endpoints behind Bearer token auth. Public read endpoints remain open. Angular SPA gains a login page, auth interceptor, and route guard for /upload. - JWTAuthProvider (HS256, sub/iat/exp, secrets.compare_digest) - POST /api/v1/auth/token login endpoint - require_auth FastAPI dependency on all write routes - AuthService, LoginComponent, authInterceptor, authGuard - Detail page hides write controls for unauthenticated visitors - 43 unit tests passing; integration tests require Docker stack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.1 KiB
API Contracts: JWT Bearer Token Authentication
Feature: 004-jwt-bearer-auth | Date: 2026-05-03
All routes remain under /api/v1/. Error responses use the existing envelope:
{ "detail": "<human message>", "code": "<machine code>" }.
New Endpoint
POST /api/v1/auth/token
Issues a bearer token for the owner after verifying credentials.
Request
Content-Type: application/json
{
"username": "<string>",
"password": "<string>"
}
Both fields are required. A missing or empty field returns 422.
Success response — 200 OK
{
"access_token": "<jwt-string>",
"token_type": "bearer",
"expires_in": 86400
}
expires_in reflects the configured JWT_EXPIRY_SECONDS value.
Failure responses
| Status | Code | When |
|---|---|---|
401 |
invalid_credentials |
Username or password is wrong |
422 |
(FastAPI default) | Missing or malformed request body |
Changed Endpoints — Access Control
The following endpoints now require a valid bearer token. Requests without
a token, or with an invalid/expired token, receive a 401.
| Method | Path | Was | Now |
|---|---|---|---|
POST |
/api/v1/images |
Public | Protected |
DELETE |
/api/v1/images/{id} |
Public | Protected |
PATCH |
/api/v1/images/{id}/tags |
Public | Protected |
Bearer token transmission
The client MUST include the token in the Authorization header:
Authorization: Bearer <access_token>
401 response shape (returned by all three protected endpoints when authentication fails):
{
"detail": "Authentication required",
"code": "unauthorized"
}
Unchanged Endpoints — Remain Public
The following endpoints require no token and must continue to accept requests
without an Authorization header:
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/images |
List / filter images |
GET |
/api/v1/images/{id} |
Get image metadata |
GET |
/api/v1/images/{id}/file |
Serve original image |
GET |
/api/v1/images/{id}/thumbnail |
Serve image thumbnail |
GET |
/api/v1/tags |
List / search tags |
GET |
/api/v1/health |
Health check |
Sending a token on these endpoints is harmless (the server ignores it) but is not required.
Token Validation Rules
The API validates tokens using the following rules, in order:
- The
Authorizationheader value MUST begin withBearer(case-sensitive). - The token MUST be a valid HS256-signed JWT (verified against
JWT_SECRET_KEY). - The
expclaim MUST be in the future (at time of request receipt). - Any failure in steps 1–3 returns
401 unauthorized.
UI Route Contracts
These are Angular SPA routes affected by this feature.
| Route | Guard | Behaviour |
|---|---|---|
/login |
None | Login form; redirects to returnUrl or / on success |
/upload |
authGuard |
Redirects to /login?returnUrl=/upload if not authenticated |
/images/:id |
None | Always accessible; tag-edit and delete controls visible only when authenticated |
/ |
None | Always accessible (library) |