Home Blog Page 111

Refresh Tokens and Token Rotation in NestJS: Secure JWT Authentication

0
nestjs fullstack course
nestjs fullstack course

As you build secure applications with NestJS and JWT authentication, one critical concern is token expiration. Access tokens should have short lifespans for security, but that often forces users to log in frequently. This is where refresh tokens come in — allowing you to issue new access tokens without requiring the user to log in again.

In this module, you’ll learn how to implement refresh tokens, understand token rotation, and ensure your authentication flow is both user-friendly and secure.


Table of Contents

  1. What Are Refresh Tokens?
  2. Why Use Refresh Tokens?
  3. How Refresh Token Flow Works
  4. Generating and Issuing Refresh Tokens
  5. Storing Refresh Tokens Securely
  6. Token Rotation: Preventing Replay Attacks
  7. Implementing Refresh Token Endpoint in NestJS
  8. Best Practices
  9. Conclusion

What Are Refresh Tokens?

A refresh token is a long-lived token that is used to obtain a new access token after the original one expires. Unlike the access token, which is usually valid for a few minutes, refresh tokens can last for days or even weeks.


Why Use Refresh Tokens?

  • Short-lived access tokens reduce the risk of token theft.
  • User doesn’t have to log in again after the token expires.
  • Enables automatic session renewal (e.g., mobile apps, SPAs).
  • Supports secure token rotation mechanisms.

How Refresh Token Flow Works

  1. Login: User provides credentials → server returns an access token + refresh token.
  2. Access Token Expires: Client detects expiry.
  3. Refresh Request: Client sends the refresh token to a dedicated endpoint.
  4. New Token Issued: Server verifies refresh token → generates a new access token (and optionally a new refresh token).
  5. Token Rotation (optional): Replace the used refresh token with a new one.

Generating and Issuing Refresh Tokens

You can generate a refresh token similarly to an access token, but with a different secret and longer expiration:

// auth.service.ts
async generateTokens(user: any) {
const payload = { sub: user.id, email: user.email };

const accessToken = this.jwtService.sign(payload, {
secret: process.env.ACCESS_TOKEN_SECRET,
expiresIn: '15m',
});

const refreshToken = this.jwtService.sign(payload, {
secret: process.env.REFRESH_TOKEN_SECRET,
expiresIn: '7d',
});

return { accessToken, refreshToken };
}

Storing Refresh Tokens Securely

Options:

  • HTTP-only cookies (recommended for web apps).
  • Database (to support token revocation and rotation).
  • Client-side local storage (less secure, not recommended).

Example: Storing hashed refresh tokens in DB

async storeRefreshToken(userId: number, token: string) {
const hashed = await bcrypt.hash(token, 10);
await this.usersService.update(userId, { refreshToken: hashed });
}

Token Rotation: Preventing Replay Attacks

Token rotation means replacing the refresh token every time it is used. This prevents replay attacks where a stolen token could be reused indefinitely.

Steps for rotation:

  1. Store hashed refresh tokens in the database.
  2. When a refresh token is used:
    • Validate it.
    • Invalidate/delete it.
    • Issue and store a new one.
  3. Reject reused or expired tokens.

Implementing Refresh Token Endpoint in NestJS

Here’s an example of a /auth/refresh endpoint:

// auth.controller.ts
@Post('refresh')
async refresh(@Body() body: { refreshToken: string }) {
const user = await this.authService.verifyRefreshToken(body.refreshToken);
const tokens = await this.authService.generateTokens(user);
await this.authService.storeRefreshToken(user.id, tokens.refreshToken);
return tokens;
}
// auth.service.ts
async verifyRefreshToken(token: string) {
try {
const payload = this.jwtService.verify(token, {
secret: process.env.REFRESH_TOKEN_SECRET,
});

const user = await this.usersService.findById(payload.sub);
if (!user || !(await bcrypt.compare(token, user.refreshToken))) {
throw new UnauthorizedException('Invalid refresh token');
}

return user;
} catch {
throw new UnauthorizedException('Invalid refresh token');
}
}

Best Practices

  • Use different secrets for access and refresh tokens.
  • Use HTTP-only cookies for web clients to reduce XSS risks.
  • Store refresh tokens hashed in the database.
  • Implement refresh token rotation to prevent reuse.
  • Blacklist or expire old tokens if a new one is issued.

Conclusion

Refresh tokens significantly enhance the security and usability of your authentication system in NestJS. With proper token rotation and validation, you can prevent common vulnerabilities while ensuring seamless user sessions.

Passport Strategies in NestJS: JWT, Local, OAuth2, and Google Login

0
nestjs fullstack course
nestjs fullstack course

NestJS integrates seamlessly with Passport.js, a popular Node.js middleware for authentication. Passport provides a rich ecosystem of strategies that you can plug into your app, including Local, JWT, OAuth2, and providers like Google, Facebook, and more.

In this module, you’ll learn how to implement and integrate various Passport strategies in NestJS, with a focus on JWT, Local, OAuth2, and Google authentication.


Table of Contents

  1. What is Passport.js and Why Use It?
  2. Strategy Overview
  3. Local Strategy: Email and Password Login
  4. JWT Strategy: Stateless Authentication
  5. OAuth2 Strategy: Third-Party Authentication
  6. Google Strategy: Social Login with Google
  7. Best Practices
  8. Conclusion

What is Passport.js and Why Use It?

Passport.js is a flexible authentication library for Node.js that supports a wide variety of authentication mechanisms via strategies. NestJS leverages Passport under the hood, providing decorators, guards, and modules to use these strategies in a structured way.


Strategy Overview

StrategyPurposeUse Case
LocalUsername/email + password loginTraditional login systems
JWTToken-based stateless authAPI token authentication
OAuth2Third-party authenticationLogin with GitHub, Google, etc.
GoogleSpecialized OAuth2 for GoogleGoogle login integration

Local Strategy: Email and Password Login

This strategy is used to authenticate a user with credentials stored in your database.

Install Packages

npm install passport passport-local
npm install --save-dev @types/passport-local

Create the Strategy

// local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}

async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}

Create Local Auth Guard

// local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

Use It in Controller

@Post('login')
@UseGuards(LocalAuthGuard)
async login(@Request() req) {
return this.authService.login(req.user); // issue JWT
}

JWT Strategy: Stateless Authentication

We’ve already covered JWT in the previous module. It issues a token after login and uses it for subsequent requests.

Here’s a quick reminder of the guard:

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

JWT Strategy uses the token to verify and validate users without storing session data on the server.


OAuth2 Strategy: Third-Party Authentication

NestJS does not officially support generic OAuth2, but you can easily use it with Passport’s strategy base.

Install Required Packages

npm install passport-oauth2
npm install --save-dev @types/passport-oauth2

Then implement a custom strategy using PassportStrategy from @nestjs/passport and configure it with your provider’s client ID, secret, and callback URL.

Due to provider-specific configurations, it’s often better to use a dedicated strategy like passport-google-oauth20.


Google Strategy: Social Login with Google

This is one of the most commonly used OAuth2 strategies.

Install Google Strategy

npm install passport-google-oauth20
npm install --save-dev @types/passport-google-oauth20

Configure the Strategy

// google.strategy.ts
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable } from '@nestjs/common';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/auth/google/redirect',
scope: ['email', 'profile'],
});
}

async validate(accessToken: string, refreshToken: string, profile: any, done: VerifyCallback): Promise<any> {
const { name, emails } = profile;
const user = {
email: emails[0].value,
name: name.givenName,
accessToken,
};
done(null, user);
}
}

Add Redirect Handlers

// auth.controller.ts
@Get('google')
@UseGuards(AuthGuard('google'))
async googleAuth() {
// initiates Google OAuth2 login flow
}

@Get('google/redirect')
@UseGuards(AuthGuard('google'))
googleAuthRedirect(@Request() req) {
return this.authService.loginWithGoogle(req.user);
}
Make sure to configure Google OAuth in the Google Developer Console and add your redirect URL.

Best Practices

  • Store secrets securely using environment variables.
  • Validate user identity on first login from external providers (e.g., link to existing user).
  • Use JWT with social login to keep a unified session management strategy.
  • Rate limit login endpoints to prevent brute force attacks.
  • Keep strategies modularized to separate logic and responsibilities.

Conclusion

NestJS and Passport together offer a flexible and robust authentication solution for any type of application. By combining Local, JWT, and OAuth2 strategies like Google login, you can provide secure and convenient authentication methods tailored to your users.

JWT Authentication in NestJS: Login, Signup, and Protected Routes

0
nestjs fullstack course
nestjs fullstack course

Authentication is a fundamental part of most modern web applications. In this module, you’ll learn how to implement JWT (JSON Web Token) based authentication in NestJS to handle user signup, login, and secure route protection.

NestJS integrates seamlessly with Passport.js, providing powerful tools to implement authentication strategies like JWT.


Table of Contents

  1. What is JWT?
  2. Installing Required Packages
  3. Setting Up the AuthModule
  4. User Signup
  5. User Login and Token Issuance
  6. Protecting Routes with Guards
  7. Accessing Authenticated User in Controllers
  8. Conclusion

What is JWT?

JWT (JSON Web Token) is a compact, URL-safe token format used for securely transmitting information between parties. In authentication, it’s commonly used to represent user identity after successful login.

JWTs typically consist of:

  • Header: Token type and signing algorithm
  • Payload: User data (e.g., sub, email, roles)
  • Signature: Ensures the token hasn’t been tampered with

Installing Required Packages

Install the necessary packages for JWT and Passport integration:

npm install @nestjs/passport passport passport-jwt
npm install @nestjs/jwt
npm install --save-dev @types/passport-jwt

Setting Up the AuthModule

1. Create AuthModule and services

nest g module auth
nest g service auth
nest g controller auth

2. Configure JwtModule in auth.module.ts

// auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';

@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: 'your_jwt_secret', // Should be stored in env file
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}

User Signup

Create DTO

// dto/create-user.dto.ts
import { IsEmail, IsNotEmpty } from 'class-validator';

export class CreateUserDto {
@IsEmail()
email: string;

@IsNotEmpty()
password: string;
}

Hash Password and Save User

// auth.service.ts
import * as bcrypt from 'bcrypt';

async signup(createUserDto: CreateUserDto) {
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(createUserDto.password, salt);

const user = this.usersRepository.create({
...createUserDto,
password: hashedPassword,
});

return this.usersRepository.save(user);
}

User Login and Token Issuance

Login DTO

// dto/login.dto.ts
import { IsEmail, IsNotEmpty } from 'class-validator';

export class LoginDto {
@IsEmail()
email: string;

@IsNotEmpty()
password: string;
}

Validate User and Generate Token

// auth.service.ts
import { JwtService } from '@nestjs/jwt';

async validateUser(email: string, password: string): Promise<any> {
const user = await this.usersRepository.findOne({ 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),
};
}

AuthController

// auth.controller.ts
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateUserDto } from './dto/create-user.dto';
import { LoginDto } from './dto/login.dto';

@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}

@Post('signup')
signup(@Body() dto: CreateUserDto) {
return this.authService.signup(dto);
}

@Post('login')
async login(@Body() dto: LoginDto) {
const user = await this.authService.validateUser(dto.email, dto.password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return this.authService.login(user);
}
}

Protecting Routes with Guards

Create JWT Strategy

// 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_jwt_secret',
});
}

async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}

Apply Auth Guard to Routes

// user.controller.ts
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';

@Controller('users')
export class UserController {
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
}

Create Guard

// jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

Accessing Authenticated User in Controllers

NestJS automatically injects the user object into the request if the route is guarded:

@Get('me')
@UseGuards(JwtAuthGuard)
getMe(@Request() req) {
return req.user;
}

You can also create a custom decorator:

// user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
(data, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);

Then use it like this:

@Get('me')
getMe(@CurrentUser() user) {
return user;
}

Conclusion

JWT authentication in NestJS provides a powerful way to secure your APIs. In this module, you learned how to:

  • Implement user signup with password hashing
  • Authenticate users and issue JWTs
  • Protect routes using guards and extract user info from the token

Today in History – 21 April

0
today in history 21 april

today in history 21 april

753 BC

According to tradition, on April 21, 753 B.C., Romulus and his twin brother, Remus, found Rome on the site where they were suckled by a she-wolf as orphaned infants. Actually, the Romulus and Remus myth originated sometime in the fourth century B.C., and the exact date of Rome’s founding was set by the Roman scholar Marcus Terentius Varro in the first century B.C.

1451

Buhlul Khan Lodi, an Afghan and founder of Lodi dynasty, ascends the throne of Delhi and displaced the Alam Shah of Syed dynasty.

1526

Mughal conqueror Babur Shah (1483-1530) defeats Ibrahim Lodi II, the Lodi Sultan of Delhi, and captures Delhi and the Koh-i-noor diamond, in the First Battle of Panipat. He founded the Mughal dynasty in India (1526-1761), consolidated by his grandson Akbar.

1720

Peshwa Balaji Vishwanath passed away and was succeeded by his son Peshwa Baji Rao I. He was a great soldier and General.

1805

Defitiniv treaty between Anandrao Gaikwad and Britishers.

1895

Woodville Latham and his sons, Otway and Gray, demonstrate their “Panopticon,” the first movie projector developed in the United States. Although motion pictures had been shown in the United States for several years using Thomas Edison’s Kinetoscope, the films could only be viewed one at a time in a peep-show box, not projected to a large audience.

1913

Preview show of ‘Raja Harishchandra’ was held on April 21 at Olympia Cinema.

1930

A fire at an Ohio prison kills 320 inmates, some of whom burn to death when they are not unlocked from their cells. It is one of the worst prison disasters in American history. The Ohio State Penitentiary was built in Columbus in 1834.

1955

Avadi session of the Indian National Congress adopts a socialistic pattern of society for India.

1975

Farakka Barrage dedicated to nation.

1990

The first indigenously built missile cruiser boat, ‘Vibhuti’, was inaugurated and launched.

1992

RBI eases credit policy, lending rates reduced from 6 to 4; 13\% interest ceiling on deposits.

1995

Medium range ‘Akash’, surface-to-air-missile, flight trial successful.

1996

Sanjay Thapar of the IAF (leader) becomes first Indian to parachute down to geometric North pole and unfurl the flag.

1997

Inder Kumar Gujaral sworn as the 12th Prime Minister of India, along with old council of ministers in the H. D. Deve Gowda govt. minus Tamil Maanila Congress Minister and D. P. Yadav of Janata Dal.

Related Articles:

Today in History – 20 April

Today in History – 19 April

Today in History – 18 April

Today in History – 17 April

Pagination, Filtering, and Query Optimization in NestJS

0
nestjs fullstack course
nestjs fullstack course

When working with APIs that return large datasets, it’s essential to provide mechanisms like pagination, filtering, and query optimization. These strategies not only enhance user experience but also help improve application performance and reduce server load.

In this module, you’ll learn how to implement efficient pagination, flexible filtering, and optimized queries in NestJS, particularly when using TypeORM.


Table of Contents

  1. Why Pagination and Filtering Matter
  2. Implementing Pagination
  3. Adding Filtering to Endpoints
  4. Query Optimization Techniques
  5. Practical Example
  6. Best Practices
  7. Conclusion

Why Pagination and Filtering Matter

Returning an entire database collection in one request is inefficient and unsustainable for both frontend and backend systems. Pagination and filtering allow clients to fetch data incrementally and refine the results, which:

  • Reduces bandwidth usage
  • Improves database performance
  • Enhances user experience with faster load times

Implementing Pagination

Offset-Based Pagination (Standard)

This is the most common method using skip and take with TypeORM.

DTO for Pagination

// dto/pagination-query.dto.ts
import { Type } from 'class-transformer';
import { IsOptional, IsPositive, Min } from 'class-validator';

export class PaginationQueryDto {
@IsOptional()
@Type(() => Number)
@IsPositive()
limit?: number;

@IsOptional()
@Type(() => Number)
@Min(0)
offset?: number;
}

Applying in Service

// user.service.ts
async findAll(paginationQuery: PaginationQueryDto) {
const { limit, offset } = paginationQuery;
return this.userRepository.find({
skip: offset,
take: limit,
order: { createdAt: 'DESC' },
});
}

Cursor-Based Pagination (Brief Intro)

While not covered deeply here, cursor-based pagination is recommended for real-time apps and large datasets where offset becomes inefficient. It involves using unique identifiers (like timestamps or IDs) to fetch the next set of data.


Adding Filtering to Endpoints

You can add flexible filtering using optional query parameters.

Using DTOs for Filters

// dto/filter-query.dto.ts
import { IsOptional, IsEmail } from 'class-validator';

export class FilterQueryDto {
@IsOptional()
name?: string;

@IsOptional()
@IsEmail()
email?: string;
}

Apply Filters in Query

async findAll(query: PaginationQueryDto & FilterQueryDto) {
const { limit, offset, name, email } = query;

const qb = this.userRepository.createQueryBuilder('user');

if (name) {
qb.andWhere('user.name LIKE :name', { name: `%${name}%` });
}

if (email) {
qb.andWhere('user.email = :email', { email });
}

return qb.skip(offset).take(limit).orderBy('user.createdAt', 'DESC').getMany();
}

Query Optimization Techniques

To improve database performance, apply the following:

1. Select Only Required Fields

qb.select(['user.id', 'user.name']);

2. Use Joins Only When Necessary

qb.leftJoinAndSelect('user.profile', 'profile'); // only if needed

3. Add Indexes to Frequently Queried Fields

In your entity:

@Index()
@Column()
email: string;

4. Use .getRawMany() if You Don’t Need Entity Transformations

const rawUsers = await qb.getRawMany();

Practical Example: Combined Usage

@Get()
async getUsers(
@Query() paginationQuery: PaginationQueryDto,
@Query() filterQuery: FilterQueryDto
) {
return this.userService.findAll({ ...paginationQuery, ...filterQuery });
}

Best Practices

  • Validate and sanitize query parameters using DTOs.
  • Cap the limit (e.g., max 100) to prevent large data dumps.
  • Paginate all list endpoints for scalability.
  • Use indexes and optimize queries with profiling tools (e.g., EXPLAIN).
  • For large datasets, prefer cursor-based pagination.

Conclusion

Pagination, filtering, and query optimization are crucial for building scalable APIs in NestJS. With a combination of DTO validation, flexible TypeORM queries, and best practices, you can ensure your endpoints are performant and reliable—regardless of the data volume.