{"id":47676334,"url":"https://github.com/luisfernando12/authenticator","last_synced_at":"2026-06-11T00:01:30.763Z","repository":{"id":341713827,"uuid":"959430450","full_name":"LuisFernando12/Authenticator","owner":"LuisFernando12","description":"NestJS authentication service with OAuth2 (Authorization Code + PKCE), JWT, Redis and PostgreSQL","archived":false,"fork":false,"pushed_at":"2026-04-24T20:49:13.000Z","size":535,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-24T22:35:47.703Z","etag":null,"topics":["authentication","backend","bcrypt","docker","jwt","nestjs","nodejs","oauth2","pkce","postgresql","redis","rest-api","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LuisFernando12.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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-04-02T19:14:37.000Z","updated_at":"2026-04-01T21:32:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/LuisFernando12/Authenticator","commit_stats":null,"previous_names":["luisfernando12/authenticator-back"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/LuisFernando12/Authenticator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuisFernando12%2FAuthenticator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuisFernando12%2FAuthenticator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuisFernando12%2FAuthenticator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuisFernando12%2FAuthenticator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LuisFernando12","download_url":"https://codeload.github.com/LuisFernando12/Authenticator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LuisFernando12%2FAuthenticator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32478162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: 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","backend","bcrypt","docker","jwt","nestjs","nodejs","oauth2","pkce","postgresql","redis","rest-api","typescript"],"created_at":"2026-04-02T13:32:09.323Z","updated_at":"2026-06-11T00:01:30.745Z","avatar_url":"https://github.com/LuisFernando12.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Authenticator Service\n\n\u003cp align=\"right\"\u003e\n  \u003ca href=\"./README.pt-BR.md\"\u003e🇧🇷 Português\u003c/a\u003e\n\u003c/p\u003e\n\n![CI](https://github.com/LuisFernando12/Authenticator-back/actions/workflows/workflow.yaml/badge.svg)\n![NestJS](https://img.shields.io/badge/NestJS-11-E0234E?logo=nestjs\u0026logoColor=white)\n![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript\u0026logoColor=white)\n![Node](https://img.shields.io/badge/Node.js-22-339933?logo=node.js\u0026logoColor=white)\n![pnpm](https://img.shields.io/badge/pnpm-10-F69220?logo=pnpm\u0026logoColor=white)\n![License](https://img.shields.io/badge/license-MIT-green)\n\nA NestJS-based authentication and authorization service implementing secure login, email verification, password reset, and a full **OAuth2** flow with Authorization Code and PKCE support.\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Tech Stack](#tech-stack)\n- [OAuth2 Flow](#oauth2-flow)\n- [Authentication Flow](#authentication-flow)\n- [Endpoints](#endpoints)\n- [Getting Started](#getting-started)\n- [Environment Variables](#environment-variables)\n- [Tests](#tests)\n- [Project Structure](#project-structure)\n- [CI/CD](#cicd)\n\n---\n\n## Features\n\n- **User Authentication** — Login, registration and email verification with JWT\n- **Password Management** — Reset and update via email code generated with `crypto.randomInt`\n- **OAuth2** — Full Authorization Code flow with PKCE (`S256`) support for public clients\n- **Client Management** — Register and manage OAuth2 applications (confidential and public clients)\n- **Email Notifications** — Dynamic templates with Nodemailer + Handlebars for account activation and password reset\n- **Rate Limiting** — Brute-force protection on sensitive endpoints via `@nestjs/throttler`\n- **Health Check** — PostgreSQL and Redis connectivity check via `@nestjs/terminus`\n- **Structured Logging** — Custom logger with per-method context, built on top of NestJS `ConsoleLogger`\n- **API Documentation** — Interactive Swagger at `/api/docs`\n\n---\n\n## Tech Stack\n\n| Layer            | Technology                   |\n| ---------------- | ---------------------------- |\n| Framework        | NestJS 11                    |\n| Language         | TypeScript 5.9               |\n| Database         | PostgreSQL (TypeORM)         |\n| Cache / Sessions | Redis (ioredis)              |\n| Authentication   | JWT (`@nestjs/jwt`) + Bcrypt |\n| Validation       | class-validator + Joi        |\n| Email            | Nodemailer + Handlebars      |\n| Documentation    | Swagger / OpenAPI            |\n| Testing          | Jest 30                      |\n| Package manager  | pnpm 10                      |\n\n---\n\n## OAuth2 Flow\n\n### Authorization Code + PKCE (public clients)\n\n```mermaid\nsequenceDiagram\n    participant App as Client Application\n    participant Auth as Authenticator Service\n    participant User as End User\n\n    App-\u003e\u003eAuth: GET /api/auth/oauth/authorize\u003cbr/\u003e(clientId, redirectUri, codeChallenge, state, scope)\n    Auth-\u003e\u003eAuth: Validates client \u0026 redirectUri\u003cbr/\u003eSaves authRequest in Redis (oauthRequestId)\n    Auth--\u003e\u003eApp: 302 redirect → login page\u003cbr/\u003e(?oauthRequestId=xxx\u0026clientId=...\u0026scope=...)\n\n    User-\u003e\u003eAuth: POST /api/auth/oauth/login\u003cbr/\u003e(email, password + query params)\n    Auth-\u003e\u003eAuth: Validates authRequest from Redis\u003cbr/\u003eVerifies user credentials\n    Auth-\u003e\u003eAuth: Generates authorization code (SHA-256)\u003cbr/\u003eSaves code + codeChallenge in Redis (5 min TTL)\n    Auth--\u003e\u003eApp: 302 redirect → redirectUri\u003cbr/\u003e(?code=xxx\u0026state=yyy)\n\n    App-\u003e\u003eAuth: POST /api/auth/oauth/token\u003cbr/\u003e(code, clientId, codeVerifier, redirectUri)\n    Auth-\u003e\u003eAuth: Validates code from Redis\u003cbr/\u003eVerifies PKCE (SHA-256 of codeVerifier)\n    Auth--\u003e\u003eApp: { access_token, token_type, scope, expiresAt }\n```\n\n### Authorization Code (confidential clients)\n\n```mermaid\nsequenceDiagram\n    participant App as Client Application\n    participant Auth as Authenticator Service\n    participant User as End User\n\n    App-\u003e\u003eAuth: GET /api/auth/oauth/authorize\u003cbr/\u003e(clientId, redirectUri, state, scope)\n    Auth--\u003e\u003eApp: 302 redirect → login page\n\n    User-\u003e\u003eAuth: POST /api/auth/oauth/login\u003cbr/\u003e(email, password + query params)\n    Auth-\u003e\u003eAuth: Verifies credentials\u003cbr/\u003eGenerates authorization code\n    Auth--\u003e\u003eApp: 302 redirect → redirectUri\u003cbr/\u003e(?code=xxx\u0026state=yyy)\n\n    App-\u003e\u003eAuth: POST /api/auth/oauth/token\u003cbr/\u003e(code, clientId, clientSecret, redirectUri)\n    Auth-\u003e\u003eAuth: Validates clientSecret\u003cbr/\u003eExchanges code for access_token\n    Auth--\u003e\u003eApp: { access_token, token_type, scope, expiresAt }\n```\n\n---\n\n## Authentication Flow\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Auth as Authenticator Service\n    participant DB as PostgreSQL\n    participant Redis\n    participant Email as SMTP\n\n    Note over Client,Email: Registration\n    Client-\u003e\u003eAuth: POST /api/auth/user (name, email, password)\n    Auth-\u003e\u003eDB: Creates user (bcrypt hash)\n    Auth-\u003e\u003eEmail: Sends activation email (JWT token)\n    Auth--\u003e\u003eClient: 201 { message }\n\n    Note over Client,Email: Account Activation\n    Client-\u003e\u003eAuth: GET /api/auth/verify-email?token=xxx\n    Auth-\u003e\u003eAuth: Verifies JWT (type: verify-email)\n    Auth-\u003e\u003eDB: Sets isVerified = true\n    Auth--\u003e\u003eClient: 200 { message }\n\n    Note over Client,Email: Login\n    Client-\u003e\u003eAuth: POST /api/auth/login (email, password)\n    Auth-\u003e\u003eDB: Finds user by email\n    Auth-\u003e\u003eAuth: bcrypt.compare(password, hash)\n    Auth-\u003e\u003eDB: Saves / updates JWT token\n    Auth--\u003e\u003eClient: 200 { token, redirect_uri }\n\n    Note over Client,Email: Password Reset\n    Client-\u003e\u003eAuth: POST /api/auth/reset-password (email)\n    Auth-\u003e\u003eRedis: Saves code (crypto.randomInt, TTL 10 min)\n    Auth-\u003e\u003eEmail: Sends recovery code\n    Auth--\u003e\u003eClient: 200 { message }\n\n    Client-\u003e\u003eAuth: POST /api/auth/new-password (email, code, password)\n    Auth-\u003e\u003eRedis: Validates and deletes code (getdel)\n    Auth-\u003e\u003eDB: Updates password (new bcrypt hash)\n    Auth--\u003e\u003eClient: 200 { message }\n```\n\n---\n\n## Endpoints\n\nAll endpoints are prefixed with `/api/auth`.\n\n### Authentication — `/api/auth`\n\n| Method | Route                     | Description                       | Rate Limit |\n| ------ | ------------------------- | --------------------------------- | ---------- |\n| `POST` | `/login`                  | User login                        | 5 req/min  |\n| `GET`  | `/verify-email?token=`    | Activates account via JWT token   | 5 req/min  |\n| `POST` | `/reset-password`         | Requests password reset by email  | 5 req/min  |\n| `POST` | `/new-password`           | Sets new password with Redis code | 5 req/min  |\n| `POST` | `/new-token/email-active` | Resends activation email          | 5 req/min  |\n\n### User — `/api/auth/user`\n\n| Method | Route | Description          |\n| ------ | ----- | -------------------- |\n| `POST` | `/`   | Registers a new user |\n\n### OAuth2 — `/api/auth/oauth`\n\n| Method | Route               | Description                                                                                                 | Rate Limit |\n| ------ | ------------------- | ----------------------------------------------------------------------------------------------------------- | ---------- |\n| `GET`  | `/authorize`        | Starts OAuth2 flow, returns redirect with `oauthRequestId`                                                  | —          |\n| `POST` | `/login`            | OAuth2 login — validates authRequest and generates `code`                                                   | 5 req/min  |\n| `POST` | `/token`            | Exchanges `code` for `access_token` (PKCE and clientSecret supported)                                       | 5 req/min  |\n| `POST` | `/refresh-token`    | Refreshes `access_token` and `refresh_token` passing a valid `refreshToken` on payload                      | 5 req/min  |\n| `POST` | `/revoke-token`     | Revokes `access_token` and `refresh_token` passing `refreshToken` or `accessToken` like token on payload    | 5 req/min  |\n| `POST` | `/token-introspect` | Introspects `access_token` or `refresh_token` passing `refreshToken` or `accessToken` like token on payload | 5 req/min  |\n\n### Client — `/api/auth/client`\n\n| Method | Route | Description                        |\n| ------ | ----- | ---------------------------------- |\n| `POST` | `/`   | Registers a new OAuth2 application |\n\n### Health — `/api/auth/health`\n\n| Method | Route | Description                              |\n| ------ | ----- | ---------------------------------------- |\n| `GET`  | `/`   | Checks PostgreSQL and Redis connectivity |\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js 22+\n- Docker and Docker Compose\n- pnpm 10+\n\n### Installation\n\n```bash\n# 1. Clone the repository\ngit clone https://github.com/LuisFernando12/Authenticator-back.git\ncd Authenticator-back\n\n# 2. Install dependencies\npnpm install\n\n# 3. Set up environment variables\ncp .env.template .env\n# edit .env with your values\n```\n\n### Running\n\n**Development mode** (starts Postgres + Redis via Docker, app in watch mode):\n\n```bash\npnpm start:dev\n```\n\n**Infrastructure only** (Postgres + Redis):\n\n```bash\npnpm start:docker\n```\n\n**Production mode:**\n\n```bash\npnpm build\npnpm start:prod\n```\n\n**Full environment with Docker Compose:**\n\n```bash\ndocker compose up\n```\n\nAPI available at `http://localhost:3000`.\nSwagger docs: `http://localhost:3000/api/docs`.\n\n---\n\n## Environment Variables\n\nCopy `.env.template` to `.env` and fill in your values:\n\n```env\n# PostgreSQL\nDB_USER=\nDB_PASSWORD=\nDB_NAME=\nDB_PORT=5432\n\n# Service URLs\nSERVICE_VERIFY_EMAIL_URL=   # Base URL for email verification links\nSERVICE_URL=                # Public URL of this service\nSERVICE_RESET_PASSWORD_URL= # URL of the password reset page\nREDIRECT_URI=               # Default redirect URI after login\n\n# CORS\nCORS_ORIGIN=                # Allowed origin (e.g. http://localhost:4000)\n\n# OAuth2\nOAUTH_LOGIN_URL=            # URL of the frontend OAuth2 login page\n\n# Redis\nREDIS_URI=                  # Full URI (e.g. redis://:password@localhost:6379)\n\n# SMTP\nSMTP_PORT=\nSERVER_SMTP=                # SMTP server hostname (e.g. smtp.example.com)\nSERVER_SMTP_USER_NAME=\nSERVER_SMTP_PASSWORD=\n\n# JWT\nSECRET=                     # Secret key for signing JWT tokens\n```\n\n---\n\n## Tests\n\n```bash\n# Unit tests\npnpm test\n\n# With coverage\npnpm test:cov\n\n# Watch mode\npnpm test:watch\n\n# E2E\npnpm test:e2e\n```\n\nUnit tests cover the main use cases with isolated fakes. The adopted pattern is `expect(fn()).rejects.toThrow()` for error scenarios, ensuring every case is actually exercised.\n\n---\n\n## Project Structure\n\n```\nsrc/\n├── auth/                           # Authentication domain\n│   ├── application/\n│   │   ├── port/                   # Application contracts\n│   │   └── use-case/               # Login, activation and password flows\n│   ├── domain/\n│   │   ├── entity/\n│   │   ├── enum/\n│   │   ├── error/\n│   │   └── value-object/\n│   └── infrastructure/\n│       ├── adapter/\n│       ├── controller/\n│       ├── dto/\n│       └── module/\n├── client/                         # OAuth2 client management\n│   ├── application/\n│   ├── domain/\n│   └── infrastructure/\n│       ├── adapter/\n│       ├── controller/\n│       ├── dto/\n│       ├── module/\n│       ├── persistence/\n│       └── repository/\n├── config/\n│   ├── database/                   # TypeORM data source and migrations\n│   ├── filters/                    # Global exception filters\n│   ├── helper/\n│   ├── logger/\n│   └── templates/                  # Handlebars email templates\n├── consent/                        # OAuth2 consent domain\n│   ├── application/\n│   │   ├── port/\n│   │   ├── service/\n│   │   └── use-case/\n│   ├── domain/\n│   └── infrastructure/\n├── core/                           # Shared app services and modules\n│   ├── application/\n│   ├── domain/\n│   └── infrastructure/\n├── module/\n│   └── app.module.ts               # Root NestJS module\n├── oauth/                          # OAuth2 authorization server flow\n│   ├── application/\n│   │   ├── port/\n│   │   └── use-case/\n│   ├── domain/\n│   │   ├── entity/\n│   │   ├── error/\n│   │   └── value-object/\n│   └── infrastructure/\n│       ├── adapter/\n│       ├── controller/\n│       ├── dto/\n│       └── module/\n├── token/                          # Token generation, refresh, revoke and introspection\n│   ├── application/\n│   │   ├── interface/\n│   │   ├── port/\n│   │   ├── service/\n│   │   ├── type/\n│   │   └── use-case/\n│   ├── domain/\n│   └── infrastructure/\n│       ├── adapter/\n│       ├── module/\n│       ├── persistence/\n│       └── repository/\n└── user/                           # User registration and lookup\n    ├── application/\n    │   ├── port/\n    │   └── use-case/\n    ├── domain/\n    └── infrastructure/\n        ├── adapter/\n        ├── controller/\n        ├── dto/\n        ├── module/\n        ├── persistence/\n        └── repository/\n\ntest/\n└── unit/\n    └── use-case/\n        ├── auth/\n        ├── consent/\n        ├── oauth/\n        ├── token/\n        └── user/\n```\n\n---\n\n## CI/CD\n\nThe CI pipeline runs automatically on every push and pull request to `main`:\n\n1. **Checkout** the code\n2. **Setup pnpm 10** + dependency cache\n3. **Setup Node.js 22**\n4. **Install** — `pnpm install --frozen-lockfile`\n5. **Build** — `pnpm build`\n6. **Test** — `pnpm test`\n7. **Coverage** — `pnpm test:cov`\n\n---\n\n## License\n\nThis project is licensed under the [MIT License](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisfernando12%2Fauthenticator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluisfernando12%2Fauthenticator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisfernando12%2Fauthenticator/lists"}