https://github.com/luisfernando12/authenticator
NestJS authentication service with OAuth2 (Authorization Code + PKCE), JWT, Redis and PostgreSQL
https://github.com/luisfernando12/authenticator
authentication backend bcrypt docker jwt nestjs nodejs oauth2 pkce postgresql redis rest-api typescript
Last synced: 18 days ago
JSON representation
NestJS authentication service with OAuth2 (Authorization Code + PKCE), JWT, Redis and PostgreSQL
- Host: GitHub
- URL: https://github.com/luisfernando12/authenticator
- Owner: LuisFernando12
- License: mit
- Created: 2025-04-02T19:14:37.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-04-24T20:49:13.000Z (2 months ago)
- Last Synced: 2026-04-24T22:35:47.703Z (2 months ago)
- Topics: authentication, backend, bcrypt, docker, jwt, nestjs, nodejs, oauth2, pkce, postgresql, redis, rest-api, typescript
- Language: TypeScript
- Homepage:
- Size: 522 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Authenticator Service






A NestJS-based authentication and authorization service implementing secure login, email verification, password reset, and a full **OAuth2** flow with Authorization Code and PKCE support.
---
## Table of Contents
- [Features](#features)
- [Tech Stack](#tech-stack)
- [OAuth2 Flow](#oauth2-flow)
- [Authentication Flow](#authentication-flow)
- [Endpoints](#endpoints)
- [Getting Started](#getting-started)
- [Environment Variables](#environment-variables)
- [Tests](#tests)
- [Project Structure](#project-structure)
- [CI/CD](#cicd)
---
## Features
- **User Authentication** β Login, registration and email verification with JWT
- **Password Management** β Reset and update via email code generated with `crypto.randomInt`
- **OAuth2** β Full Authorization Code flow with PKCE (`S256`) support for public clients
- **Client Management** β Register and manage OAuth2 applications (confidential and public clients)
- **Email Notifications** β Dynamic templates with Nodemailer + Handlebars for account activation and password reset
- **Rate Limiting** β Brute-force protection on sensitive endpoints via `@nestjs/throttler`
- **Health Check** β PostgreSQL and Redis connectivity check via `@nestjs/terminus`
- **Structured Logging** β Custom logger with per-method context, built on top of NestJS `ConsoleLogger`
- **API Documentation** β Interactive Swagger at `/api/docs`
---
## Tech Stack
| Layer | Technology |
| ---------------- | ---------------------------- |
| Framework | NestJS 11 |
| Language | TypeScript 5.9 |
| Database | PostgreSQL (TypeORM) |
| Cache / Sessions | Redis (ioredis) |
| Authentication | JWT (`@nestjs/jwt`) + Bcrypt |
| Validation | class-validator + Joi |
| Email | Nodemailer + Handlebars |
| Documentation | Swagger / OpenAPI |
| Testing | Jest 30 |
| Package manager | pnpm 10 |
---
## OAuth2 Flow
### Authorization Code + PKCE (public clients)
```mermaid
sequenceDiagram
participant App as Client Application
participant Auth as Authenticator Service
participant User as End User
App->>Auth: GET /api/auth/oauth/authorize
(clientId, redirectUri, codeChallenge, state, scope)
Auth->>Auth: Validates client & redirectUri
Saves authRequest in Redis (oauthRequestId)
Auth-->>App: 302 redirect β login page
(?oauthRequestId=xxx&clientId=...&scope=...)
User->>Auth: POST /api/auth/oauth/login
(email, password + query params)
Auth->>Auth: Validates authRequest from Redis
Verifies user credentials
Auth->>Auth: Generates authorization code (SHA-256)
Saves code + codeChallenge in Redis (5 min TTL)
Auth-->>App: 302 redirect β redirectUri
(?code=xxx&state=yyy)
App->>Auth: POST /api/auth/oauth/token
(code, clientId, codeVerifier, redirectUri)
Auth->>Auth: Validates code from Redis
Verifies PKCE (SHA-256 of codeVerifier)
Auth-->>App: { access_token, token_type, scope, expiresAt }
```
### Authorization Code (confidential clients)
```mermaid
sequenceDiagram
participant App as Client Application
participant Auth as Authenticator Service
participant User as End User
App->>Auth: GET /api/auth/oauth/authorize
(clientId, redirectUri, state, scope)
Auth-->>App: 302 redirect β login page
User->>Auth: POST /api/auth/oauth/login
(email, password + query params)
Auth->>Auth: Verifies credentials
Generates authorization code
Auth-->>App: 302 redirect β redirectUri
(?code=xxx&state=yyy)
App->>Auth: POST /api/auth/oauth/token
(code, clientId, clientSecret, redirectUri)
Auth->>Auth: Validates clientSecret
Exchanges code for access_token
Auth-->>App: { access_token, token_type, scope, expiresAt }
```
---
## Authentication Flow
```mermaid
sequenceDiagram
participant Client
participant Auth as Authenticator Service
participant DB as PostgreSQL
participant Redis
participant Email as SMTP
Note over Client,Email: Registration
Client->>Auth: POST /api/auth/user (name, email, password)
Auth->>DB: Creates user (bcrypt hash)
Auth->>Email: Sends activation email (JWT token)
Auth-->>Client: 201 { message }
Note over Client,Email: Account Activation
Client->>Auth: GET /api/auth/verify-email?token=xxx
Auth->>Auth: Verifies JWT (type: verify-email)
Auth->>DB: Sets isVerified = true
Auth-->>Client: 200 { message }
Note over Client,Email: Login
Client->>Auth: POST /api/auth/login (email, password)
Auth->>DB: Finds user by email
Auth->>Auth: bcrypt.compare(password, hash)
Auth->>DB: Saves / updates JWT token
Auth-->>Client: 200 { token, redirect_uri }
Note over Client,Email: Password Reset
Client->>Auth: POST /api/auth/reset-password (email)
Auth->>Redis: Saves code (crypto.randomInt, TTL 10 min)
Auth->>Email: Sends recovery code
Auth-->>Client: 200 { message }
Client->>Auth: POST /api/auth/new-password (email, code, password)
Auth->>Redis: Validates and deletes code (getdel)
Auth->>DB: Updates password (new bcrypt hash)
Auth-->>Client: 200 { message }
```
---
## Endpoints
All endpoints are prefixed with `/api/auth`.
### Authentication β `/api/auth`
| Method | Route | Description | Rate Limit |
| ------ | ------------------------- | --------------------------------- | ---------- |
| `POST` | `/login` | User login | 5 req/min |
| `GET` | `/verify-email?token=` | Activates account via JWT token | 5 req/min |
| `POST` | `/reset-password` | Requests password reset by email | 5 req/min |
| `POST` | `/new-password` | Sets new password with Redis code | 5 req/min |
| `POST` | `/new-token/email-active` | Resends activation email | 5 req/min |
### User β `/api/auth/user`
| Method | Route | Description |
| ------ | ----- | -------------------- |
| `POST` | `/` | Registers a new user |
### OAuth2 β `/api/auth/oauth`
| Method | Route | Description | Rate Limit |
| ------ | ------------------- | ----------------------------------------------------------------------------------------------------------- | ---------- |
| `GET` | `/authorize` | Starts OAuth2 flow, returns redirect with `oauthRequestId` | β |
| `POST` | `/login` | OAuth2 login β validates authRequest and generates `code` | 5 req/min |
| `POST` | `/token` | Exchanges `code` for `access_token` (PKCE and clientSecret supported) | 5 req/min |
| `POST` | `/refresh-token` | Refreshes `access_token` and `refresh_token` passing a valid `refreshToken` on payload | 5 req/min |
| `POST` | `/revoke-token` | Revokes `access_token` and `refresh_token` passing `refreshToken` or `accessToken` like token on payload | 5 req/min |
| `POST` | `/token-introspect` | Introspects `access_token` or `refresh_token` passing `refreshToken` or `accessToken` like token on payload | 5 req/min |
### Client β `/api/auth/client`
| Method | Route | Description |
| ------ | ----- | ---------------------------------- |
| `POST` | `/` | Registers a new OAuth2 application |
### Health β `/api/auth/health`
| Method | Route | Description |
| ------ | ----- | ---------------------------------------- |
| `GET` | `/` | Checks PostgreSQL and Redis connectivity |
---
## Getting Started
### Prerequisites
- Node.js 22+
- Docker and Docker Compose
- pnpm 10+
### Installation
```bash
# 1. Clone the repository
git clone https://github.com/LuisFernando12/Authenticator-back.git
cd Authenticator-back
# 2. Install dependencies
pnpm install
# 3. Set up environment variables
cp .env.template .env
# edit .env with your values
```
### Running
**Development mode** (starts Postgres + Redis via Docker, app in watch mode):
```bash
pnpm start:dev
```
**Infrastructure only** (Postgres + Redis):
```bash
pnpm start:docker
```
**Production mode:**
```bash
pnpm build
pnpm start:prod
```
**Full environment with Docker Compose:**
```bash
docker compose up
```
API available at `http://localhost:3000`.
Swagger docs: `http://localhost:3000/api/docs`.
---
## Environment Variables
Copy `.env.template` to `.env` and fill in your values:
```env
# PostgreSQL
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_PORT=5432
# Service URLs
SERVICE_VERIFY_EMAIL_URL= # Base URL for email verification links
SERVICE_URL= # Public URL of this service
SERVICE_RESET_PASSWORD_URL= # URL of the password reset page
REDIRECT_URI= # Default redirect URI after login
# CORS
CORS_ORIGIN= # Allowed origin (e.g. http://localhost:4000)
# OAuth2
OAUTH_LOGIN_URL= # URL of the frontend OAuth2 login page
# Redis
REDIS_URI= # Full URI (e.g. redis://:password@localhost:6379)
# SMTP
SMTP_PORT=
SERVER_SMTP= # SMTP server hostname (e.g. smtp.example.com)
SERVER_SMTP_USER_NAME=
SERVER_SMTP_PASSWORD=
# JWT
SECRET= # Secret key for signing JWT tokens
```
---
## Tests
```bash
# Unit tests
pnpm test
# With coverage
pnpm test:cov
# Watch mode
pnpm test:watch
# E2E
pnpm test:e2e
```
Unit 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.
---
## Project Structure
```
src/
βββ auth/ # Authentication domain
β βββ application/
β β βββ port/ # Application contracts
β β βββ use-case/ # Login, activation and password flows
β βββ domain/
β β βββ entity/
β β βββ enum/
β β βββ error/
β β βββ value-object/
β βββ infrastructure/
β βββ adapter/
β βββ controller/
β βββ dto/
β βββ module/
βββ client/ # OAuth2 client management
β βββ application/
β βββ domain/
β βββ infrastructure/
β βββ adapter/
β βββ controller/
β βββ dto/
β βββ module/
β βββ persistence/
β βββ repository/
βββ config/
β βββ database/ # TypeORM data source and migrations
β βββ filters/ # Global exception filters
β βββ helper/
β βββ logger/
β βββ templates/ # Handlebars email templates
βββ consent/ # OAuth2 consent domain
β βββ application/
β β βββ port/
β β βββ service/
β β βββ use-case/
β βββ domain/
β βββ infrastructure/
βββ core/ # Shared app services and modules
β βββ application/
β βββ domain/
β βββ infrastructure/
βββ module/
β βββ app.module.ts # Root NestJS module
βββ oauth/ # OAuth2 authorization server flow
β βββ application/
β β βββ port/
β β βββ use-case/
β βββ domain/
β β βββ entity/
β β βββ error/
β β βββ value-object/
β βββ infrastructure/
β βββ adapter/
β βββ controller/
β βββ dto/
β βββ module/
βββ token/ # Token generation, refresh, revoke and introspection
β βββ application/
β β βββ interface/
β β βββ port/
β β βββ service/
β β βββ type/
β β βββ use-case/
β βββ domain/
β βββ infrastructure/
β βββ adapter/
β βββ module/
β βββ persistence/
β βββ repository/
βββ user/ # User registration and lookup
βββ application/
β βββ port/
β βββ use-case/
βββ domain/
βββ infrastructure/
βββ adapter/
βββ controller/
βββ dto/
βββ module/
βββ persistence/
βββ repository/
test/
βββ unit/
βββ use-case/
βββ auth/
βββ consent/
βββ oauth/
βββ token/
βββ user/
```
---
## CI/CD
The CI pipeline runs automatically on every push and pull request to `main`:
1. **Checkout** the code
2. **Setup pnpm 10** + dependency cache
3. **Setup Node.js 22**
4. **Install** β `pnpm install --frozen-lockfile`
5. **Build** β `pnpm build`
6. **Test** β `pnpm test`
7. **Coverage** β `pnpm test:cov`
---
## License
This project is licensed under the [MIT License](./LICENSE).