A production-oriented authentication and authorization API built with NestJS, TypeScript, PostgreSQL, Prisma, JWT, Auth0, and OpenAPI. It keeps the same REST contract and database model as the sibling Express implementation while using Nest modules, providers, guards, pipes, filters, and dependency injection.
- Local registration and login with bcrypt and signed JWTs
- Auth0 RS256 access-token validation through remote JWKS
- Database-backed permissions evaluated on every protected request
- Direct and group-derived role assignments
- Immutable built-in
USERandADMINinvariants - Serializable final-administrator protection
- Transactional access-control audit logs
- Cursor pagination and DTO validation
- English, French, and Spanish problem details
- Global and authentication-specific rate limits
- Liveness and PostgreSQL readiness probes
- Swagger UI, ReDoc, and raw OpenAPI JSON
Nest owns the application object graph. Controllers inject services, services inject repository abstractions, repositories inject PrismaService, and guards inject authentication services.
TypeScript interfaces do not exist at runtime, so repository and password abstractions use Symbol tokens:
@Injectable()
export class AuthService {
constructor(
@Inject(USER_REPOSITORY) private readonly users: UserRepository,
@Inject(PASSWORD_HASHER) private readonly passwords: PasswordHasher,
private readonly jwt: JwtService,
) {}
}The module selects the production implementations:
providers: [
{ provide: USER_REPOSITORY, useClass: PrismaUserRepository },
{ provide: PASSWORD_HASHER, useClass: BcryptPasswordHasher },
];Tests replace these providers with fakes through Test.createTestingModule().
Requires Node.js 24 and PostgreSQL 14 or newer. Node 20 and 22 releases covered by the engines range are also supported.
nvm install
nvm use
cp .env.example .env
npm ci
npm run db:deploy
npm run db:seed
npm run start:devSet a strong JWT_SECRET and review all values in .env first. The seed uses ADMIN_SEED_NAME, ADMIN_SEED_EMAIL, and ADMIN_SEED_PASSWORD to create or update the initial administrator.
To run PostgreSQL with Docker instead, start the included service:
docker compose up -d postgresThe Compose service publishes PostgreSQL on host port 5433, so set DATABASE_URL=postgresql://postgres:postgres@localhost:5433/nestjs_auth_api when using it from the host.
The API defaults to http://localhost:3001. ReDoc is available at /redoc, Swagger UI at /docs, and OpenAPI JSON at /openapi.json when DOCS_ENABLED=true.
Permissions are stable, code-managed capabilities. Roles bundle permissions, groups receive roles, and users receive roles directly or through group membership. New local accounts always receive USER. Effective permissions are read from the database for authorization decisions, so revocation does not wait for a JWT to expire.
The built-in ADMIN role retains the full permission catalog, the built-in USER role cannot be removed from an account, and the final effective administrator cannot be removed. Access-control mutations and their audit records share one transaction.
| Method | Route | Requirement |
|---|---|---|
GET |
/health/live |
Public |
GET |
/health/ready |
Public |
GET |
/api/v1/locales |
Public |
POST |
/api/v1/auth/register |
Public |
POST |
/api/v1/auth/login |
Public |
GET |
/api/v1/auth/me |
Local JWT |
GET |
/api/v1/auth0/me |
Auth0 token |
GET |
/api/v1/users |
users:read |
PUT/DELETE |
/api/v1/users/:userId/roles/:roleId |
users:roles:manage; admins:manage when changing ADMIN |
GET |
/api/v1/permissions |
roles:read |
GET/POST |
/api/v1/roles |
roles:read / roles:manage |
GET |
/api/v1/roles/:roleId |
roles:read |
PUT/DELETE |
/api/v1/roles/:roleId/permissions/:permissionId |
roles:manage |
GET/POST |
/api/v1/groups |
groups:read / groups:manage |
GET |
/api/v1/groups/:groupId |
groups:read |
PUT/DELETE |
/api/v1/groups/:groupId/roles/:roleId |
groups:manage; admins:manage when changing ADMIN |
PUT/DELETE |
/api/v1/groups/:groupId/users/:userId |
groups:manage; admins:manage for an ADMIN group |
GET |
/api/v1/audit-logs |
audit:read |
Collections accept ?limit=25&cursor=UUID. Audit logs additionally accept actor, action, target, and date-range filters.
Errors use application/problem+json and include a stable code and request trace ID. Authentication responses are marked no-store.
curl -X POST http://localhost:3001/api/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"name":"Ada Lovelace","email":"ada@example.com","password":"correct-horse-42"}'Use the returned access token as Authorization: Bearer YOUR_ACCESS_TOKEN for local-JWT routes.
src/
access-control/ RBAC controllers, services, DTOs, and repository
auth/ Local JWT authentication and permission guard
auth0/ Auth0 access-token integration
common/ Request context, localization, errors, and problem filter
config/ Environment validation
database/ Prisma service and administrator seed
docs/ ReDoc integration
health/ Liveness and readiness endpoints
locales/ Supported-locale endpoint
prisma/ Schema and migrations
test/ Unit and controller tests
npm run typecheck
npm run lint
npm test
npm run build
npx prisma validateDo not commit .env, generated Prisma output, dist, coverage, or dependencies. Commit schema changes together with their migration and updated lockfile.