Construindo uma API com NestJS, PostgreSQL e Docker — Parte 2: Autenticação e Autorização

Iago Maia Silva
7 min readFeb 26, 2020

--

NestJS — Um framework Node.js para construção de aplicações confiáveis e escaláveis.

Recentemente comecei a estudar Node pensei em desenvolver uma API com funcionalidades básicas, que praticamente todo projeto precisa, como forma de me familiarizar com a linguagem. Para me auxiliar nessa tarefa utilizei o framework NestJS. Nesta série de tutoriais vou compartilhar o que aprendi.

Série completa:

Irei atualizando essa lista com links para as devidas partes conforme eu for postando!

Requisitos

O código da etapa anterior se encontra em um repositório no meu GitHub:

Você também pode ler o artigo onde descrevi a criação desse código aqui.

TL;DR

O código desta etapa também se encontra em um repositório no meu GitHub:

Autenticação e Autorização

Quase sempre um dos requisitos de um projeto é que certos recursos do sistema estejam disponíveis apenas para aqueles usuários que possuem um cadastro. E, indo mais além, alguns recursos só podem ser acessados por usuários cadastrados e com autorizações especiais.

E é sobre isso que esse tutorial irá tratar.

Autenticação via JWT

JWT (JSON Web Tokens) é o método de autenticação mais utilizado em APIs, uma vez que como não há uma sessão entre cliente e servidor, a autenticação deve ser feita a cada requisição. Como seria inviável e inseguro enviar nome de usuário e senha junto com cada requisição, o JWT foi uma das maneiras encontradas para se resolver esse problema. É possível aprender mais sobre JWT aqui.

Para introduzir autenticação à nossa API, vamos criar um módulo auth.

$ nest g module auth

A primeira coisa que iremos fazer nesse módulo é introduzir um endpoint para que um usuário consiga se cadastrar no sistema como usuário comum. Para isso podemos reaproveitar muita coisa que já foi feita na parte 1 desse tutorial, onde criamos um endpoint para criação de um usuário administrador.

Vamos adicionar um Controller e um Service ao nosso módulo

$ nest g controller auth --no-spec
$ nest g service auth --no-spec

Vamos agora adicionar ao nosso módulo o repositório de usuários, uma vez que precisamos do método de criação de usuários que criamos anteriormente. Nosso auth.module.ts vai ficar dessa forma:

auth.module.ts

Com o nosso UserRepository adicionado podemos criar nosso método para criação de um usuário comum em auth.service.ts:

auth.service.ts

Repare como importamos as classes CreateUserDto, User e o enum UserRole do módulo de usuários, reaproveitando o que já havia sido feito.

Agora basta chamarmos esse método através do auth.controller.ts:

auth.controller.ts

Vamos inicializar nossa API e testar nosso endpoint no Postman:

$ npm run start:dev
Requisição para criação de um novo usuário

Ao enviar essa requisição, você deve receber uma mensagem informando que a criação de usuário foi bem sucedida. Vamos agora à criação do nosso endpoint de login.

Primeiro, vamos criar um DTO representando as credenciais de login:

credentials.dto.ts

Vamos agora adicionar no user.entity.ts o método responsável por conferir a senha de um usuário:

user.entity.ts

No nosso users.repository.ts, dentro do módulo users vamos acrescentar um método para verificação das credenciais de um usuário:

users.repository.ts

Vamos adicionar o método de login ao auth.service.ts:

auth.service.ts

Precisamos agora gerar o JWT para nosso usuário recém autenticado. Para isso precisaremos adicionar mais alguns pacotes ao nosso projeto:

$ npm i --save passport passport-jwt @nestjs/jwt @nestjs/passport

Com isso podemos agora registrar o módulo JWT dentro do nosso auth.module.ts:

auth.module.ts

Agora podemos finalizar nosso método de login no auth.service.ts:

auth.service.ts

Vamos agora incluir esse endpoint no auth.controller.ts:

auth.controller.ts

Vamos inicializar o servidor e testar o login com o Postman:

$ npm run start:dev
Requisição de login com o usuário criado anteriormente

Devemos receber então nosso JWT:

JWT

Agora como podemos testar se nosso método de autenticação funciona? Vamos criar um endpoint para que um usuário autenticado consiga obter seus próprios dados de cadastro da API.

Primeiramente, vamos criar nossa estratégia de validação, criando o arquivo jwt.strategy.ts dentro do módulo auth:

jwt.strategy.ts

Basicamente o que fizemos aqui foi criar uma estratégia para autenticação JWT onde, ao identificarmos um token no cabeçalho da requisição, vamos validá-lo e extrairmos dele o id do usuário que enviou a requisição. Com o id do usuário nós buscamos os dados referentes a ele no banco de dados e retornamos essa entidade ao final da validação do token.

Vamos agora deixar essa nossa estratégia visível para todo o módulo, além de exportá-la para outros módulos utilizarem quando necessitarem de autenticação. Para isso modificaremos o auth.module.ts:

auth.module.ts

Repare que além de importarmos a estratégia para nosso módulo auth nós também importamos um novo módulo, o PassportModule, que será o responsável por adicionar a funcionalidade de proteger um endpoint de usuários não autenticados.

Vamos agora adicionar o endpoint ao auth.controller.ts:

auth.controller.ts

Usamos agora um novo decorator, o @UseGuards(). Ele é o responsável por incluir Guards a um endpoint, que nada mais são do que uma forma de restringir o acesso ao recurso. Nesse caso utilizamos a AuthGuard(), que já nos é fornecida pelos pactes que instalamos anteriormente.

Repare que busco os dados do usuário dentro da requisição que chega ao controller. Por padrão os dados do usuário autenticado, conforme retornado pelo método validate no jwt.strategy.ts, são adicionados à requisição dentro da chave user. Para entender mais sobre Guards, pode acessar a documentação.

Podemos substituir isso por um simples decorator criado por nós. Vamos fazer isso agora, criando dentro do módulo auth o arquivo get-user.decorator.ts:

get-user.decorator.ts

Uma observação: Com o Nest 7.0.0 houve uma alteração no formato da requisição. Sendo assim, nosso get-user.decorator.ts fica assim agora:

get-user.decorator.ts

Foi uma alteração pequena na localização do user dentro da requisição. Se você clonou meu repositório para acompanhar o tutorial é muito provável que não encontrará esse problema. Mas caso esteja fazendo tudo do zero você vai se deparar com esse erro.

Em ambos os casos o decorator nada mais faz do que retornar o objeto do usuário de dentro da requisição. Podemos agora incluí-lo no auth.controller.ts:

auth.controller.ts

Vamos agora testar se conseguimos obter nossos próprios dados:

Testando o endpoint para buscar dados do usuário autenticado

Atenção ao detalhe importante: Na aba “Authorization” devemos configurar o tipo de autorização para “Bearer Token” e depois devemos informar nosso token no campo indicado. O token informado deve ser aquele obtido ao se realizar uma requisição para o enpoint de login.

Convém agora também protegermos nosso endpoint de criação de usuários administradores para que apenas usuários autenticados possam criar novos administradores:

users.module.ts
users.controller.ts

Primeiro nós adicionamos o módulo do Passport ao nosso módulo users adicionando o import do módulo no nosso users.module.ts.

Feito isso adicionamos o decorator @UseGuards() no endpoint de criação de usuários administradores, passando o guard AuthGuard() como parâmetro. Vamos agora tentar criar um administrador sem estarmos autenticados:

Tentativa de criar um administrador sem estar autenticado

Recebemos um erro com código 401, que é o código padrão para informar que o usuário não está autenticado. Mudando o tipo de autenticação para “Bearer Token” e incluindo o token:

Sucesso ao criar um administrador após utilizar autenticação

Com isso concluímos a parte de autenticação deste tutorial.

Autorização

Vamos analisar nosso endpoint de criação de usuários administradores:

Não seria interessante que apenas administradores pudessem criar outros administradores? Afinal, não queremos que nossos usuários comuns saiam alterando informações sensíveis do sistema.

Para isso precisamos realizar uma validação de autorização.

Diferente da autenticação, que é um meio do usuário provar que ele é quem ele diz ser, a autorização é o que define o que um usuário, já autenticado, pode ou não fazer.

Para implementar um sistema de autorização vamos seguir a recomendação da própria documentação do NestJS e utilizar Guards. Dentro de auth vamos criar a guard roles.guard.ts:

roles.guard.ts

Repare que estamos extraindo o perfil de usuário (role) de dentro do nosso contexto da requisição. Para que isso funcione precisamos colocar essa informação lá antes de realizarmos essa verificação. Vamos criar um decorator para isso, também dentro de auth:

role.decorator.ts

A função desse decorator é bem simples: ele irá receber como parâmetro um perfil de usuário armazenar ele nos metadados.

E com isso nosso sistema de autorização por perfil já está pronto. Para testar vamos adicionar nossa RolesGuard ao nosso endpoint de criação de usuários administradores:

users.controller.ts

Vamos agora fazer login através do Postman com um usuário que não seja administrador e, utilizando o token obetido para autenticação, vamos tentar criar um usuário administrador:

A requisição falhou e retornou um status 403, indicando que o acesso a esse recurso é proibido

Agora se fizermos login com um usuário administrador e tentarmos novamente:

Requisição obteve sucesso

E com isso encerramos a parte de Autenticação e Autorização. Na próxima parte da série vamos finalizar nosso CRUD de usuários.

--

--