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('test@example.com', '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
Build Production-Ready GraphQL APIs: Complete NestJS, Prisma, and Apollo Federation Guide

Learn to build scalable GraphQL APIs with NestJS, Prisma & Apollo Federation. Complete guide covering authentication, caching & deployment. Start building now!

Blog Image
Build a Real-time Collaborative Document Editor with Socket.io, Operational Transform, and Redis Complete Guide

Learn to build a real-time collaborative document editor using Socket.io, Operational Transform & Redis. Master conflict resolution, scaling & deployment.

Blog Image
Build a Complete Rate-Limited API Gateway: Express, Redis, JWT Authentication Implementation Guide

Learn to build scalable rate-limited API gateways with Express, Redis & JWT. Master multiple rate limiting algorithms, distributed systems & production deployment.

Blog Image
Next.js with Prisma ORM: Complete Guide to Building Type-Safe Full-Stack Applications

Learn how to integrate Next.js with Prisma ORM for type-safe database operations. Build full-stack apps faster with this powerful combination.

Blog Image
Build High-Performance GraphQL APIs with NestJS, Prisma, and Redis: Complete 2024 Guide

Master NestJS GraphQL APIs with Prisma & Redis: Build high-performance APIs, implement caching strategies, prevent N+1 queries, and deploy production-ready applications.

Blog Image
Complete Guide: Building Type-Safe Full-Stack Apps with Next.js and Prisma Integration

Learn how to integrate Next.js with Prisma ORM for type-safe, full-stack web applications. Master database operations, migrations, and TypeScript integration.