La documentation est un peu mince ici, donc j'ai rencontré un problème. J'essaie d'utiliser des gardes pour sécuriser le contrôleur ou ses actions, donc je vais demander le rôle des demandes authentifiées (par JWT). Dans mon auth.guard.ts, je demande "request.user" mais il est vide, donc je ne peux pas vérifier le rôle des utilisateurs. Je ne sais pas comment définir "request.user". Voici mon module d'authentification et ses importations.
auth.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { RolesGuard } from './auth.guard';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Get('token')
async createToken(): Promise<any> {
return await this.authService.signIn();
}
@Get('data')
@UseGuards(RolesGuard)
findAll() {
return { message: 'authed!' };
}
}
roles.guard.ts
Ici, user.request est vide, car je ne le définis jamais. La documentation ne montre ni comment ni où.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user; // it's undefined
const hasRole = () =>
user.roles.some(role => !!roles.find(item => item === role));
return user && user.roles && hasRole();
}
}
auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { HttpStrategy } from './http.strategy';
import { UserModule } from './../user/user.module';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secretOrPrivateKey: 'secretKey',
signOptions: {
expiresIn: 3600,
},
}),
UserModule,
],
providers: [AuthService, HttpStrategy],
controllers: [AuthController],
})
export class AuthModule {}
auth.service.ts
import { Injectable } from '@nestjs/common';
import { UserService } from '../user/user.service';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private readonly userService: UserService,
private readonly jwtService: JwtService,
) {}
async signIn(): Promise<object> {
// In the real-world app you shouldn't expose this method publicly
// instead, return a token once you verify user credentials
const user: any = { email: '[email protected]' };
const token: string = this.jwtService.sign(user);
return { token };
}
async validateUser(payload: any): Promise<any> {
// Validate if token passed along with HTTP request
// is associated with any registered account in the database
return await this.userService.findOneByEmail(payload.email);
}
}
jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'secretKey',
});
}
async validate(payload: any) {
const user = await this.authService.validateUser(payload);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Documentation: https://docs.nestjs.com/guards
Merci pour toute aide.
En plus de votre RolesGuard
, vous devez utiliser un AuthGuard
.
Vous pouvez utiliser l'implémentation standard AuthGuard
qui attache l'objet utilisateur à la demande. Il génère une erreur 401 lorsque l'utilisateur n'est pas authentifié.
@UseGuards(AuthGuard('jwt'))
Si vous devez écrire votre propre garde car vous avez besoin d'un comportement différent, étendez l'original AuthGuard
et remplacez les méthodes que vous devez modifier (handleRequest
dans l'exemple):
@Injectable()
export class MyAuthGuard extends AuthGuard('jwt') {
handleRequest(err, user, info: Error) {
// don't throw 401 error when unauthenticated
return user;
}
}
Si vous regardez le code source du AuthGuard
, vous pouvez voir qu'il attache l'utilisateur à la demande comme rappel à la méthode du passeport. Si vous ne souhaitez pas utiliser/étendre le AuthGuard
, vous devrez implémenter/copier les parties pertinentes.
const user = await passportFn(
type || this.options.defaultStrategy,
options,
// This is the callback passed to passport. handleRequest returns the user.
(err, info, user) => this.handleRequest(err, info, user)
);
// Then the user object is attached to the request
// under the default property 'user' which you can change by configuration.
request[options.property || defaultOptions.property] = user;
Est-ce que cela fonctionne si vous utilisez req.authInfo
?
Tant que vous ne fournissez pas de rappel personnalisé à la méthode passport.authenticate, les données utilisateur doivent être attachées à l'objet de demande comme ceci.
req.authInfo
devrait être l'objet que vous avez renvoyé dans votre méthode validate
Vous pouvez attacher plusieurs gardes ensemble (@UseGuards (AuthGuard ('jwt'), RolesGuard)) pour passer le contexte entre elles. Ensuite, vous aurez accès à l'objet 'req.user' à l'intérieur de 'RolesGuard'.