Optional Authentication in nestjs
Another way of doing this is by creating an anonymous passport strategy:
// In anonymous.strategy.ts
@Injectable()
export class AnonymousStrategy extends PassportStrategy(Strategy, 'anonymous') {
constructor() {
super()
}
authenticate() {
return this.success({})
}
}
Then, chaining this strategy in the controller:
// In create-post.controller.ts
@Controller()
export class CreatePostController {
@UseGuards(AuthGuard(['jwt', 'anonymous'])) // first success wins
@Post('/posts')
async createPost(@Req() req: Request, @Body() dto: CreatePostDto) {
const user = req.user as ExpressUser
if (user.email) {
// Do something if user is authenticated
} else {
// Do something if user is not authenticated
}
...
}
}
There is no built-in decorator but you can easily create one yourself. See the example from the docs:
import { createParamDecorator } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
export const User = createParamDecorator((data, req) => {
return req.user;
});
Since the built-in AuthGuard
throws an exception, you can create your own version and overwrite the request handler:
@Injectable()
export class MyAuthGuard extends AuthGuard('jwt') {
handleRequest(err, user, info) {
// no error is thrown if no user is found
// You can use info for logging (e.g. token is expired etc.)
// e.g.: if (info instanceof TokenExpiredError) ...
return user;
}
}
Make sure that you are not throwing errors in your JwtStrategy
:
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'secretKey',
});
}
async validate(payload) {
const user = await this.authService.validateUser(payload);
// in the docs an error is thrown if no user is found
return user;
}
}
Then you can use it in your Controller
like this:
@Get()
@UseGuards(MyAuthGuard)
getUser(@User() user) {
return {user};
}