Ruby on Rails + Knock + Auth0: autenticação na nuvem

Uma característica comum de aplicações web profissionais é o uso de serviços externos para autenticação. Diversos motivos levam a isso, como por exemplo o amplo tool box ofertado pelas soluções disponíveis no mercado (verificação de email, Multi Factor Authentication, Passwordless Authentication, etc).

A integração desse serviço numa aplicação pode ser um desafio. Esse será o assunto abordado neste artigo, especificamente na parte de autenticação numa aplicação Rails API only.

O que vamos criar

Neste tutorial criaremos uma aplicação Rails API contendo um serviço de autenticação externa do Auth0 e utilizando como suporte a gem Knock.

Desenvolveremos de forma simples as funcionalidades de sign in e sign up utilizando os endpoints do Auth0. Também será detalhado como utilizar a gem Knock para realizar a autenticação do usuário com o que foi retornado pelo Auth0.

Porque utilizar o Auth0?

A escolha do Auth0 se deve por ser um serviço conhecido e bastante utilizado, pois possui um plano free muito atrativo (https://auth0.com/pricing) mesmo para aplicações que lidam com alguns milhares de usuários.

O Knock por sua vez foi implementado idealizando o suporte à integração com o Auth0, agilizando o trabalho do desenvolvedor. Existe pouca informação sobre como fazer isso, o que traz uma complexidade desnecessária para tarefa e justifica a criação deste artigo.

 

 

Dependências

  • – Uma conta free no Auth0
  • – Ruby (2.6.3)
  • – Ruby on Rails (5.2.3)

 

 

Desenvolvimento

 

Configurando a aplicação no Auth0

  1. Para começar, crie uma conta free no Auth0.
  2. Localize e entre na opção Applications
  3. Clique em Create application e selecione o tipo Machine to Machine Application. (O tipo de aplicação determina os parâmetros que poderemos configurar.)
  1. Depois selecione a api Auth0 Management API, clique em Select All e em Authorize

Utilizaremos a autenticação clássica através do password e esta opção pode estar desabilitada em sua aplicação.

  1. Certifique-se de ativar a autenticação por password acessando: página da sua aplicação > Settings > Advanced Settings > Grant Types
  2. Habilitando o checkbox Password e em seguida salvando a alteração realizada.
  1. No canto superior direito, clique no ícone do seu usuário e depois em Settings
  2. Em API Authorization Settings localize o campo Default Directory, preencha ele com Username-Password-Authentication e clique no botão salvar.

Gerando nosso projeto

1 – Crie uma aplicação Rails API-only rodando o seguinte comando:

2 – Crie o banco de dados executando no terminal:

3 – Para representar o usuário logado, crie um model chamado User

4 – A migration gerada deve ser completada com o seguinte conteúdo:

5 – Em seguida, execute crie a tabela users no banco de dados:

6 – O arquivo user.rb deve ser complementado com o seguinte:

(O campo auth0_uid nada mais é do que o ID do nosso usuário na conta do Auth0.)

 

Setando as variáveis de ambiente

1 – Para utilizar variáveis de ambiente adicione a gem dontenv-rails ao grupo de desenvolvimento:

2 – Instale rodando:

Obs: Por questões de boa prática, caso deseje, liste o arquivo .env em seu .gitignore

3 – Crie um arquivo chamado .env e adicione as seguintes variáveis:

Obs: Com exceção da AUTH0_RSA_DOMAIN, todas informações se encontram na página da aplicação criada no Auth0. Veja elas na aba Settings (AUTH0_CLIENT_SECRET, AUTH0_CLIENT_ID e AUTH0_CLIENT_ID) e APIs (AUTH0_AUDIENCE, descrito na página como API IDENTIFIER).

4 – Neste artigo você utilizará a API padrão da aplicação, nomeada como Auth0 Management API.

Sua AUTH0_RSA_DOMAIN estará no path padrão do Auth0 (/.well-known/jwks.json)Exemplo: https://rwehresmann.auth0.com/.well-known/jwks.json.

 

Configurando a gem Knock

1 – Adicione ao Gemfile as gems Knock e Http:

 

A gem http é apenas minha escolha pessoal para facilitar chamadas HTTP que serão necessárias dentro da nossa API.

2 – Dando continuidade, no terminal, instale as gems executando bundle install e em seguida geraremos o arquivo de configuração do Knock com:

O arquivo de configuração knock.rb lhe traz como comentários as configurações padrões da gem e brevemente aponta configurações alternativas. Sem mais delongas, tenha em mente que a gem não recebe atualizações há alguns anos e portanto alguns exemplos comentados estão defasados.

3 – Substitua o conteúdo desse arquivo pelo seguinte:

 

Em maiores detalhes:

  • config.token_secret_signature_key: obrigatório utilizarmos a chave de assinatura da nossa aplicação no Auth0 para todos ambientes com exceção do de teste . Em ambientes de teste não queremos realizar nenhuma chamada externa para a executar a autenticação. Portanto, mantive a opção padrão do Knock que é a leitura do que quer que esteja definido nas credentials do Rails OBS: não serão abordados testes automatizados com Knock neste exemplo, mas a ideia aqui é chamar a atenção de que para ambiente de teste a configuração é diferente.
  • config.token_audience: obrigatório utilizarmos o identificador da API da nossa aplicação no Auth0;
  • config.token_signature_algorithm: a configuração padrão nos traz o algoritmo HS256, mas a aplicação no Auth0 utiliza o algoritmo RS256;
  • config.token_secret_signature_key: obrigatório utilizarmos o secret token da nossa aplicação no Auth0;
  • config.token_public_key: o algoritmo RS256 é um algoritmo assimétrico, o que significa que ele requer tanto de uma chave privada como uma pública. A chave pública fica no domínio que definimos em uma variável de ambiente, e o restante do código neste trecho simplesmente trata de extrair a chave pública de lá.

 

Criando nossos wrappers para as chamadas de sign up e sign in

Para este artigo, optei pela escolha de criar o signup.rb e o signin.rb dentro da seguinte estrutura de pastas: app/lib/auth0. A implementação de ambos é simples, e seu funcionamento depende apenas de informar o email e senha do usuário em questão.

1 – Crie o arquivo signup.rb em app/lib/auth0 e coloque:

2 – Crie o arquivo signin.rb em app/lib/auth0 e coloque:

 

Maiores detalhes sobre a gem http podem ser consultados na sua documentação. Os endpoints e formato de chamadas para o Auth0 estão disponíveis aqui. Agora testaremos essas classes no rails console.

3- Inclua lib aos autoload_paths adicionando ao arquivo config/application.rb

4 – Para testar a autenticação abra o rails console:

5 – Rode o seguinte comando para testar o SignUp:

Perfeito! O usuário foi criado e nós também podemos confirmar isso visualmente na conta do Auth0.

(Numa situação real, você provavelmente criaria uma classe que utilizaria esse wrapper de signup e adicionaria uma lógica para criar o usuário no seu banco de dados, salvando o ID retornado pelo Auth0. Neste artigo não trataremos dessa implementação, então crie o usuário manualmente.)

6 – Com o email e ID retornados crie um usuário no banco de dados rodando:

OBS: Não esqueça de utilizar o email e o id retornado para você ao invés dos utilizados neste exemplo!

Note o formato do ID exemplificado acima. O que de fato representa o ID do usuário no Auth0 seguirá o formato provider|ID. Em caso de dúvidas, é possível consultar essa informação na página do usuário no Auth0, buscando por user_id na seção Identity Provider Attributes.

7 – Rode o seguinte comando para testar o SignIn:

Funcionou! Agora temos em mãos o meu token que será utilizada para autenticação na API Rails.

 

Alterando a implementação do Knock

Ao implementar qualquer tipo de autenticação numa aplicação Rails, acaba-se deparando com a necessidade de utilização de um método comum normalmente chamado de current_user (que lhe retorna o usuário logado). O Knock possui este mesmo método, e é necessário alterar alguns detalhes em sua implementação interna para que a integração com o Auth0 funcione.

1 – Adicione o seguinte em seu knock.rb:

 

Pontos chaves desta alteração:

  • Na implementação descrita nesse artigo, para facilitar a compreensão nesse ponto, entenda que entity_class.find_by será o equivalente a User.find_by(key_to_find => @payload['sub']) ;
  • @payload conterá informações extraídas do token (JWT), e sub é a entidade à quem o token pertence, normalmente representado por um ID (foge do escopo deste artigo explicar em maiores detalhes o JWT, mas para maiores informações você pode ler artigos como esse aqui);
  • Knock::AuthToken.class_eval nos permite reescrever a classe entity_for do módulo Knock::AuthToken, e é importante salientar que para que a alteração tenha efeito, isso precisa ser feito antes de qualquer comando include deste módulo (como neste exemplo, dentro do knock.rb que é um initializer).

Com isso, agora a implementação do Knock está pronta para trabalhar com o Auth0. A implementação original do método reescrito pode ser consultada aqui.

Perceba que a alteração tem como propósito, basicamente, realizar um find_by(auth0_uid: @payload['sub']) ao invés de usar um simples find. Isso porque o nosso ID do model User está sendo tratado como um número incremental, e não o ID retornado pelo Auth0, que neste caso estaremos salvando no campo auth0_uid.

 

Autenticando na API Rails

Vamos ao passo final da integração.

1 – Adicione o módulo de autenticação do Knock ao application_controller.rb:

2 – Crie o controllerhome_controller.rb com a action index, adicionando o callback para a autenticação do usuário:

3 – Adicione a rota para action implementada:

Por fim, testaremos a autenticação na API.

Neste exemplo utilizaremos o comando curl do Linux para realizar as chamadas a API Rails, mas você pode perfeitamente realizá-las utilizando algum software como o Postman.

4 – Num terminal, inicie o servidor com:

5 – Em outro terminal, tente realizar uma chamada sem autenticação de usuário:

O retorno foi o status 401 (unauthorized).

6 – Agora, no rails console, execute uma chamada para obter o token de autenticação do Auth0:

7 – Na sequência execute uma requisição igual ao comando abaixo.

Não se esqueça de sbustituar SEUTOKEN pelo access_token retornado anteriormente

8 – Pronto! Foi retornado o usuário autenticado.

 

Dica bônus: investigando erros no Knock

A implementação da versão do Knock utilizada neste artigo não nos da muitas pistas sobre o motivo de receber o erro *401 (Unauthorized)* do Auth0. No entanto, podemos realizar uma pequena mudança online dating free dating site para evidenciar o erro.

1 – No knock.rb, adicioe o seguinte código:

 

Não se preocupe em entender tudo o que está acontecendo neste método. A implementação original do método pode ser consultada https://onebitcode.com/dating-site-in-new-york-city/, e a alteração realizada apenas removeu uma estrutura de begin rescue utilizada na chamada do entity_for, forçando assim que vejamos o raise error do Knock::AuthToken acontecer, quando acontecer.

 

Conclusão

Este artigo exemplificou como realizar, em alguns passos, a integração de uma API Rails com o serviço de autenticação Auth0, utilizando como suporte a gem Knock.

A falta de documentação da gem Knock exemplificando como utilizá-la em conjunto com o Auth0 pode desencorajar sua utilização para este propósito e até mesmo frustrar uma tentativa. Ter o conhecimento de que alguns pontos da implementação interna do Knock precisam ser alterados para realização da integração podem assustar ainda mais. No entanto, com as informações entregues acima todo o processo se torna relativamente simples e lhe entrega o Knock num estado funcional para ser utilizado com o Auth0.

Conclui-se então que a implementação realizada neste artigo pode ser um guia sobre como integrar um serviço externo para autenticação nos seus projetos.

O código-fonte desenvolvido está disponível no fort lauderdale single over 50 dating sites.

 



12 formas de vencer o bloqueio criativo e escrever textos memoráveis (e 6 dicas extras)

Não perca nenhum conteúdo

Receba nosso resumo semanal com os novos posts, cursos, talks e vagas o/



 


Primeira vez no OneBitCode? Curtiu esse conteúdo?
O OneBitCode tem muito mais para você!


O OneBitCode traz conteúdos de qualidade, e em português, sobre programação com foco em Ruby on Rails e também JavaScript.
Além disso, aqui sempre levamos à você conteúdos valiosos sobre a carreira de programação, dicas sobre currículos, portfólios, perfil profissional, soft skills, enfim, tudo o que você precisa saber para continuar evoluindo como Programador(a)!

Fique por dentro de todos os conteúdos o/

 

Nossas redes sociais:

📹 • https://youtube.com/Onebitcode [Live todas as terças-feiras às 19h)
💻 • https://linkedin.com/company/onebitcode
🙂 • https://facebook.com/onebitcode
 📱  • https://instagram.com/one_bit_code
🐦 • https://twitter.com/onebitcode

 

Nossos cursos:

🥇 • Programador Full Stack Javascript em 8 Semanas
💎 • Curso Completo de Ruby
 ⚙  • Minicurso: API Rails 5 Completo
🐞 • Minicurso de Testes para Ruby on Rails com RSpec

 

Espero que curta nossos conteúdos e sempre que precisar de ajuda, fala com a gente!
E
stamos aqui para você 🙂

Bem-vindo à família OneBitCode o/

5 3 votes
Article Rating
janeiro 17, 2020
Subscribe
Notify of
guest
0 Comentários
Inline Feedbacks
View all comments

Feito com s2 por OneBitCode

0
Would love your thoughts, please comment.x
()
x