Como eu integrei o Rails 5 API + JWT + Angular - Parte 1

Antes de mais nada verifique se você possui o Rails 5 instalado em sua máquina, como o foco é mostrar a integração Rails + Angular não irei mostrar o processo de instalação e configuração do Ruby + Rails 5, para darmos início vamos fazer a inicialização do projeto Rails 5 com o comando:

$ rails new my_api --api
Tudo Ok! Vamos lá!

Note que usamos o parâmetro --api, ou seja, criamos o projeto sem a camada das views, pois iremos usar o Angular para isso. O próximo passo é instalar uma gem chamada rack-cors para integrarmos a API com o Angular, para instalar a gem basta adicionar: gem ‘rack-cors’, :require => ‘rack/cors’ no seu Gemfile em seguida digite o comando abaixo:

$ bundle install

O próximo passo é adicionar as configurações do Rack Cors no seu arquivo config.ru que se encontra na raiz do seu projeto, como no exemplo abaixo:

Agora vamos criar o arquivo config/initializers/cors.rb caso não exista, caso exista basta inserir as configurações de origins e resource, como no arquivo abaixo:

Note que o origins recebe um parâmetro ‘*’, que indica que serão aceitas requisições de qualquer host independente do seu endereço, caso queira determinar o endereço que terá acesso a sua API basta substituir o * pelo endereço que deseja (exemplo: https://google.com), já no resources é onde é determinado os métodos que sua aplicação permitirá receber, como os conhecidos POST e GET por exemplo, além de determinar quais HEADERS poderão ser recebidos por sua API e enviados dela em caso de resposta, para definir qual HEADER você terá acesso na resposta use a chave expose e no valor da chave passe um array de strings com os nomes dos HEADERS no meu caso passei o 'Jwt-Token-Renewed' no array onde usarei para renovar o JWT quando o mesmo estiver próximo de expirar na nossa aplicação Angular posteriormente.

Feito isso já temos o Rack Cors devidamente configurado, e nossa API já poderá ser acessada na nossa aplicação Angular, porém ainda teremos que instalar mais uma gem chamada jwt, instale com a gem inserindo a seguinte linha no seu Gemfile: gem 'jwt'. Em seguida execute o seguinte comando no seu terminal:

$ bundle install

Pronto! Agora nossa aplicação Rails já pode fazer o encode e o decode do JWT, porém precisamos filtrar “todas” as requests e verificar se o token foi enviado no HEADER da requisição e se o token é válido, para isso nós faremos uma série de verificações no nosso application_controller.rb, mas antes vamos gerar um secret com o comando abaixo:

$ rake secret

Após executado o comando acima será mostrado um hash, copie ele, e agora vamos criar uma nova chave no arquivo config/secrets.yml, como estamos no enviroment de desenvolvimento, vamos adicionar a chave hmac_secret dentro do development e colar o hash que copiamos anteriormente como no exemplo abaixo:

Note que os secrets da aplicação que eu criei foi substituído por “<%= ENV[“SECRET_KEY_BASE”] %>” sendo assim na sua aplicação deverá ter alguns hashs imensos ao envés disso, vale lembrar que os secrets como o nome já diz, são os segredos de sua aplicação e você não deve compartilha-los com ninguém, se você tiver uma aplicação em produção não é aconselhado deixar seus secrets dentro do repositório git, por questões de segurança deixe-os somente na máquina que você hospedou sua aplicação. Agora que salvamos nosso secret podemos resgata-los onde e quando quisermos na nossa aplicação Rails.

Voltando ao application_controller.rb, caso você não saiba o ApplicationController é o controller principal e os controllers gerados consequentemente herdarão dele, sendo assim faremos as verificações e validações necessárias no próprio, segue abaixo o application_controller.rb já pronto para ser implementado:

Note que eu usei o before_action no método jwt_auth_validation, isso significa que caso algum controller herde o ApplicationController, o método jwt_auth_validation será executado automaticamente antes de qualquer action do controller que herdou do ApplicationController, ou seja, dessa forma poderemos verificar se a request tem em seu HEADER o JWT token e se o mesmo é válido, sendo assim, permitir ou não o acesso às informações ou ações requisitadas. Nesse caso eu verifico se existe o header Authentication e faço as devidas verificações como: O token existe? É válido? Expirou? Possui um payload válido? E verifico caso falte 1 hora para o token atual expirar, que ele me gere outro token com validade renovada por mais 4 horas e me retorne no HEADER de qualquer resposta que venha a vir de qualquer controller que herde do ApplicationController, o token renovado é retornado no JWT-Token-Renewed dentro do HEADER de resposta. Preferi por as verificações no ApplicationController pelo fato de ser mais fácil de compreender o que foi feito, porém o correto seria criar um Concern e fazer um include no ApplicationController.

Bom agora que já temos o controller principal pronto, vamos criar um scaffold chamado users, no terminal digite:

$ rails g scaffold user name:text email:text password_digest:string && rake db:create db:migrate

Pronto! Agora temos nosso primeiro scaffold gerado e note que a classe UserController já herda de ApplicationController automaticamente, sendo assim todas as verificações anteriores já surtirão efeito e não conseguiremos acessar o conteúdo do UserController sem autenticar, porém ainda não temos um controller para autenticação, sendo assim vamos criar um, digite o seguinte comando no terminal:

$ rails g controller auth

Agora que já temos o controller AuthController vamos criar um método para autenticar na nossa API, e vamos retirar o filtro do jwt_auth_validation na nossa classe com o skip_before_action, pois o nosso auth precisa ser público e não deve passar pelas verificações que fizemos no ApplicationController, segue abaixo o controller:

Agora que já temos o controller vamos na rota e adicionar uma entrada para esse controller utilizando POST para enviar os dados email e password para o endpoint 'user_auth' da nossa API, abaixo segue a rota:

Ok! Já temos o controller para autenticar e scaffold gerado para User porém para criptografar o campo password no nosso DB precisaremos adicionar o seguinte código ao nosso model User:

has_secure_password

Isso fará com que a coluna password_digest gerada no DB receba somente dados criptografados, para melhor segurança e privacidade da senha do usuário, e como usamos User para autenticar na nossa API será necessário fazer isso para usar o método authenticate da classe ActiveRecord no momento do POST da nossa autenticação.

Em seguida verifique se seu gemfile possui:

gem ‘bcrypt’

Caso não possua, o insira e execute:

bundle install

Pronto! Agora vamos criar um usuário padrão usando o seeds para que possamos testar nossa autenticação na nossa API, no seu arquivo db/seeds.rb adicione o seguinte código:

Ufaaa! Agora já temos a camada inicial da API concluída!!! =D

Vamos vamos testar nossa API no postman, vamos mandar um POST na rota user_auth passando os parâmetros: {“email”:”user@email.com”, “password”:”user@myapi”} e olha só o resultado:

Token de autenticação retornado

Agora se colocarmos o token gerado na chave Authentication no Header para cada requisição da nossa API a aplicação vai saber que você já está autenticado e liberará o acesso a rota caso o token seja válido, o resultado da requisição com o token de autenticação abaixo:

Bom pessoal por hora é isso! Na segunda parte desse post faremos a integração da nossa API com o Angular 4, vou deixar o link do repositório do github para você dar uma conferida no código da aplicação.

Até breve! =)