No artigo Autenticação: Geração de Token JWT com NestJS e Passport aprendemos a gerar Tokens JWT utilizando o NestJS e a biblioteca Passport. Agora vamos dar continuidade em relação à autenticação com NestJS e Passport.
Vale ressaltar que este artigo será uma continuação do artigo citado acima, portanto, é importante que você tenha o ambiente e a aplicação exemplo funcionando. Você pode baixar a aplicação no repositório do GitHub.
Curso Node.js - Fundamentos
Conhecer o cursoCriando rota lista de usuários
Exemplificando, vamos criar uma rota onde há uma lista de usuários, esta rota irá retornar esta lista, porém, somente usuários autenticados terão acesso a essa rota. Para isto vamos criar o método findAll()
dentro do users.service.ts
, este método irá retornar um JSON com alguns usuários, conforme abaixo:
//users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
create(createUserDto: CreateUserDto) {
return this.userRepository.save(createUserDto);
}
findAll() {
return {
usuarios: [
{
nome: 'Paulo',
endereco: 'av paulista',
telefone: '1155555555',
},
{
nome: 'Maria',
endereco: 'av faria lima',
telefone: '1155555580',
},
{
nome: 'Samantha',
endereco: 'av paulista',
telefone: '1155555590',
},
],
};
}
async findOneByEmail(username: string) {
return await this.userRepository.findOneBy({ email: username });
}
}
Agora vamos criar a rota no users.controller.ts
:
//users.controller.ts
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { AuthGuard } from '@nestjs/passport';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
async create(@Body() createUserDto: CreateUserDto) {
return await this.usersService.create(createUserDto);
}
@Get()
@UseGuards(AuthGuard('jwt')) // Decorator responsável pelo Guard
listarUsuarios() {
return this.usersService.findAll();
}
}
Criamos uma rota GET e retornamos a lista de usuários, porém há uma particularidade, o uso do decorator @UseGuards()
, é necessário usar este decorator para passar os Guards que serão responsáveis por verificar as regras de autenticação e autorização desta rota.
Curso TypeScript - Fundamentos
Conhecer o cursoGuards
O recurso “Guards” como o nome diz, são “Guardiões”, é o recurso que irá verificar se o usuário está autenticado para a rota em específico.
É importante saber que podemos criar guards específicos para cada necessidade. Neste caso o passport disponibiliza o AuthGuard
, onde podemos apontar a estratégia que estamos utilizando, no caso JWT conforme exemplo acima.
Com a rota e os guards devidamente configurados precisamos atualizar o arquivo local.auth.ts
. Neste momento é necessário fazer uma refatoração já que estamos utilizando a estratégia JWT para a autenticação. Devemos trocar o nome do arquivo para jwt-strategy.ts
e o nome da classe para JwtStrategy
. Em seguida, faremos algumas alterações conforme abaixo:
//jwt-strategy.ts
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JwtPayload } from './jwt-payload';
import { UsersService } from 'src/users/users.service';
import { User } from 'src/users/entities/user.entity';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(private usersService: UsersService) {
super({
secretOrKey: 'sua-chave',
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
});
}
async validate(payload: JwtPayload): Promise<User> {
const { email } = payload;
const user = await this.usersService.findOneByEmail(email);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Obs.: Confira onde estava a importação do LocalStrategy para atualizar para JwtStrategy.
JWT Strategy
Nós fizemos algumas alterações importantes, passamos como parâmetro a estratégia JWT no PassportStrategy
. Vale salientar que devemos prestar atenção no import correto, o Strategy
deve ser importado da biblioteca passport-jwt
, caso seja importado de uma biblioteca referente a outra estratégia teremos um erro na autenticação.
No super()
, temos três propriedades, secretOrKey
que deve ser a mesma chave que utilizamos ao gerar o Token JWT, jwtFromRequest
aqui é onde iremos passar o Token na requisição, utilizando padrão Bearer Token no Header, e por último para que o Token expire no tempo determinado na criação do mesmo.
Devemos passar o payload no método validate()
, que é o que passamos no corpo da requisição onde geramos o token relacionado a autenticação, esse payload é composto pelo email do usuário, em outros casos pode também possuir outros parâmetros. O passport também gera automaticamente as propriedades iat
e exp
que são, respectivamente, sobre a criação do token e expiração. Você pode consultar o site jwt.io e ver exemplos até criar tokens para ficar mais claro como eles são gerados.
Perceba que o payload é do tipo JwtPayload
, mas não temos nenhuma referência ainda a ele, portanto vamos criar uma interface, que terá como propriedade o email. Assim, vamos mudar um pouco a estrutura da aplicação, onde vamos criar o diretório strategies
, dentro do diretório auth
, nele vamos adicionar o arquivo jwt-strategy.ts
e criar o arquivo jwt-payload.ts
.
//jwt-payload.ts
export interface JwtPayload {
email: string;
}
A estrutura da pasta auth
ficará da seguinte forma:
Agora vamos conferir se os arquivos auth.module.ts
e users.module.ts
estão devidamente configurados conforme abaixo:
//auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './strategies/jwt-strategy';
@Module({
imports: [TypeOrmModule.forFeature([User]), JwtModule.register({})],
controllers: [AuthController],
providers: [AuthService, UsersService, JwtStrategy],
})
export class AuthModule {}
//users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from 'src/auth/auth.service';
import { JwtStrategy } from 'src/auth/strategies/jwt-strategy';
@Module({
imports: [TypeOrmModule.forFeature([User]), JwtModule.register({})],
controllers: [UsersController],
providers: [UsersService, AuthService, JwtStrategy],
})
export class UsersModule {}
Testando proteção de rotas com Token JWT
Podemos testar a autenticação utilizando o insomnia, primeiramente vamos gerar o token na rota http://localhost:3000/auth/login
, lembrando que é uma rota POST.
Agora vamos acessar a rota de listagem de usuários (http://localhost:3000/users
), para isso precisamos copiar o token e passar na requisição, lembrando que é uma rota GET:
Selecionaremos Bearer na segunda aba, adicionaremos o Token no campo respectivo e clicar em Send. Veja que obtemos a lista de usuários como retorno, porém caso o token esteja expirado, incorreto ou inexistente, retornará o seguinte erro:
O usuário terá acesso às rotas que necessitam de autenticação somente enquanto o Token for válido. Outro ponto que devemos nos atentar é o conceito de autorização, pois o usuário pode ter gerado um token, mas uma rota determinada pode ser acessada por um tipo de usuário específico, por exemplo, veremos sobre este assunto no próximo artigo.
Curso Nest.js - Fundamentos
Conhecer o cursoConclusão
Nos aprendemos no artigo anterior a gerar o Token JWT com o NestJS e a biblioteca Passport, já neste criamos uma rota protegida, necessário então a autenticação por um Token JWT válido. Esses são alguns conceitos e recursos que podemos utilizar para criar aplicações mais robustas e seguras, outros pontos que vamos ver nos próximos artigos serão sobre autorização e “refresh tokens”. Desta forma abrangendo outros aspectos de uma aplicação que utiliza estes recursos.
O código do exemplo pode ser consultado neste diretório do github :)
Por fim, caso queira aprender mais sobre NestJS saiba que aqui na TreinaWeb temos o curso Nest.js - Fundamentos que possui 02h07 de vídeos 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 Nest.js - Fundamentos:
- Conhecendo a estrutura;
- Utilizando Nest CLI
- Entendendo Rotas, Controllers e Views;;
- Conexão com banco de dados;
- Usando TypeORM;
- Template Engine.