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:
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
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..
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.
E para facilitar nossa vida, vamos fazer uma propriedade get para recuperar o valor do token mais facilmente.
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.
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.
E ai la no HTML a gente faz isso
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:

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.

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.