Files
go-simple-api/docs/API.md
T
2026-07-16 10:13:46 +03:30

185 lines
5.1 KiB
Markdown

# 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": "<message>"}`.