0
votes

L'authentification JWT Nest.js Auth Guard renvoie constamment 401 non autorisés

En utilisant Postman pour tester mes points de terminaison, je peux "me connecter" avec succès et recevoir un jeton JWT. Maintenant, j'essaie d'atteindre un point de terminaison qui a supposément un AuthGuard pour m'assurer que maintenant que je suis connecté, je peux maintenant y accéder.

Cependant, il renvoie constamment 401 Unauthorized même lorsqu'il est présenté le jeton JWT dans Postman.

Voici mon code:

user.controller.ts

@Controller('auth')
export class AuthenticationController {
    constructor(
        private readonly authenticationService: AuthenticationService,
        private readonly usersService: UsersService,
    ) {}

    @UseGuards(AuthGuard('local'))
    @Post('login')
    public async loginAsync(@Response() res, @Body() login: LoginModel) {
        const user = await this.usersService.getUserByUsernameAsync(login.username);

        if (!user) {
            res.status(HttpStatus.NOT_FOUND).json({
                message: 'User Not Found',
            });
        } else {
            const token = this.authenticationService.createToken(user);
            return res.status(HttpStatus.OK).json(token);
        }
    }
}

jwt.strategy.ts

@Module({
    imports: [
        ConfigModule,
        UsersModule,
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secret: 'SuperSecretJWTKey',
            signOptions: { expiresIn: 3600 },
        }),
    ],
    controllers: [AuthenticationController],
    providers: [AuthenticationService, LocalStrategy, JwtStrategy],
    exports: [AuthenticationService, LocalStrategy, JwtStrategy],
})
export class AuthenticationModule {}

J'ai également essayé ExtractJWT.fromAuthHeaderWithScheme('JWT') mais cela ne fonctionne pas.

authentication.module.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(
        private readonly authenticationService: AuthenticationService,
    ) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: 'SuperSecretJWTKey',
        });
    }

    async validate(payload: any, done: Function) {
        console.log("I AM HERE"); // this never gets called.
        const user = await this.authenticationService.validateUserToken(payload);

        if (!user) {
            return done(new UnauthorizedException(), false);
        }

        done(null, user);
    }
}

authentication.controller.ts

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @UseGuards(AuthGuard())
    @Get()
    getUsers() {
        return this.usersService.getUsersAsync();
    }
}

Dans Postman, je peux utiliser mon point de terminaison de connexion pour me connecter avec les informations d'identification appropriées et recevoir un jeton JWT. Ensuite, j'ajoute un en-tête d' Authentication à une demande GET, copie et colle dans le jeton JWT, et j'ai essayé les schémas "Bearer" et "JWT" et les deux retournent 401 Unauthorized comme vous pouvez le voir dans les images ci-dessous.

entrez la description de l'image ici

entrez la description de l'image ici

J'ai utilisé le débogueur JWT.IO, pour vérifier s'il y a quelque chose qui ne va pas avec mon jeton et qu'il semble correct: entrez la description de l'image ici

Je ne sais pas quel pourrait être le problème ici. Toute aide serait grandement appréciée.


0 commentaires

3 Réponses :


2
votes

Notez que la fonction validate() de votre stratégie JWT n'est appelée qu'après validation réussie du JWT. Si vous obtenez systématiquement une réponse 401 lorsque vous essayez d'utiliser le JWT, vous ne pouvez pas vous attendre à ce que cette fonction soit appelée.

Le return de la méthode validate() est injecté dans l'objet de requête de toute opération protégée par l'authentification JWT.

Je ne suis pas sûr de la fonction done() que vous appelez, mais voici une méthode validate() partir d'un de mes projets actuels:

  async login(authCredentialsDto: AuthCredentialsDto): Promise<{ accessToken: string }> {
    const { email, password } = authCredentialsDto

    const success = await this.usersRepository.verifyCredentials(email, password)

    if (!success) {
      throw new UnauthorizedException('Invalid credentials')
    }

    // roles, email, etc can be added to the payload - but don't add sensitive info!
    const payload: JwtPayload = { email } 
    const accessToken = this.jwtService.sign(payload)

    this.logger.debug(`Generated JWT token with payload ${JSON.stringify(payload)}`)

    return { accessToken }
  }

Il semble que vous soyez sur la bonne voie dans le désir de renvoyer un utilisateur. Assurez-vous que c'est ce que fait réellement authenticationService.validateUserToken() .

Dans la stratégie, jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() semble correct, et dans Postman, l'utilisation de l'en-tête Authorization avec Bearer TOKEN semble également correcte.

En ce qui concerne votre fichier authentication.controller.ts , veillez à utiliser les objets @Request et @Response directement dans vos contrôleurs dans NestJS. Ceux-ci accèdent au cadre sous-jacent, par exemple Express, et sont susceptibles de contourner de nombreuses fonctionnalités mises en œuvre par Nest. Reportez-vous à https://docs.nestjs.com/faq/request-lifecycle pour voir ce que vous sautez ...

Vous pouvez renvoyer des objets et lancer des erreurs directement à partir d'une méthode de contrôleur décorée (par exemple @Get() , Post() , etc.) dans NestJS et le framework se chargera du reste: code HTTP, JSON, etc.

À partir de votre contrôleur, pensez à abandonner @Reponse res et à utiliser throw new UnauthorizedException('User Not Found') et une simple approche return { token } (ou similaire) à la place.

Dans votre itinéraire protégé, j'ai trouvé que déclarer explicitement AuthGuard('jwt') fonctionne mieux et ne produit pas d'avertissements dans certains cas, même si vous avez défini votre stratégie par défaut sur JWT.

Avez-vous réellement besoin d' AuthGuard('local') sur votre itinéraire de connexion?

Dans votre méthode loginAsync() , N'OUBLIEZ PAS l'étape cruciale de la signature effective de votre token avec votre payload. Vous n'avez pas fourni votre code pour l' createToken() méthode createToken() dans votre service d'authentification, mais je soupçonne que c'est peut-être ce qui vous manque.

Considérez cette implémentation fonctionnelle d'un service de connexion (qui est simplement appelée par la fonction de connexion de son contrôleur):

async validate(payload: JwtPayload): Promise<User> {
  const { email } = payload
  const user = await this.authService.getActiveUser(email)

  if (!user) {
    throw new UnauthorizedException()
  }

  return user
}

Notez que le jwtService est injecté dans la classe via Dependency Injection en ajoutant private jwtService: JwtService aux private jwtService: JwtService du constructeur.

Notez également dans ce qui précède comment une interface est définie pour JwtPayload afin qu'elle soit explicitement typée. C'est mieux que d'en utiliser tel any dans votre code.

Enfin, si votre JWT ne se valide toujours pas, assurez- vous absolument que vous utilisez correctement votre jeton dans Postman. Faites très attention à ne pas ajouter d'espaces de début / de fin, de nouvelles lignes, etc. J'ai commis cette erreur moi-même. Vous voudrez peut-être vérifier la validité en écrivant un fichier JS rapide pour essayer votre API et faire une requête d'extraction qui définit l'en-tête d'autorisation avec la valeur Bearer ${token} .

J'espère que cela aide, bonne chance!


2 commentaires

Merci. C'était une aide précieuse. Ce qui se passait, c'est que j'utilisais un package appelé jsonwebtoken et que je faisais quelque chose comme import * as jwt from 'jsonwebtoken' suivi de jwt.sign(...) . Lors de l'utilisation du JwtService réel de @nestjs/jwt , cela a résolu le @nestjs/jwt .


Je suis heureux que cela ait aidé, c'est sûr que l'utilisation de @nestjs/jwt est la voie à suivre avec cette approche! À votre santé!



1
votes

J'ai eu exactement le même problème. Mon problème était que JwtModule secret et JwtStrategy secretOrKey étaient différents. J'espère que cela pourrait aider quelqu'un coincé avec ça!


1 commentaires

J'ai exactement le même problème! Lié à .env n'est pas chargé correctement. C'est le troisième problème que j'ai remarqué lié à cela et je n'arrive toujours pas à le résoudre.



0
votes

J'avais un statut 401 similaire. Mon problème était que l'expiration du jeton était vraiment courte (60 s). Assurez-vous également d'avoir une période d'expiration raisonnable lorsque vous testez jwt.


0 commentaires