{"id":31186042,"url":"https://github.com/obeim/fastify-auth-template","last_synced_at":"2026-05-05T14:03:18.860Z","repository":{"id":314496677,"uuid":"1055151913","full_name":"obeim/fastify-auth-template","owner":"obeim","description":"A secure authentication \u0026 authorization starter template built with Fastify, TypeScript, and Prisma, following OWASP security best practices.","archived":false,"fork":false,"pushed_at":"2025-12-11T20:20:29.000Z","size":105,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-13T00:49:58.509Z","etag":null,"topics":["authentication","authorization","e2e-testing","fastify","integration-test","nodejs","owasp-top-10","postgres","prisma","rest-api","testing","typescript","unit-testing","vitest"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obeim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-11T21:05:44.000Z","updated_at":"2025-12-11T20:20:33.000Z","dependencies_parsed_at":"2025-09-12T21:11:33.356Z","dependency_job_id":"3b0540cc-fe60-461b-90f6-dbe4f95b60a0","html_url":"https://github.com/obeim/fastify-auth-template","commit_stats":null,"previous_names":["obeim/fastify-auth-template"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/obeim/fastify-auth-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obeim%2Ffastify-auth-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obeim%2Ffastify-auth-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obeim%2Ffastify-auth-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obeim%2Ffastify-auth-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obeim","download_url":"https://codeload.github.com/obeim/fastify-auth-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obeim%2Ffastify-auth-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32652480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","e2e-testing","fastify","integration-test","nodejs","owasp-top-10","postgres","prisma","rest-api","testing","typescript","unit-testing","vitest"],"created_at":"2025-09-19T19:22:33.997Z","updated_at":"2026-05-05T14:03:18.854Z","avatar_url":"https://github.com/obeim.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fastify Auth Template (TypeScript + Prisma)\n\nA secure authentication \u0026 authorization starter template built with **Fastify**, **TypeScript**, and **Prisma**, following **OWASP security best practices**. Designed as a boilerplate for small to mid-sized projects.\n\n## Table of Contents\n\n- [Features](#features)\n- [Tech Stack](#tech-stack)\n- [Project Structure](#project-structure)\n- [Getting Started](#getting-started)\n  - [Prerequisites](#prerequisites)\n  - [Installation](#installation)\n  - [Environment Variables](#environment-variables)\n  - [Database Setup](#database-setup)\n- [Development](#development)\n- [API Endpoints](#api-endpoints)\n- [Authentication Flow](#authentication-flow)\n- [Role-Based Authorization](#role-based-authorization)\n- [Testing](#testing)\n  - [Test Structure](#test-structure)\n  - [Running Tests](#running-tests)\n  - [Writing Tests](#writing-tests)\n- [Security Best Practices](#security-best-practices)\n- [Project Guidelines](#project-guidelines)\n- [Contributing](#contributing)\n\n---\n\n## Features\n\n- **JWT Authentication**\n  - Access token stored in memory (15-minute expiry)\n  - Refresh token stored in **HTTP-only secure cookies** (7-day expiry)\n  - Automatic token rotation on refresh\n- **Role-based Authorization**\n  - Built-in roles: `USER`, `ADMIN`, `MODERATOR`\n  - Flexible route protection with role combinations\n- **OWASP Security Standards**\n  - Secure cookie flags (`HttpOnly`, `Secure`, `SameSite=Strict`)\n  - Token rotation on refresh (prevents token replay attacks)\n  - CORS with strict origin \u0026 credentials\n  - Input validation with [TypeBox](https://github.com/sinclairzx81/typebox)\n  - Password hashing with bcrypt (10 rounds)\n  - Helmet for secure HTTP headers\n  - Rate limiting (100 requests/minute)\n- **Developer Experience**\n  - Full TypeScript support with strict typing\n  - Hot reload in development\n  - ESLint + Husky for code quality\n  - Comprehensive test suite (Unit, Integration, E2E)\n\n---\n\n## Tech Stack\n\n| Technology                                         | Purpose                              |\n| -------------------------------------------------- | ------------------------------------ |\n| [Fastify](https://fastify.io/)                     | High-performance web framework       |\n| [TypeScript](https://www.typescriptlang.org/)      | Type safety and developer experience |\n| [Prisma](https://www.prisma.io/)                   | Database ORM with type-safe queries  |\n| [PostgreSQL](https://www.postgresql.org/)          | Primary database                     |\n| [Vitest](https://vitest.dev/)                      | Testing framework                    |\n| [TypeBox](https://github.com/sinclairzx81/typebox) | Runtime type validation              |\n\n---\n\n## Project Structure\n\n```\nfastify-auth-template/\n├── src/\n│   ├── server.ts              # Application entry point\n│   ├── config/\n│   │   └── config.ts          # Environment configuration\n│   ├── controllers/\n│   │   ├── auth.controller.ts # Authentication request handlers\n│   │   └── users.controller.ts# User routes request handlers\n│   ├── plugins/\n│   │   ├── auth.ts            # JWT authentication plugin\n│   │   └── prisma.ts          # Prisma database plugin\n│   ├── routes/\n│   │   ├── auth.ts            # Authentication routes\n│   │   └── users.ts           # User routes (with role examples)\n│   ├── services/\n│   │   ├── auth.service.ts    # Business logic for auth\n│   │   └── auth.service.test.ts # Unit tests for auth service\n│   ├── types/\n│   │   ├── auth.d.ts          # JWT payload type definitions\n│   │   └── fastify.d.ts       # Fastify request/reply types\n│   └── validations/\n│       └── auth.ts            # TypeBox validation schemas\n├── __tests__/\n│   ├── helpers/\n│   │   ├── test-app.ts        # Test application builder\n│   │   └── test-database.ts   # Database utilities for tests\n│   ├── e2e/\n│   │   ├── auth.e2e.test.ts   # End-to-end auth flow tests\n│   │   └── users.e2e.test.ts  # End-to-end user flow tests\n│   └── integration/\n│       ├── auth.integration.test.ts  # Auth API integration tests\n│       └── users.integration.test.ts # User routes integration tests\n├── prisma/\n│   ├── schema.prisma          # Database schema\n│   └── migrations/            # Database migrations\n├── vitest.config.ts           # Test configuration\n├── tsconfig.json              # TypeScript configuration\n├── eslint.config.ts           # ESLint configuration\n└── package.json               # Dependencies and scripts\n```\n\n### Directory Breakdown\n\n| Directory                | Purpose                                                   |\n| ------------------------ | --------------------------------------------------------- |\n| `src/config/`            | Application configuration and environment variables       |\n| `src/controllers/`       | HTTP request handlers (thin layer, delegates to services) |\n| `src/plugins/`           | Fastify plugins for cross-cutting concerns                |\n| `src/routes/`            | Route definitions and middleware attachment               |\n| `src/services/`          | Business logic layer (testable, framework-agnostic)       |\n| `src/types/`             | TypeScript type definitions and declarations              |\n| `src/validations/`       | Request/response validation schemas                       |\n| `__tests__/helpers/`     | Shared test utilities and fixtures                        |\n| `__tests__/e2e/`         | End-to-end tests (complete user flows)                    |\n| `__tests__/integration/` | Integration tests (API endpoint testing)                  |\n| `prisma/`                | Database schema and migrations                            |\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- **Node.js** \u003e= 18.x\n- **pnpm** (recommended) or npm\n- **PostgreSQL** \u003e= 13.x\n- **Docker** (optional, for containerized development)\n\n### Installation\n\n```bash\n# Clone the repository\ngit clone \u003crepository-url\u003e\ncd fastify-auth-template\n\n# Install dependencies\npnpm install\n\n# Generate Prisma client\npnpm prisma generate\n```\n\n### Environment Variables\n\nCreate a `.env` file in the root directory:\n\n```env\n# Server\nPORT=3000\nHOST=0.0.0.0\nNODE_ENV=development\n\n# Database\nDATABASE_URL=\"postgresql://user:password@localhost:5432/mydb?schema=public\"\n\n# JWT\nJWT_SECRET=\"your-super-secret-jwt-key-change-in-production\"\n\n# CORS (JSON array of allowed origins)\nCORS_ALLOWED_ORIGINS='[\"http://localhost:3000\", \"http://localhost:5173\"]'\n```\n\n\u003e ⚠️ **Important**: Never commit `.env` to version control. Use strong, unique secrets in production.\n\n### Database Setup\n\n```bash\n# Run migrations\npnpm prisma migrate dev\n\n# (Optional) Seed the database\npnpm prisma db seed\n\n# View database in Prisma Studio\npnpm prisma studio\n```\n\n---\n\n## Development\n\n```bash\n# Start development server with hot reload\npnpm dev\n\n# Build for production\npnpm build\n\n# Start production server\npnpm start\n\n# Type checking\npnpm check-types\n\n# Linting\npnpm lint\n```\n\n---\n\n## API Endpoints\n\n### Authentication (`/api/v1/auth`)\n\n| Method | Endpoint    | Description                  | Auth Required |\n| ------ | ----------- | ---------------------------- | ------------- |\n| `POST` | `/register` | Register a new user          | No            |\n| `POST` | `/login`    | Login and get tokens         | No            |\n| `GET`  | `/refresh`  | Refresh access token         | Cookie        |\n| `GET`  | `/logout`   | Logout and invalidate tokens | Yes           |\n\n### User Routes (`/api/v1`)\n\n| Method | Endpoint                  | Description              | Required Role          |\n| ------ | ------------------------- | ------------------------ | ---------------------- |\n| `GET`  | `/publicRoute`            | Public endpoint          | None                   |\n| `GET`  | `/authRoute`              | Authenticated users only | Any authenticated      |\n| `GET`  | `/adminRoute`             | Admin only               | `ADMIN`                |\n| `GET`  | `/moderatorRoute`         | Moderator only           | `MODERATOR`            |\n| `GET`  | `/moderatorAndAdminRoute` | Admin or Moderator       | `ADMIN` or `MODERATOR` |\n\n### Request/Response Examples\n\n**Register**\n\n```bash\nPOST /api/v1/auth/register\nContent-Type: application/json\n\n{\n  \"email\": \"user@example.com\",\n  \"password\": \"securePassword123\",\n  \"name\": \"John Doe\"\n}\n```\n\n**Login**\n\n```bash\nPOST /api/v1/auth/login\nContent-Type: application/json\n\n{\n  \"email\": \"user@example.com\",\n  \"password\": \"securePassword123\"\n}\n\n# Response\n{\n  \"user\": { \"id\": 1, \"name\": \"John Doe\", \"role\": \"USER\" },\n  \"accessToken\": \"eyJhbGciOiJIUzI1NiIs...\"\n}\n# + HttpOnly cookie: refreshToken\n```\n\n**Access Protected Route**\n\n```bash\nGET /api/v1/authRoute\nAuthorization: Bearer \u003caccessToken\u003e\n```\n\n---\n\n## Authentication Flow\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   Client    │     │   Server    │     │  Database   │\n└──────┬──────┘     └──────┬──────┘     └──────┬──────┘\n       │                   │                   │\n       │ 1. POST /login    │                   │\n       │──────────────────\u003e│                   │\n       │                   │ 2. Verify creds   │\n       │                   │──────────────────\u003e│\n       │                   │\u003c──────────────────│\n       │                   │                   │\n       │ 3. Access Token   │                   │\n       │   + Refresh Cookie│                   │\n       │\u003c──────────────────│                   │\n       │                   │                   │\n       │ 4. GET /protected │                   │\n       │   + Bearer Token  │                   │\n       │──────────────────\u003e│                   │\n       │                   │ 5. Verify JWT     │\n       │                   │                   │\n       │ 6. Response       │                   │\n       │\u003c──────────────────│                   │\n       │                   │                   │\n       │ 7. GET /refresh   │                   │\n       │   + Cookie        │                   │\n       │──────────────────\u003e│                   │\n       │                   │ 8. Validate \u0026     │\n       │                   │    Rotate Token   │\n       │                   │──────────────────\u003e│\n       │                   │\u003c──────────────────│\n       │ 9. New Tokens     │                   │\n       │\u003c──────────────────│                   │\n       │                   │                   │\n```\n\n### Token Specifications\n\n| Token         | Storage          | Expiry     | Purpose            |\n| ------------- | ---------------- | ---------- | ------------------ |\n| Access Token  | Client memory    | 15 minutes | API authentication |\n| Refresh Token | HTTP-only cookie | 7 days     | Token renewal      |\n\n---\n\n## Role-Based Authorization\n\n### Available Roles\n\n```typescript\nenum UserRole {\n  USER      // Default role for new registrations\n  ADMIN     // Full administrative access\n  MODERATOR // Limited administrative access\n}\n```\n\n### Protecting Routes\n\n```typescript\n// Single role\nfastify.get(\n  \"/admin\",\n  {\n    preHandler: [fastify.authenticate, fastify.authorize([UserRole.ADMIN])],\n  },\n  handler\n);\n\n// Multiple roles (OR logic)\nfastify.get(\n  \"/staff\",\n  {\n    preHandler: [\n      fastify.authenticate,\n      fastify.authorize([UserRole.ADMIN, UserRole.MODERATOR]),\n    ],\n  },\n  handler\n);\n\n// Any authenticated user\nfastify.get(\n  \"/profile\",\n  {\n    preHandler: [fastify.authenticate],\n  },\n  handler\n);\n```\n\n---\n\n## Testing\n\n### Test Structure\n\n```\n__tests__/\n├── helpers/                    # Shared test utilities\n│   ├── test-app.ts            # Builds test Fastify instance\n│   └── test-database.ts       # Database seeding \u0026 cleanup\n├── e2e/                       # End-to-End Tests\n│   ├── auth.e2e.test.ts       # Complete auth user journeys\n│   └── users.e2e.test.ts      # Multi-user scenarios\n└── integration/               # Integration Tests\n    ├── auth.integration.test.ts   # Auth API endpoints\n    └── users.integration.test.ts  # User route permissions\n\nsrc/services/\n└── auth.service.test.ts       # Unit tests (mocked dependencies)\n```\n\n### Test Types\n\n| Type            | Location                 | Purpose                          | Database       |\n| --------------- | ------------------------ | -------------------------------- | -------------- |\n| **Unit**        | `src/**/*.test.ts`       | Test business logic in isolation | Mocked         |\n| **Integration** | `__tests__/integration/` | Test API endpoints               | Real (test DB) |\n| **E2E**         | `__tests__/e2e/`         | Test complete user flows         | Real (test DB) |\n\n### Running Tests\n\n```bash\n# Run all tests\npnpm test\n\n# Run tests in watch mode\npnpm test -- --watch\n\n# Run specific test file\npnpm test -- auth.integration.test.ts\n\n# Run with coverage\npnpm test -- --coverage\n\n# Run only unit tests\npnpm test -- src/\n\n# Run only integration tests\npnpm test -- __tests__/integration/\n\n# Run only E2E tests\npnpm test -- __tests__/e2e/\n```\n\n### Writing Tests\n\n**Unit Test Example** (with mocks)\n\n```typescript\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport AuthService from \"./auth.service\";\n\nconst prismaMock = {\n  user: {\n    findUnique: vi.fn(),\n    create: vi.fn(),\n  },\n};\n\ndescribe(\"AuthService\", () =\u003e {\n  let authService: AuthService;\n\n  beforeEach(() =\u003e {\n    vi.resetAllMocks();\n    authService = new AuthService(fastifyMock as any);\n  });\n\n  it(\"should throw error for invalid credentials\", async () =\u003e {\n    prismaMock.user.findUnique.mockReturnValue(null);\n    await expect(\n      authService.loginUser(\"test@email.com\", \"pass\")\n    ).rejects.toThrow(\"Invalid credentials\");\n  });\n});\n```\n\n**Integration Test Example** (real API calls)\n\n```typescript\nimport { describe, it, expect, beforeAll, afterAll } from \"vitest\";\nimport { buildTestApp, generateTestEmail } from \"../helpers/test-app\";\n\ndescribe(\"Auth API\", () =\u003e {\n  let app: FastifyInstance;\n\n  beforeAll(async () =\u003e {\n    app = await buildTestApp();\n  });\n\n  afterAll(async () =\u003e {\n    await app.close();\n  });\n\n  it(\"should register a user\", async () =\u003e {\n    const response = await app.inject({\n      method: \"POST\",\n      url: \"/api/v1/auth/register\",\n      payload: {\n        email: generateTestEmail(),\n        password: \"password123\",\n        name: \"Test User\",\n      },\n    });\n\n    expect(response.statusCode).toBe(201);\n  });\n});\n```\n\n**E2E Test Example** (complete flows)\n\n```typescript\ndescribe(\"User Journey\", () =\u003e {\n  it(\"register → login → access protected → logout\", async () =\u003e {\n    // 1. Register\n    const register = await app.inject({\n      /* ... */\n    });\n    expect(register.statusCode).toBe(201);\n\n    // 2. Login\n    const login = await app.inject({\n      /* ... */\n    });\n    const { accessToken } = JSON.parse(login.payload);\n\n    // 3. Access protected route\n    const protected = await app.inject({\n      method: \"GET\",\n      url: \"/api/v1/authRoute\",\n      headers: { authorization: `Bearer ${accessToken}` },\n    });\n    expect(protected.statusCode).toBe(200);\n\n    // 4. Logout\n    const logout = await app.inject({\n      /* ... */\n    });\n    expect(logout.statusCode).toBe(200);\n  });\n});\n```\n\n---\n\n## Security Best Practices\n\nThis template implements several OWASP recommendations:\n\n| Practice                    | Implementation                                       |\n| --------------------------- | ---------------------------------------------------- |\n| **Secure Password Storage** | bcrypt with 10 salt rounds                           |\n| **JWT Security**            | Short-lived access tokens, HTTP-only refresh cookies |\n| **Token Rotation**          | Refresh tokens are rotated on each use               |\n| **CORS**                    | Strict origin allowlist with credentials             |\n| **HTTP Headers**            | Helmet with CSP, CORP, and other security headers    |\n| **Rate Limiting**           | 100 requests per minute per IP                       |\n| **Input Validation**        | TypeBox schema validation on all inputs              |\n| **Cookie Security**         | `HttpOnly`, `Secure`, `SameSite=Strict`              |\n\n### Production Checklist\n\n- [ ] Use strong, unique `JWT_SECRET` (32+ characters)\n- [ ] Set `NODE_ENV=production`\n- [ ] Configure proper `CORS_ALLOWED_ORIGINS`\n- [ ] Use HTTPS (required for `Secure` cookies)\n- [ ] Set up database connection pooling\n- [ ] Enable logging and monitoring\n- [ ] Review and adjust rate limits\n- [ ] Set up health checks\n\n---\n\n## Project Guidelines\n\n### Code Organization Rules\n\n1. **Controllers** - Thin layer, only HTTP concerns (request/response)\n2. **Services** - Business logic, testable without HTTP context\n3. **Plugins** - Fastify decorators and hooks\n4. **Routes** - Route definitions with middleware attachment\n5. **Validations** - TypeBox schemas, no logic\n\n### Adding New Features\n\n1. **New Route**\n\n   ```\n   1. Create validation schema in src/validations/\n   2. Create/update service in src/services/\n   3. Create/update controller in src/controllers/\n   4. Register route in src/routes/\n   5. Add tests (unit + integration)\n   ```\n\n2. **New Role**\n   ```\n   1. Add to UserRole enum in prisma/schema.prisma\n   2. Run pnpm prisma migrate dev\n   3. Update route preHandlers as needed\n   4. Add E2E tests for role access\n   ```\n\n### File Naming Conventions\n\n| Type              | Pattern                  | Example                    |\n| ----------------- | ------------------------ | -------------------------- |\n| Controllers       | `*.controller.ts`        | `auth.controller.ts`       |\n| Services          | `*.service.ts`           | `auth.service.ts`          |\n| Routes            | `*.ts` (in routes/)      | `auth.ts`                  |\n| Validations       | `*.ts` (in validations/) | `auth.ts`                  |\n| Unit Tests        | `*.test.ts` (co-located) | `auth.service.test.ts`     |\n| Integration Tests | `*.integration.test.ts`  | `auth.integration.test.ts` |\n| E2E Tests         | `*.e2e.test.ts`          | `auth.e2e.test.ts`         |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobeim%2Ffastify-auth-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobeim%2Ffastify-auth-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobeim%2Ffastify-auth-template/lists"}