# 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`

```json
{
  "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):

```json
{
  "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:

1. The `Authorization` header value MUST begin with `Bearer ` (case-sensitive).
2. The token MUST be a valid HS256-signed JWT (verified against `JWT_SECRET_KEY`).
3. The `exp` claim MUST be in the future (at time of request receipt).
4. 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) |
