Autenticação com Angular (Loopback 4)

Provavelmente devem existir cerca de um milhão de tutoriais de como criar um sistema de autorização em Angular. Mas mesmo assim decidi fazer o meu.

Focarei mais na ideia geral e como você deve pensar para construir um esse modelo de autenticação. Se você quer só o código, vá direto no repositório clicando aqui. Mas se você quer entender como fazer isso numa próxima vez sem tutorial nenhum, utilizando apenas as ferramentas que o Angular fornece, sugiro que leia devagar tentando absorver as ideias gerais.

Autenticação e Autorização

Autenticação é quando o usuário se autentica, ou seja, fazer o login e falar pro sistema quem é ele.

Já a autorização são as permissões que esse usuário é capaz de fazer dentro do sistema. Ele pode criar um post? Criar um comentário? Etc…Depende exclusivamente das regras de negócio.

Nesse post falarei apenas na autenticação.

Método de autenticação

Existem alguns métodos de autenticação. Um muito usado é o JWT (Json Web Token). Consiste basicamente em uma string grande que pode ser usada no header das requisições para que o servidor backend saiba quem é.

Explicando JWT: ele encoda os dados de uma forma padrão dentro de uma string. Exemplo de JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Dentro desse string estranha teremos a cabeça (header), os dados (payload) e a assinatura (signature). Podemos decodificar esse código com qualquer biblioteca em qualquer linguagem, basta procurar por jwt decode (em sua linguagem de preferência).

O mais importante é que no payload é que ficam as informações do usuário.

Implementamos uma autenticação utilizando JWT.

Backend (NodeJs e Loopback)

Antes de tudo, devo falar que estou utilizando um framework chamado Loopback 4. Ele foi criado pela IBM. Igual ao Angular, ele também utiliza de Typescript.

A vantagem do Loopback é que a IBM já implementou duas classes muito bacanas. TokenService e UserService. Com o TokenService, podemos verificar se um token é válido, além de gerar novos tokens. E com o serviço de usuário, podemos verificar se a senha realmente é a do usuário, entre outras coisas.

Um ótimo tutorial sobre como isso tudo funciona pode ser encontrado aqui.

Se você seguir o tutorial, terá uma rota esperando e-mail e senha do usuário e enquanto o retorno é um token.

E que retorna

Aqui está exemplo de um token retornado

Temos nosso backend concluído e vamos para o que realmente interessa.

Implementação da Autenticação no Angular

Aconselho que crie um modelo metal todos os passos e porque eles são importantes — para, assim, implementá-los.

O que queremos é que o usuário nos forneça um usuário e uma senha. Com esses dados, conseguimos buscar na API o token responsável por esse usuário.

Quando essa requisição for feita, deve-se guardar esse token para as futuras requisições. E como fazemos isso? O navegador fornece um local de armazenamento para cada site e podemos acessá-lo utilizando a função localStorage (claro que poderíamos usar cache ou outros métodos, mas aqui nesse tutorial vou utilizar o local storage).

É importante ressaltar que o local storage é um dicionário (chave-valor) e que não expira.

Guardando valores no localStorage

Para setar um valor no local storage podemos utilizar o método setItem. Exemplo:

localStorage.setItem(‘myCat’, ‘Tom’);

Recuperando valores no localStorage

Para recuperar algum valor, basta buscar pela chave.

const cat = localStorage.getItem(‘myCat’);

Removendo valores no localStorage

Para remover, basta dizer qual é a chave

localStorage.removeItem(‘myCat’);

Limpando todas as chaves

Para limpar:

localStorage.clear();

Com isso em mente, agora temos nossa ferramenta para guardar o token que a nossa API retornará.

Criando o serviço de autenticação

Vamos criar o serviço de usuário para autenticação. Porém, primeiro precisamos criar um model para representar esse token dentro do sistema. Entre no projeto e digite o comando

ng generate class models/UserToken

Dentro dela, crie uma propriedade do tipo string chamado Token, como na imagem a seguir:

O repositorio do codigo esta no fim do post
O repositorio do codigo esta no fim do post

Agora temos como representar o Token como objetos do tipo UserToken no nosso sistema.

Crie o UserService pelo seguinte comando:

ng generate service services/user

É gerado um serviço chamado UserService dentro de uma pasta services. Esse serviço será responsável por logar/deslogar o usuário, além de saber quem está logado.

É possível guardar o estado da aplicação (usuário logado) através do padrão Observer utilizando a biblioteca RxJs (se você não sabe do que estou falando, google it).

Para utilizar a biblioteca RxJs, precisamos criar dois objetos, um privado e outro público. O privado é de quem de fato tem o BehaviorSubject e ninguém mais a não ser os métodos que iremos implementar pode modificá-lo. O público é apenas o valor desse sujeito.

Então

O repositório do código esta no fim do post
O repositório do código esta no fim do post

Repare que o tokenSubject recebe um valor por padrão no construtor. Esse valor é buscado no localStorage. Ele verifica se existe um user-token no localStorage e faz o parse dele, jogando pra dentro do subject. Se caso não existir, ele joga um valor nulo.

Vamos fazer a injeção de dependência de dois outros objetos importantes: Router e HttpClient. O primeiro é para manipular suas rotas e o segundo para fazer requisições http.

E agora com todo o alicerce preparado, podemos criar o método de login.

Queremos fazer uma requisição do tipo post no nosso backend passando o e-mail e password recebidos como argumento pela função. Logo após isso, a gente mapeia essa resposta para um objeto do tipo UserToken, setamos no localStorage e avisamos para todos os ouvintes que esse token foi mudado (pelo método next do tokenSubject). Por fim o token é retornado..

O repositório do código esta no fim do post
O repositório do código esta no fim do post

Para o logout, precisamos primeiro remover do localStorage esse token, avisar para todos os ouvintes. Também pode-se mandar o usuário para outra rota.

O repositório do código esta no fim do post
O repositório do código esta no fim do post

E para facilitar nossa vida, vamos fazer uma propriedade get para recuperar o valor do token mais facilmente.

O repositório do código esta no fim do post
O repositório do código esta no fim do post

Com isso acabamos a implementação do nosso serviço de usuário/autenticação.

Exemplo da utilização do nosso serviço

A tela de login

Focaremos primeiro na tela de login. É uma tela de login convencional com dois inputs e um botão. Na hora que o cara submeter o formulário, o evento de onSubmit será acionado, fazendo que a propriedade loading se torne true (para acionarmos o ícone de loading) e o UserService.login é chamado.

O repositório do código esta no fim do post
O repositório do código esta no fim do post

Se der sucesso, o objeto router vai mandar o usuário pro URL que eu definir. Se não o loading para de rodar na tela e podemos apresentar algum erro pro usuário.

Deslogando usuário

Aqui é fácil. Basta injetar um objeto do UserService onde você quiser e mandar o logout(). Exemplo que pode ficar no menu do sistema.

O repositório do código esta no fim do post
O repositório do código esta no fim do post

E ai la no HTML a gente faz isso

O repositório do código esta no fim do post
O repositório do código esta no fim do post

Resultado

Tela de login

Logado e com a possibilidade agora de deslogar.

Injetando esse token nas requisições

Um dos objetivos de autenticar usuários é esconder rotas na API em que esse usuário não deve ter acesso. Procure na documentação do seu framework server-side como autenticar a rota. No Loopback 4, temos um decorator em que colocamos em cima dos controllers.

Agora essa requisição espera que você passe no header o token JWT. Para fazer isso, usaremos os Middlewares do Angular.

Middleware no Angular

Middleware é um padrão em que injetamos funções antes da execução de uma função maior.

Então, para toda requisição de saída será injetado uma função interceptadora em que é verificado se o usuário está logado ou não. Se estiver, ele coloca no header da requisição o token do usuário.

Criamos um Interceptor pelo seguinte comando

ng g interceptor interceptors/Jwt

O código será o seguinte:

O repositório do código esta no fim do post
O repositório do código esta no fim do post

No final precisamos mandar o next para que ele continue executando os outros middlewares.

Além disso, precisamos avisar para o modulo que estamos utilizando esse interceptor.

O repositório do código esta no fim do post
O repositório do código esta no fim do post
Dentro do app.module.ts

Pronto, agora toda requisição está com o token do usuário.

Considerações finais

É importante saber que o sistema de login ficou fácil assim porque o Angular por default implementa sua injeção de dependência singleton. Com isso, conseguimos fazer o login apenas uma vez e esse objeto permanecerá na memória até o fim da execução do seu projeto (quando o usuário fechar o navegador). Quando o usuário retornar, o construtor de UserService tentará puxar do localStorage. Se encontrar, automaticamente o usuário já estará logado.

Esse conceito é muito importante de entender já que a maioria dos frameworks server sides que eu já trabalhei implementam injeção de dependência transiente por default.

Outra coisa importante é entender o padrão Observer. Falando por alto ele é uma lista em que outras funções/objetos podem dizer que querem receber uma chamada quando tal ação acontecer. Então quando usamos RxJs, queremos justamente avisar para outros objetos que determinadas ações aconteceram. Muitas vezes essas ações são assíncronas, podendo eliminar algumas chamadas async/await do código.

Inclusive a biblioteca de requisições default do Angular, HttpClient, é implementada utilizando o padrão Observer, ou seja, RxJs.

Github onde está a implementação desse guia: https://github.com/carloszan/ng-auth-guide

Bom, é isso galera. Qualquer dúvida ou sugestão, deixe aqui embaixo.

Ou envie um e-mail para outras ideias.

trying to make things valuable

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store