CodePark/ questions / Guards & auth
START PRACTICINGPRACTICE
Backend question · NestJS › Guards & auth

How do you implement JWT authentication in Nest with a guard, and make most routes protected by default with a few public ones?

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwt: JwtService, private reflector: Reflector) {}

  async canActivate(ctx: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [ctx.getHandler(), ctx.getClass()]);
    if (isPublic) return true;
    const req = ctx.switchToHttp().getRequest();
    const token = req.headers.authorization?.split(" ")[1];
    if (!token) throw new UnauthorizedException();
    try { req.user = await this.jwt.verifyAsync(token); }
    catch { throw new UnauthorizedException(); }
    return true;
  }
}

Use a JWT guard registered globally, plus a @Public() decorator for the few open routes.

Login. Register JwtModule from @nestjs/jwt with a secret and an expiry time. A login endpoint checks the user's credentials. Then it returns a token with await jwtService.signAsync({ sub: user.id, roles }).

The guard:

  • It takes the bearer token from the Authorization header.
  • It checks the token with verifyAsync.
  • It attaches the token's payload to req.user.
  • If the token is missing or invalid, it throws UnauthorizedException. The client gets 401 instead of the default 403.

Protected by default. Register the guard globally with { provide: APP_GUARD, useClass: JwtAuthGuard }. This way also gives the guard DI (dependency injection: Nest creates and passes in the services it needs).

Public routes. Make a @Public() decorator with SetMetadata(IS_PUBLIC_KEY, true). Put it on routes like login. The guard checks this flag with Reflector first and lets those requests through.

@nestjs/passport with a JWT strategy is another way to do the same job.

  • On login, use JwtModule and JwtService.signAsync to create the token.
  • The guard verifies the bearer token and sets req.user.
  • Throw UnauthorizedException to return 401.
  • Register the guard globally with APP_GUARD.
  • A @Public() metadata flag lets a route skip the guard.