Creating a plug-and-play authentication system in NestJS means building a module that is:
-
Self-contained
-
Reusable across multiple apps or microservices
-
Easy to configure (e.g., JWT secret, user model)
-
Extendable (e.g., social login or OTP)
Let’s walk through this step by step with real-world logic explanations for each part.
Here we are not using any database connections or any custom logics, this is a basic setup where we can enhance it to meet the business needs.
1. Module Structure
auth/
├── auth.module.ts
├── auth.service.ts
├── auth.controller.ts
├── jwt.strategy.ts
├── local.strategy.ts
├── guards/
│ └── jwt-auth.guard.ts
├── dto/
│ └── login.dto.ts
├── interfaces/
│ └── user.interface.ts
Your Dynamic Snippet will be displayed here... This message is displayed because you did not provided both a filter and a template to use.
2. JWT-Based Plug-and-Play Auth
auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: 'your-secret-key', // Plug: you can use config service to pull this dynamically
signOptions: { expiresIn: '1h' },
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: 'your-secret-key', // Plug: you can use config service to pull this dynamically
signOptions: { expiresIn: '1h' },
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
Explanation:
-
PassportModule allows plugging in various strategies (JWT, local, Google).
-
JwtModule registers the signing logic.
-
JwtStrategy handles decoding and validating the token on each request.
auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async validateUser(username: string, password: string) {
// Example plug: Replace this with your real DB logic
if (username === 'admin' && password === 'admin123') {
return { userId: 1, username: 'admin' };
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async validateUser(username: string, password: string) {
// Example plug: Replace this with your real DB logic
if (username === 'admin' && password === 'admin123') {
return { userId: 1, username: 'admin' };
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}
Explanation:
-
validateUser() is where you hook your own DB or user service.
-
login() creates the JWT token from the payload.
auth.controller.ts
import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('login')
async login(@Body() body: { username: string, password: string }) {
const user = await this.authService.validateUser(body.username, body.password);
if (!user) throw new Error('Invalid credentials');
return this.authService.login(user);
}
}
import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('login')
async login(@Body() body: { username: string, password: string }) {
const user = await this.authService.validateUser(body.username, body.password);
if (!user) throw new Error('Invalid credentials');
return this.authService.login(user);
}
}
Explanation:
-
/auth/login accepts credentials and returns a signed JWT.
-
This is a clean API endpoint that can be dropped into any NestJS app.
jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'your-secret-key', // Same key used for signing
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'your-secret-key', // Same key used for signing
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
Explanation:
-
Extracts token from Authorization header.
-
Validates the token and attaches the payload to request.user.
jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Explanation:
-
A simple wrapper to secure routes using JWT.
3. Securing Routes in Other Modules
any.controller.ts
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
@Controller('profile')
export class ProfileController {
@UseGuards(JwtAuthGuard)
@Get()
getProfile(@Request() req) {
return req.user;
}
}
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
@Controller('profile')
export class ProfileController {
@UseGuards(JwtAuthGuard)
@Get()
getProfile(@Request() req) {
return req.user;
}
}
Explanation:
-
This route requires a valid JWT in the header.
-
Returns the decoded user info (in real case, fetch from DB).
This is a simple POC plug and play auth made in nestjs. For the more customization keep adding the business flow.
Hope you find it helpful!