Module 6: Complete Auth System Project — Introduction

Capsule 07: API Documentation

Overview

Your auth system works and it's tested. But if another developer can't use it in 30 minutes, it isn't production-ready. Documentation is what separates "code that works" from "code you can hand over."

In this capsule you'll create: a professional README for developers consuming your API, OpenAPI tags and descriptions that show up in /docs, code examples for common flows, and the final SECURITY.md.


The three kinds of documentation

KindAudienceFormat
READMEDevelopers who will use your APIMarkdown
OpenAPI/SwaggerQuick reference, explorationAuto-generated by FastAPI
SECURITY.mdSecurity teams, auditorsMarkdown

A professional README

# Auth System API

Production-grade auth system with FastAPI: register, login, JWT, RBAC, API keys.

## Stack

- Python 3.12+
- FastAPI
- PostgreSQL 16
- Redis 7
- argon2id (pwdlib) for password hashing
- PyJWT for JWT
- OAuth 2.0 password flow

## Quick Start

```bash
# 1. Clone
git clone https://github.com/you/auth-system.git
cd auth-system

# 2. Install
pip install -r requirements.txt

# 3. Setup .env
cp .env.example .env
# Edit .env with your values

# 4. Generate JWT_SECRET_KEY
python -c "import secrets; print(secrets.token_urlsafe(32))"
# Copy it into .env

# 5. Start Postgres and Redis
docker-compose up -d

# 6. Migrations
alembic upgrade head

# 7. Seed RBAC
python -m app.scripts.seed_rbac

# 8. Run
uvicorn app.main:app --reload

Visit /docs for the interactive Swagger UI.

Authentication Flow

1. Register

curl -X POST http://localhost:8000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"alice@example.com","password":"purple horse jumps moon"}'

Response:

{
  "id": 1,
  "email": "alice@example.com",
  "created_at": "2026-04-25T10:30:00Z"
}

2. Login

OAuth 2.0 password flow — form-data, NOT JSON:

curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=alice@example.com&password=purple horse jumps moon"

Response:

{
  "access_token": "eyJhbGciOi...",
  "refresh_token": "eyJhbGciOi...",
  "token_type": "bearer"
}

3. Use access token

curl http://localhost:8000/api/v1/users/me \
  -H "Authorization: Bearer eyJhbGciOi..."

4. Refresh

When the access token expires (15 min):

curl -X POST http://localhost:8000/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"eyJhbGciOi..."}'

Returns new access + refresh tokens. The old refresh becomes invalid (rotation).

5. Logout

curl -X POST http://localhost:8000/api/v1/auth/logout \
  -H "Authorization: Bearer eyJhbGciOi..."

Token revoked instantly.

Roles & Permissions

Three roles with hierarchy:

  • user — read posts, comment
  • editor — user permissions + create/edit/delete OWN posts
  • admin — editor permissions + manage users, edit/delete ANY post

[See ROLES.md for full matrix]

API Keys (for service-to-service)

# Create
curl -X POST http://localhost:8000/api/v1/api-keys \
  -H "Authorization: Bearer <user-token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI/CD"}'

# Use
curl http://localhost:8000/api/v1/integrations/posts \
  -H "X-API-Key: sk_live_..."

API key plaintext shown ONCE. Save it securely.

Error Codes

StatusMeaning
200/201/204Success
400Business rule violation
401Authentication issue (no token, invalid, expired)
403Authorization issue (no permission)
404Resource not found
409Conflict (e.g., email exists)
422Validation error
429Rate limit exceeded
500Server error (bug — please report)

Rate Limits

EndpointLimit
POST /auth/register3/min per IP
POST /auth/login5/min per IP
POST /auth/refresh20/min per IP

When rate limited: response includes Retry-After: <seconds> header.

Security

See SECURITY.md for security posture details and reporting vulnerabilities.

Development

# Run tests
pytest

# Coverage
pytest --cov=app

# Format
ruff format app/

# Lint
ruff check app/

# Type check
mypy app/

Production Deployment

See DEPLOYMENT.md.

License

MIT


---

## OpenAPI tags and descriptions

FastAPI auto-generates the documentation, but you can improve it:

```python
# app/main.py
app = FastAPI(
    title="Auth System API",
    version="1.0.0",
    description="""
Production-grade authentication system.

## Features

- 🔐 Register, login, refresh, logout
- 🎭 RBAC with 3 roles (user, editor, admin)
- 🔑 API keys for service-to-service
- 🛡️ Argon2id password hashing
- 📝 OAuth 2.0 password flow + JWT
    """,
    contact={
        "name": "Support",
        "email": "support@myapp.com",
    },
    license_info={
        "name": "MIT",
    },
)


# Tags with descriptions
tags_metadata = [
    {
        "name": "auth",
        "description": "Authentication endpoints: register, login, refresh, logout.",
    },
    {
        "name": "users",
        "description": "User-related endpoints (e.g., /me).",
    },
    {
        "name": "posts",
        "description": "Blog posts CRUD. Public reads, authenticated writes.",
    },
    {
        "name": "admin",
        "description": "Admin-only endpoints. Requires `admin` role.",
    },
    {
        "name": "api-keys",
        "description": "Manage API keys for service-to-service authentication.",
    },
    {
        "name": "integrations",
        "description": "Service-to-service endpoints. Authenticated via API key (X-API-Key header).",
    },
]

app.openapi_tags = tags_metadata

Endpoint descriptions

@router.post(
    "/register",
    response_model=UserPublic,
    status_code=status.HTTP_201_CREATED,
    summary="Register a new user",
    description="""
Creates a new user account.

**Validation:**
- Email must be valid format
- Password: 8-128 chars, not in common passwords list

**On success:** returns user profile (without password hash).

**On error:**
- 409 if email already registered
- 422 if validation fails
- 429 if rate limited (3/min per IP)
    """,
    responses={
        201: {"description": "User created successfully"},
        409: {"description": "Email already registered"},
        422: {"description": "Validation error"},
        429: {"description": "Rate limit exceeded"},
    },
)
@limiter.limit("3/minute")
async def register(...):
    ...

Examples in OpenAPI

from pydantic import BaseModel, ConfigDict


class UserRegister(BaseModel):
    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "email": "alice@example.com",
                "password": "purple horse jumps moon",
            }
        }
    )
    
    email: EmailStr
    password: str = Field(..., min_length=8, max_length=128)

/docs will show the example automatically, easy to run.


Multiple examples

class LoginRequest(BaseModel):
    model_config = ConfigDict(
        json_schema_extra={
            "examples": [
                {
                    "summary": "Standard login",
                    "value": {
                        "username": "alice@example.com",
                        "password": "secure_password",
                    },
                },
                {
                    "summary": "Login with scope",
                    "value": {
                        "username": "alice@example.com",
                        "password": "secure_password",
                        "scope": "read:posts write:posts",
                    },
                },
            ]
        }
    )

The final SECURITY.md

# Security Posture

## Reporting Vulnerabilities

**DO NOT** report security issues via GitHub issues.

Send to: security@myapp.com

We'll respond within 48 hours and coordinate disclosure.

## Stack

- **Password hashing:** argon2id via pwdlib (NIST SP 800-63B compliant)
- **JWT:** PyJWT with HS256, secret rotated every 6 months
- **OAuth 2.0:** Password flow with refresh tokens
- **Rate limiting:** slowapi with Redis backend
- **Token revocation:** Redis blacklist with auto-TTL
- **Security headers:** HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy

## Authentication

### Password Storage

- Argon2id with default parameters (memory=64MB, time=3, parallelism=4)
- NIST 2024 compliant: no composition rules, length 8+, blacklist of common passwords
- 256-bit salt generated automatically per password

### Token Management

- Access tokens: 15 minute expiration
- Refresh tokens: 7 day expiration with rotation
- Each token has unique JTI (JWT ID)
- Logout invalidates token via Redis blacklist
- Token versioning for "logout all devices" (admin action)

### Rate Limiting

- POST /auth/register: 3/min per IP
- POST /auth/login: 5/min per IP
- POST /auth/refresh: 20/min per IP

## Authorization

Role-based access control (RBAC) with hierarchy:

- `user` (default) — read access
- `editor` — content management
- `admin` — full access

13 granular permissions, see ROLES.md.

## OWASP API Top 10 (2023) Coverage

| Risk | Status |
|---|---|
| API1: BOLA | ✅ Mitigated |
| API2: Broken Auth | ✅ Mitigated |
| API3: Object Property Auth | ✅ Mitigated |
| API4: Resource Consumption | ✅ Mitigated (pagination, rate limit) |
| API5: Function Auth | ✅ Mitigated (RBAC) |
| API6: Business Flows | N/A |
| API7: SSRF | N/A |
| API8: Misconfiguration | ✅ Mitigated |
| API9: Inventory | ✅ Mitigated |
| API10: Unsafe API Consumption | N/A |

## Compliance

- GDPR: data minimization, right to deletion (admin endpoint)
- SOC 2 Type 1: in progress
- Pen tested: annually by [Vendor]

## Audit Log

Security-relevant events are logged:
- Failed login attempts (with IP, but NOT password)
- Successful login (user_id, IP, timestamp)
- Logout
- Role changes
- API key create/delete
- Permission denials

Logs retained for 1 year.

## Incident Response

If you suspect a security incident:
1. Email security@myapp.com immediately
2. Include: timestamp, affected endpoints, suspicious behavior
3. We'll respond within 4 hours during business hours

ROLES.md (RBAC documentation)

# Roles & Permissions

## Roles

### user (default)
Permissions: posts:read, comments:create, comments:delete:own

Can:
- Read all posts
- Create comments
- Delete their own comments

### editor
Inherits user, plus:
- posts:create
- posts:edit:own
- posts:delete:own

Can:
- All `user` actions
- Create posts
- Edit their own posts
- Delete their own posts

### admin
Inherits editor, plus:
- posts:edit:any
- posts:delete:any
- users:read
- users:manage
- roles:assign
- roles:manage

Can:
- All `editor` actions
- Edit/delete any post
- List/delete users
- Assign/remove roles

## Permission Naming

Format: `resource:action[:scope]`

- `resource` — what entity (posts, users, etc.)
- `action` — what to do (read, create, edit, delete)
- `scope` (optional) — `own` or `any`

## Endpoint × Role Matrix

[Full table from Module 6, capsule 4]

## Adding a New Role

1. Edit `app/scripts/seed_rbac.py`
2. Add to `ROLE_PERMISSIONS_DIRECT`
3. Optionally add to `ROLE_HIERARCHY`
4. Run: `python -m app.scripts.seed_rbac`
5. No code changes needed in endpoints

DEPLOYMENT.md

# Deployment Guide

## Production Checklist

### Pre-deployment

- [ ] All tests passing
- [ ] Coverage > 90%
- [ ] No secrets in code
- [ ] DEBUG=False
- [ ] /docs disabled (or behind auth)
- [ ] CORS configured (no `*`)
- [ ] HTTPS enabled
- [ ] PostgreSQL replica set up
- [ ] Redis with persistence enabled
- [ ] Logging configured (Sentry/CloudWatch/etc.)
- [ ] Monitoring set up (response times, error rates)
- [ ] Backups automated

### Environment Variables

```bash
# Required
DATABASE_URL=postgresql+asyncpg://...
JWT_SECRET_KEY=<openssl rand -base64 32>
REDIS_URL=redis://...

# Recommended
ENVIRONMENT=production
CORS_ORIGINS=["https://myapp.com"]
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=7

Infrastructure

Minimum:

  • 1 application instance (2 vCPU, 4 GB RAM)
  • 1 PostgreSQL instance (with daily backups)
  • 1 Redis instance

Recommended:

  • 2+ application instances behind load balancer
  • PostgreSQL primary + replica
  • Redis with replication

CI/CD

See .github/workflows/test.yml and .github/workflows/deploy.yml.

Common Deployments

Render

# render.yaml
services:
  - type: web
    name: auth-api
    env: python
    buildCommand: pip install -r requirements.txt && alembic upgrade head
    startCommand: uvicorn app.main:app --host 0.0.0.0 --port $PORT

Docker

[Dockerfile + docker-compose example]

AWS

[ECS / Lambda / EC2 instructions]


---

## Expected outputs

After this capsule you have:

- `README.md` — getting started + API usage
- `SECURITY.md` — security posture
- `ROLES.md` — RBAC reference
- `DEPLOYMENT.md` — production guide
- An improved `/docs` with descriptions and examples

Any developer can onboard in under an hour.

---

## Troubleshooting

### Problem 1: The README is too long

Split it into separate sections (DEPLOYMENT.md, ROLES.md). The README should only have the quick start + links.

### Problem 2: /docs doesn't show examples

Check `model_config = ConfigDict(json_schema_extra=...)` in your Pydantic models.

### Problem 3: SECURITY.md has sensitive info

Be careful with details about:

- Internal structure (paths to sensitive files)
- Specific software versions (adversaries can look up CVEs)
- IP addresses / server names

Keep it general — save the specifics for incidents.

---

## Exercises

### Exercise 1: Write the README

Implement the README for your system. It should let a new developer get from setup to login in under 30 min.

<details>
<summary>See solution</summary>

Use the template from the capsule. Customize it for your specific project.

</details>

### Exercise 2: Improve /docs

Add tags metadata, descriptions, and examples to your main endpoints.

<details>
<summary>See solution</summary>

The code is in the capsule. Apply it to:

- /auth/register
- /auth/login
- /auth/refresh
- /posts (POST, PATCH, DELETE)
- /admin/users/{id}/roles

</details>

### Exercise 3: SECURITY.md

Implement SECURITY.md following the template.

<details>
<summary>See solution</summary>

The template is in the capsule. Adapt:

- A real contact email
- The specific stack of your deployment
- Whatever compliance applies to your case

</details>

### Exercise 4: A test that the README still works

Implement a test that runs the README's commands and verifies they work. Useful to keep the README from going stale.

<details>
<summary>See solution</summary>

```bash
#!/bin/bash
# scripts/test_readme.sh
set -e

# Assuming a clean environment
pip install -r requirements.txt
alembic upgrade head
python -m app.scripts.seed_rbac

# Try the README's register command
RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"test@readme.com","password":"purple horse jumps moon"}')

echo "$RESPONSE" | jq -e '.id' > /dev/null || (echo "Register failed" && exit 1)

# Login
TOKENS=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -d "username=test@readme.com&password=purple horse jumps moon")
ACCESS=$(echo "$TOKENS" | jq -r .access_token)

# /me
curl -s http://localhost:8000/api/v1/users/me \
  -H "Authorization: Bearer $ACCESS" | jq -e '.email' > /dev/null

echo "✓ All README commands work"

Run it in CI before every deploy.

Exercise 5: Generate an exportable OpenAPI spec

Export your OpenAPI spec to an openapi.json file.

See solution
# Manual
curl http://localhost:8000/openapi.json > openapi.json

# Or with a script
python -c "
from app.main import app
import json
print(json.dumps(app.openapi(), indent=2))
" > openapi.json

Useful for:

  • Generating client SDKs (with tools like openapi-generator)
  • Offline documentation
  • Versioning the API

Summary

  • A professional README = onboarding in under 30 min for new developers.
  • Quick Start is the critical section — it has to work end-to-end.
  • OpenAPI tags + descriptions + examples improve /docs significantly.
  • SECURITY.md documents posture, not detailed implementation.
  • ROLES.md is the RBAC reference for auditors and devs.
  • DEPLOYMENT.md is the production guide.
  • Documentation is code — versioned, tested, maintained.

Additional resources

  1. README best practices (GitHub) — Examples
  2. OpenAPI Specification — The official spec
  3. FastAPI — Path Operation Configuration — Official docs
  4. Diátaxis — A framework for technical documentation
  5. API Documentation Best Practices — Guidelines

Next step

Capsule 08: Final project + Production checklist. Complete verification, production deployment notes, delivery of the system.