Vamos começar uma série de artigos sobre Autenticação JWT com NestJS abordando os pontos como: geração de Token JWT, proteção de rotas, autorização e “refresh token”.
Neste artigo vamos começar a utilizar a autenticação JWT (JSON Web Token) com o NestJS para a geração de Tokens utilizando a biblioteca Passport que é recomendada pela própria documentação do NestJS, pois o Passport é uma biblioteca muito utilizada e confiável do ecossistema NodeJS.
Curso Node.js - Fundamentos
Conhecer o cursoAntes de tudo, aconselho a leitura do artigo “Criando primeiro Crud com NestJS”, onde criamos um sistema de cadastro de usuário que podemos reaproveitá-lo neste artigo. Você pode clonar o projeto no seguinte repositório do GitHub: https://github.com/wesleygado/artigo-crud-nestjs. Também recomendo a leitura dos artigos “O que é JWT?” e “Fluxo de autenticação baseado em JWT” caso não conheça o que é o JWT e como podemos utiliza-lo para realizar autenticação em nossas aplicações.
Perfeito, com o projeto anterior já configurado vamos fazer algumas alterações, a primeira é excluir o arquivo do banco anterior, que está localizado em .db/sql
e adicionar a propriedade password
na entidade users
e no DTO, outra alteração é trocar a propriedade idade
por email
sendo uma string, o email será o nome de usuário (username) que vamos utilizar para efetuar a autenticação do usuário, desta forma:
//user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn('increment')
id: number;
@Column()
nome: string;
@Column()
email: string;
@Column()
password: string;
}
Desta forma alterando também no DTO:
//create-user.dto.ts
export class CreateUserDto {
nome: string;
email: string;
password: string;
}
Desta forma agora temos cada usuário possuindo uma senha, vale salientar que em uma aplicação real é importante utilizar meios de criptografia de senha, como o bcrypt. Aqui estamos salvando a senha simples no banco para fins didáticos e também focarmos em como a autenticação JWT funciona e na geração do Token.
Agora o segundo passo é instalar o pacote Passport, pacote este que facilitará a configuração da autenticação, você pode saber mais sobre ele na própria documentação.
Para instalar basta digitarmos os comandos:
npm install @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt
npm install @nestjs/passport passport passport-local
npm install --save-dev @types/passport-local
Agora temos os pacotes necessários para a implementação do JWT. Logo em seguida vamos criar o diretório auth
em src
, criar um módulo em /auth
o seus respectivos service
e controller
:
nest g module auth
nest g service auth
nest g controller auth
Com os arquivos criados, vamos configurar o auth.service.ts
, desta forma:
//auth.service.ts
import {
Injectable,
NotAcceptableException,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
@Injectable()
export class AuthService {
constructor(
private readonly usersService: UsersService,
private jwtService: JwtService,
) {}
async validarUsuario(username: string, password: string): Promise<any> {
const user = await this.usersService.findOneByEmail(username);
if (!user) {
throw new UnauthorizedException('Usuário ou Senha Inválidos');
}
if (user.password === password) {
return await this.gerarToken(user);
}
throw new UnauthorizedException('Usuário ou Senha Inválidos');
}
async gerarToken(payload: User) {
return {
access_token: this.jwtService.sign(
{ email: payload.email },
{
secret: 'topSecret512',
expiresIn: '50s',
},
),
};
}
}
Curso TypeScript - Fundamentos
Conhecer o cursoCriamos dois métodos, o método validarUsuario
, como diz o nome, para validar o usuário, e o método gerarToken
. No método validarUsuario
vamos efetuar a busca pelo usuário recebendo via corpo da requisição o username
e o password
, logo em seguida, verificar se esse usuário existe e então confirmar se a senha está correta.
No método gerarToken
vamos receber o usuário caso esteja tudo ok, e então utilizaremos o jwtService.sign
, esse método faz parte do pacote do Passporte, que permite gerar tokens com diversas opções, neste caso, vamos utilizar duas propriedade:
- secret: Confirma que é a nossa aplicação que está fazendo a geração do token, caso uma aplicação terceira tente gerar um token, essa aplicação não saberá que é a chave base para a geração do token.
- expiresIn: O tempo de duração do Token, podemos selecionar um tempo para que esse token deixe de ser válido, desta forma sendo necessário gerar outro token, permitindo que não seja utilizado de forma maliciosa caso algum atacante tenha acesso a esse Token
Obs: no artigo passamos a secret direto na propriedade, porém é importante que para esses dados sensíveis sejam utilizados as variáveis de ambiente.
Outro ponto que não pode passar despercebido é que temos o método findOnByEmail
recebendo o username
como parâmetro, porém não temos ele em nosso usersService
, para isto, vamos criar um novo método de busca de usuário por email no arquivo users.service.ts
:
//users.service.ts
//[...]
findOneByEmail(username: string) {
return this.userRepository.findOneBy({ email: username });
}
//[...]
Ótimo! O arquivo auth.service.ts
está configurado.
A próxima etapa é criar o arquivo local.auth.ts
, neste arquivo vamos configurar a estratégia de autenticação do Passport, ele possibilita a configuração de várias estratégias que podem ser consultadas na sua documentação e então escolhidas conforme a sua necessidade. Neste caso vamos selecionar a estratégia local e o JWT, o arquivo local.auth.ts
ficará da seguinte forma:
//local.auth.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Com o auth.service
e local.auth.ts
configurados, precisamos adicionar os imports e providers no módulo auth:
//auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local-strategy';
@Module({
imports: [
TypeOrmModule.forFeature([User]),
PassportModule,
JwtModule,
],
providers: [AuthService, UsersService, LocalStrategy],
})
export class AuthModule {}
No módulo basicamente vamos importar o TypeOrmModule
pois precisamos utilizar a busca do usuário no banco, o PassportModule
e o JwtModule
que são os responsáveis pela autenticação. Nos providers vamos utilizar o UsersService
e o LocalStrategy
, que devem ser adicionados.
Neste momento precisamos configurar a rota no auth.controller
:
//auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller()
export class AuthController {
constructor(private authService: AuthService) {}
@Post('auth/login')
async login(@Body() body) {
return this.authService.validarUsuario(body.username, body.pass);
}
}
Portanto teremos a rota POST para /auth/login
, passando o username
e pass
via body chamando o método validarUsuario
do authService
para efetuar a validação e geração do Token.
Agora podemos testar a aplicação e gerar o Token JWT que será consumido, para testar a aplicação vamos utilizar o software Insomnia:
Fizemos a requisição POST passando via body os dados do usuário e o Token JWT foi gerado com sucesso. Podemos testar também o cenário caso não encontre o usuário ou que os dados estejam incorretos:
Curso Nest.js - Fundamentos
Conhecer o cursoConclusão
Com o NestJS podemos utilizar a biblioteca Passport do ecossistema NodeJS para trabalhar com autenticação, recurso importante que garante segurança e uma série de possibilidades para o desenvolvimento de suas aplicações. Neste momento fizemos uma introdução à autenticação para a geração de Tokens JWT, continuando a série deste artigo, veremos em breve como utilizar a proteção de rotas e autorização.
Ah, antes que me esqueça, a aplicação do projeto pode ser acessada pelo repositório do github para sanar qualquer dúvida :)
Nesse artigo criamos uma aplicação para a geração de Tokens JWT com NestJS, mas isso é apenas o começo, existem muitos outros recursos e funcionalidades disponíveis neste incrível framework, caso queira aprender mais sobre NestJS saiba que aqui na TreinaWeb nós temos o curso NestJS - Fundamentos que possui 02h07 de videos e um total de 18 exercícios. Conheça também nossos outros cursos de TypeScript.
Veja quais são os tópicos abordados durante o curso de NestJS - Fundamentos:
- Conhecendo a estrutura;
- Utilizando Nest CLI;
- Entendendo Rotas, Controllers e Views;
- Conexão com banco de dados;
- Usando TypeORM;
- Template Engine.