Passport Strategies
⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 7 min
Passport Strategies are plugins that teach the Passport library how to authenticate a user using a specific protocol or provider.
Overview
There are over 500 Passport strategies available on npm. They all follow a similar pattern but require different configuration options.
The two most fundamental strategies used in almost every API are:
passport-local: Used for the initial login. It receives a username and password from the request body, verifies them, and proves the user is who they say they are.passport-jwt: Used for all subsequent requests. It extracts a JSON Web Token from the Authorization header, verifies its signature, and proves the user is still authenticated.
Key Concepts
- Naming Conventions: When you extend
PassportStrategy(Strategy), it defaults to the strategy’s standard name (e.g.,'local'or'jwt'). You can pass a second string argument to override this name (e.g.,PassportStrategy(Strategy, 'jwt-refresh')), which is essential if you need two variations of the same strategy type. - The Strategy Pipeline: A request hits a Guard -> The Guard invokes Passport -> Passport runs the Strategy logic (checking headers/passwords) -> If successful, Passport calls your
validate()method ->validate()returns the User -> Guard attaches User to Request.
Code Examples
Implementing Google OAuth 2.0 Strategy
This demonstrates how easily you can plug in a third-party strategy (like passport-google-oauth20) into the NestJS pattern.
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(private configService: ConfigService) {
// 1. Configure the strategy with your Google API credentials
super({
clientID: configService.get('GOOGLE_CLIENT_ID'),
clientSecret: configService.get('GOOGLE_CLIENT_SECRET'),
callbackURL: 'http://localhost:3000/auth/google/callback',
scope: ['email', 'profile'], // What data we want from Google
});
}
// 2. Google redirects back to your API, passing the access token and user profile
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback
): Promise<any> {
const { name, emails, photos } = profile;
// 3. Format the data into our internal User shape
const user = {
email: emails[0].value,
firstName: name.givenName,
lastName: name.familyName,
picture: photos[0].value,
accessToken
};
// 4. Pass the user object to the 'done' callback (which attaches it to request.user)
done(null, user);
}
}
Triggering the Strategy
To trigger the Google strategy, you simply use the built-in AuthGuard from @nestjs/passport, passing the name of the strategy.
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
// 1. This route redirects the user to the Google Login screen
@Get('google')
@UseGuards(AuthGuard('google'))
async googleAuth(@Req() req) {}
// 2. Google redirects back to this route after login
@Get('google/callback')
@UseGuards(AuthGuard('google'))
googleAuthRedirect(@Req() req) {
// The user is now authenticated!
// req.user contains the object we formatted in the validate() method.
return {
message: 'User information from google',
user: req.user
};
}
}
Best Practices
- Environment Variables: Never hardcode API keys, client secrets, or JWT secrets inside your Strategy classes. Always use the NestJS
ConfigServiceto load them securely from your.envfile. - Multiple Strategies: A single route can attempt multiple strategies by passing an array to the Guard:
@UseGuards(AuthGuard(['jwt', 'api-key'])). If any of the strategies succeed, the user is authenticated.