# API Reference Base URL (local dev): `http://localhost:8080` All request/response bodies are JSON. Authenticated endpoints rely on the `session_id` cookie set by `/login` or the Google OAuth callback - include it automatically by using a cookie-aware HTTP client (browsers do this natively; with `curl`, use `-c cookies.txt -b cookies.txt`). --- ## `GET /health` Liveness check. No authentication, no rate limiting beyond the global limit. **Response `200`** ```json { "status": "ok" } ``` ```bash curl http://localhost:8080/health ``` --- ## `POST /register` Creates a new password-based account. Rate limited to **5 requests/minute per IP** (shared with `/login`). **Request body** ```json { "email": "hamid@example.com", "password": "secret123" } ``` - `email` - required, must be unique across all accounts. - `password` - required, minimum 8 characters. **Response `201`** ```json { "id": 1, "email": "hamid@example.com" } ``` **Errors** | Status | Body | Cause | |---|---|---| | 400 | `{"error":"invalid request body"}` | Malformed JSON | | 400 | `{"error":"email and password are required"}` | Missing field | | 400 | `{"error":"password must be at least 8 characters"}` | Password too short | | 409 | `{"error":"email already registered"}` | Email already taken | | 429 | (rate limit response) | Too many requests from this IP | | 500 | `{"error":"internal error"}` | Unexpected server/database failure | ```bash curl -X POST http://localhost:8080/register \ -H "Content-Type: application/json" \ -d '{"email":"hamid@example.com","password":"secret123"}' ``` --- ## `POST /login` Authenticates with email + password and starts a server-side session. Rate limited to **5 requests/minute per IP** (shared with `/register`). **Request body** ```json { "email": "hamid@example.com", "password": "secret123" } ``` **Response `200`** ```json { "id": 1, "email": "hamid@example.com" } ``` Also sets a `session_id` cookie (`HttpOnly`, `SameSite=Lax`, `Secure` in production). **Errors** | Status | Body | Cause | |---|---|---| | 400 | `{"error":"invalid request body"}` | Malformed JSON | | 401 | `{"error":"invalid email or password"}` | No such email, OR wrong password (identical message for both, deliberately - see Security notes in the README) | | 429 | (rate limit response) | Too many requests from this IP | | 500 | `{"error":"internal error"}` | Unexpected server/database failure | ```bash curl -c cookies.txt -X POST http://localhost:8080/login \ -H "Content-Type: application/json" \ -d '{"email":"hamid@example.com","password":"secret123"}' ``` --- ## `POST /logout` Destroys the current session (deletes it from Redis, expires the cookie). Not rate-limited beyond the global limit - deliberately excluded from the strict `/login`/`/register` limit so a legitimate user can always log out. **Response `200`** ```json { "message": "logged out" } ``` ```bash curl -b cookies.txt -c cookies.txt -X POST http://localhost:8080/logout ``` --- ## `GET /me` **Requires authentication** (a valid `session_id` cookie from a prior login). Returns the currently logged-in user. **Response `200`** ```json { "id": 1, "email": "hamid@example.com" } ``` **Errors** | Status | Body | Cause | |---|---|---| | 401 | `{"error":"unauthorized"}` | No session, expired session, or the session's user no longer exists | ```bash curl -b cookies.txt http://localhost:8080/me ``` --- ## `GET /auth/google/login` Redirects the browser to Google's OAuth2 consent screen. **Must be opened in an actual browser** - this endpoint returns an HTTP redirect, and the subsequent Google login page cannot be driven via `curl`. **Response**: `307 Temporary Redirect` to `accounts.google.com`. ``` open http://localhost:8080/auth/google/login ``` --- ## `GET /auth/google/callback` Google redirects here automatically after the user approves access. Not meant to be called directly - `state` and `code` query parameters are supplied by Google. On success: creates a new user (or links Google to an existing email-matched account), starts a session exactly like `/login` does, and returns: **Response `200`** ```json { "id": 2, "email": "hamid@gmail.com" } ``` **Errors** | Status | Body | Cause | |---|---|---| | 400 | `{"error":"invalid oauth state"}` | Missing/mismatched CSRF state - usually means the flow wasn't started via `/auth/google/login`, or the session expired mid-flow | | 400 | `{"error":"missing code"}` | Google didn't include an authorization code | | 500 | `{"error":"internal error"}` | Token exchange, Google API call, or database failure | --- ## General notes - **CORS**: browser-based requests from an origin not listed in `ALLOWED_ORIGINS` will be blocked by the browser itself, before this API's own logic ever runs. Non-browser clients (curl, mobile apps, server-to-server) are unaffected by CORS entirely. - **Rate limiting**: exceeding a limit returns HTTP `429 Too Many Requests`. The global limit (100/min/IP) applies to every route; the strict limit (5/min/IP) applies only to `/register` and `/login`, and stacks with the global limit. - All error responses share the same shape: `{"error": ""}`.