web-dev-qa-db-fra.com

Obtention de données utilisateur à l'aide de gardes (rôles, JWT)

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.

7
bonblow

En plus de votre RolesGuard, vous devez utiliser un AuthGuard.

La norme

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'))

Extension

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;
  }

}

Pourquoi faire ceci?

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;
0
Kim Kern

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

0
Lewsmith

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'.

0
balakrishna