js

Complete NestJS Authentication Guide: JWT, Prisma, and Advanced Security Patterns

Build complete NestJS authentication with JWT, Prisma & PostgreSQL. Learn refresh tokens, RBAC, email verification, security patterns & testing for production-ready apps.

Complete NestJS Authentication Guide: JWT, Prisma, and Advanced Security Patterns

I’ve been thinking a lot about authentication systems lately—how they form the foundation of nearly every modern application, yet so many implementations cut corners on security. That’s why I want to share a comprehensive approach to building a robust authentication system using NestJS, Prisma, and JWT.

Getting started requires setting up our foundation properly. Let’s begin by creating our project structure and installing the necessary dependencies. This ensures we have everything needed for a production-ready authentication system.

npm i -g @nestjs/cli
nest new auth-system
cd auth-system
npm install @nestjs/jwt @nestjs/passport passport-jwt bcryptjs
npm install prisma @prisma/client
npx prisma init

Have you ever wondered what makes some authentication systems more secure than others? It often comes down to proper token management and database design. Let’s set up our Prisma schema to handle users, refresh tokens, and security features properly.

// schema.prisma
model User {
  id        String   @id @default(cuid())
  email     String   @unique
  password  String
  role      String   @default("USER")
  isVerified Boolean @default(false)
  createdAt DateTime @default(now())
  refreshTokens RefreshToken[]
}

model RefreshToken {
  id     String @id @default(cuid())
  token  String @unique
  userId String
  user   User   @relation(fields: [userId], references: [id])
  expiresAt DateTime
}

Now let’s implement the core authentication service. This is where we handle password hashing, token generation, and validation. Notice how we use bcrypt for password security and implement proper error handling.

// auth.service.ts
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private prisma: PrismaService,
    private jwtService: JwtService,
  ) {}

  async validateUser(email: string, password: string) {
    const user = await this.prisma.user.findUnique({ where: { email } });
    if (user && await bcrypt.compare(password, user.password)) {
      const { password: _, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = { email: user.email, sub: user.id };
    return {
      access_token: this.jwtService.sign(payload),
      refresh_token: await this.generateRefreshToken(user.id),
    };
  }
}

What about handling refresh tokens securely? This is crucial for maintaining user sessions without compromising security. Let’s implement a proper refresh token mechanism that includes expiration and validation.

// refresh-token.service.ts
@Injectable()
export class RefreshTokenService {
  async generateRefreshToken(userId: string): Promise<string> {
    const token = crypto.randomBytes(40).toString('hex');
    const expiresAt = new Date();
    expiresAt.setDate(expiresAt.getDate() + 7); // 7 days expiration
    
    await this.prisma.refreshToken.create({
      data: {
        token,
        userId,
        expiresAt,
      },
    });
    
    return token;
  }

  async validateRefreshToken(token: string): Promise<boolean> {
    const storedToken = await this.prisma.refreshToken.findUnique({
      where: { token },
      include: { user: true },
    });
    
    return storedToken && storedToken.expiresAt > new Date();
  }
}

Role-based access control is another critical component. How do we ensure users only access what they’re supposed to? Let’s create a custom decorator and guard for handling different user roles.

// roles.decorator.ts
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

// roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<string[]>(
      'roles',
      context.getHandler(),
    );
    if (!requiredRoles) return true;

    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.includes(user.role);
  }
}

Security headers and rate limiting are often overlooked but essential for production systems. Let’s implement these features to protect against common attacks and abuse.

// main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  app.use(helmet());
  app.use(
    rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100, // limit each IP to 100 requests per windowMs
    }),
  );
  
  await app.listen(3000);
}

Testing is non-negotiable for authentication systems. How can we ensure our implementation works correctly under various scenarios? Here’s a basic test structure for our auth service.

// auth.service.spec.ts
describe('AuthService', () => {
  let service: AuthService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: JwtService, useValue: mockJwtService },
      ],
    }).compile();

    service = module.get<AuthService>(AuthService);
  });

  it('should validate user credentials', async () => {
    const result = await service.validateUser('[email protected]', 'password');
    expect(result).toHaveProperty('id');
  });
});

Deployment requires careful configuration of environment variables and security settings. Remember to use different secrets for development and production, and never commit sensitive data to version control.

# .env.production
JWT_SECRET=your-production-jwt-secret
DATABASE_URL=your-production-database-url
JWT_EXPIRES_IN=15m
REFRESH_TOKEN_EXPIRES_IN=7d

Building a complete authentication system involves many moving parts, but each component plays a vital role in overall security. From proper password hashing to secure token management and role-based access control, every detail matters.

What security measures have you found most effective in your projects? I’d love to hear about your experiences and recommendations. If you found this guide helpful, please share it with others who might benefit from it, and feel free to leave your thoughts in the comments below.

Keywords: NestJS authentication tutorial, JWT authentication NestJS, Prisma ORM integration, Node.js authentication system, NestJS Prisma JWT, role-based access control RBAC, NestJS security best practices, TypeScript authentication API, NestJS refresh tokens, PostgreSQL authentication tutorial



Similar Posts
Blog Image
How to Integrate Vite with Tailwind CSS: Complete Setup Guide for Lightning-Fast Frontend Development

Learn how to integrate Vite with Tailwind CSS for lightning-fast frontend development. Boost build speeds, reduce CSS bundles, and streamline your workflow today.

Blog Image
Building Production-Ready Event-Driven Microservices with NestJS: Complete RabbitMQ and Prisma Integration Guide

Learn to build production-ready event-driven microservices using NestJS, RabbitMQ, and Prisma. Complete guide with code examples, deployment, and best practices.

Blog Image
Build Type-Safe GraphQL APIs: Complete TypeGraphQL, Prisma & PostgreSQL Guide for Modern Developers

Learn to build type-safe GraphQL APIs with TypeGraphQL, Prisma & PostgreSQL. Step-by-step guide covering setup, schemas, resolvers, testing & deployment.

Blog Image
Build Complete Multi-Tenant SaaS API with NestJS Prisma PostgreSQL Row-Level Security Tutorial

Learn to build a secure multi-tenant SaaS API using NestJS, Prisma & PostgreSQL Row-Level Security. Complete guide with tenant isolation, authentication & performance optimization.

Blog Image
Build High-Performance File Upload System: Multer, Sharp, AWS S3 in Node.js

Build a high-performance Node.js file upload system with Multer, Sharp & AWS S3. Learn secure uploads, image processing, and scalable storage solutions.

Blog Image
Build Production-Ready GraphQL API with NestJS, Prisma, PostgreSQL: Authentication, Real-time Subscriptions & Deployment Guide

Learn to build a production-ready GraphQL API with NestJS, Prisma, and PostgreSQL. Includes JWT authentication, real-time subscriptions, and deployment guide.