Autenticação com AWS Cognito no MongoDB Realm

Leticia Godoy
Legiti
Published in
9 min readMay 11, 2021
Photo by My Foto Canva on Unsplash

Olá. Me chamo Leticia, sou Frontend na Legiti. Mesmo apaixonada por React, encontrei desafios interessantes no GraphQL, o que me faz buscar aventuras nesta área ainda um tanto nova, mas muito promissora. Na Legiti trabalhamos com React JS e GraphQL, mas tivemos que fazer algumas mudanças e fomos atrás de outras soluções e foi então que nos deparamos com o Realm, do Mongo. Neste artigo vou instruí-los, ensiná-los como fazer a autenticação no Realm utilizando o AWS Cognito, que era um dos requisitos importantes para essa mudança e utilização do Realm.

A autenticação garante que as informações sejam acessadas ou alteradas somente pelas pessoas que queremos em nosso sistema. A forma mais comum é a utilização de usuário e senha.

Uma das maneiras mais usadas para autenticação em sistemas web é o JWT (JSON Web Token), que é um método para realizar autenticação entre duas partes, por meio de um token assinado que autentica uma requisição web. Esse token é um código em Base64 que armazena objetos JSON com os dados que permitem a autenticação da requisição.

Um JSON Web Token é composto por três componentes básicos: HEADER, PAYLOAD e SIGNATURE.

Header é o cabeçalho do token, que contém dois campos: “alg”, que informa o algoritmo usado para criar a hash da assinatura; e “typ” que indica que este se trata de um token JWT.

Payload é o componente onde se encontram os dados referentes à própria autenticação, como usuário, e-mail, nome, id e quaisquer outras informações que acharmos necessárias para identificar o usuário.

Signature é a assinatura única de cada token que é gerada a partir de um algoritmo de criptografia e tem seu corpo com base no header, no payload e no segredo definido pela aplicação.

O AWS Cognito é um serviço da Amazon que fornece autenticação, autorização e gerenciamento de usuários. Ao fazer a autenticação usando o SDK do Cognito, ele retorna um token JWT que, integrado a sua API, faz a decodificação, autorizando o uso da API.

MongoDB Realm é um aplicativo de back-end serverless que simplifica o acesso aos dados e a segurança sem a necessidade de boilerplates. O Realm oferece um CLI próprio e interface GraphQL.

Vamo aos passos:

Criar o banco no mongo:

Para quem for criar uma conta nova, ao fazer o sign in, o mongo já redireciona para a criação do cluster. Não tem muito segredo: você escolhe o local onde será hospedado, responde qual plano deseja (no caso usamos o sandbox que é a versão gratuita), dá nome ao cluster e pronto.

Demora alguns minutinhos para concluir mas, caso você não seja redirecionado para a criação do cluster, basta clicar no botão verde à direita, que diz “create a new cluster”.

Aêee, cluster criado! Agora vá em collections e vamos colocar alguns dados.

Como exemplo, vou criar uma database chamada “library”, com uma única collection: “books”.

A intenção é: visualizar somente os “books” do grupo a que o usuário pertence.

Preenchi o exemplo com alguns dados sem criatividade, mas vão servir!

Agora que temos nosso database feito, vamos para o Realm.

Criar o Realm:

Clique na aba do Realm.

Como só tenho um cluster, o Realm já pré-seleciona os itens para a configuração.

Aqui temos a tela inicial do Realm. Logo em cima, temos o botão “guide” para a documentação. É bem útil, com exemplos simples. O Realm oferece SDKs para utilizá-lo e a interface GraphQL, que é a que vamos usar. Não precisamos criar nenhum servidor, só configurar as permissões nas collections e campos de cada uma delas. O próprio Realm se encarrega de gerar os schemas, os resolvers e criar o endpoint para uso do GraphQL. Até o momento deste artigo, o GraphQL do Realm é um pouco limitado mas, para projetos simples, atende bem.

Liberar as tabelas:

Para configurar as collections no Realm, vamos em “rules”, no menu lateral. Repare que ele já disponibiliza os databases e collections que existem no cluster selecionado, na hora de criar o Realm. Podemos ter collections de databases diferentes no mesmo endpoint de GraphQL, mas é preciso cuidado com o nome de cada uma delas pois, se estiverem duplicados, podem dar bastante dor de cabeça.

Ps.: Podemos adicionar collections por aqui mesmo, não precisa ter dados. Mas fazer tudo manualmente é um pouco mais difícil.

Ao selecionar a collection, escolhemos o template de permissões. Já temos alguns pré configurados, mas é possível fazer todo personalizado, campo por campo. É bem versátil! Vamos colocar, primeiro, que os usuários podem ler todos os dados. Para configurar write, update e delete, é da mesma forma, sem segredos.

Assim que selecionamos o template “users can only read all data”, vamos para essa tela. Desta forma que está, todos os campos estão liberados para todos os usuários. Você pode alterar a listagem dos campos adicionando cada um deles, um por um, no botão “add fields”. Para ficar mais divertido, não quero que o usuário veja o grupo que está na collections.

Dessa forma, os únicos campos que podem ser visualizados são o “_id”, “name” e “author”.

Ah, não se esqueça de clicar em salvar! Agora vamos ao Schema.

Esse é o schema que irá gerar o type e o próprio schema do Mongo. Como temos dados na collection vamos gerar automaticamente.

A auto geração de schema pega uma certa quantidade de dados, faz a leitura e monta o schema. Por padrão ele pega os 100 primeiros registros, mas é possível filtrar usando o advanced.

Mas por que isso? Pense que você tem muitos registros, mas nem todos preenchem os campos corretamente. Você pode inserir um registro de teste com todos os campos preenchidos e gerar o schema somente a partir dele.

Após a geração do schema tem uma aba de validação, que confere os registros na collection com o schema gerado. Caso algum tenha vindo com type diferente, é possível forçar o type no schema gerado também.

Schema gerado e validado. Agora é só clicar em “review & deploy”.

Testar com o GraphQL:

Dentro do Realm temos a guia “GraphQL” que, basicamente, serve para testarmos as queries e mutations. Mas tem um detalhe: o playground do Realm não aplica regras de permissões. Portanto, se você quiser testar queries relacionadas a isso, ou que é importante que sejam filtradas, por ali não vai funcionar.

Como alternativa, usamos o “Insomnia” para testar as queries e as permissões. Para usar o “insomnia”, basta copiar o endpoint que o Realm disponibiliza na aba do GraphQL mesmo. Mas como não temos nenhum autenticador configurado, mesmo anônimo, temos o erro de “acesso não permitido”.

Criar a autenticação com custom JWT:

Para criar a autenticação, vamos primeiro na aba “Authentication”.

O Realm fornece seu próprio autenticador, com e-mail e senha, e ainda permite que você crie a collection de usuários com outro campos personalizados. Além disso, dispõe outras formas de autenticação, como conta do Facebook, Google, entre outros.

Para usar o AWS Cognito vamos usar o custom JWT Authentication.

O Custom JWT Authentication possui 2 modos de autenticação:

  • Pela chave de segurança.
  • E pela URI de configuração do autenticador.

Para o Cognito vamos fazer da seguinte forma:

  • Obviamente temos que ativar o provider.
  • Selecionar o JWK URI.
  • Colocamos o link.
  • Colocamos o hash de Audiance.
  • Salvar e dar o deploy.

JWK URI.: como pegar esse link?

Você cria, ele já existe. No caso, é só substituir o region-name e user-poll do seu cognito. https://cognito-idp.<region-name>/<user-poll-id>/.well-known/jwks.json

Audience: onde achar isso?

O Hash de audience é “client_id” do Cognito.

Depois de salvo, não esqueça do deploy. Fazendo isso, gere o JWT, vá no insomnia> headers, e cole o JWT.

Detalhe: o Realm não usa o header padrão “Authentication: Bearer <JWT>”. O nome do header é “jwtokenstring” e não usa o Bearer.

Testamos as queries e…pronto! Temos os dados vindo corretamente.

Lembre-se que não autorizamos a leitura do campo “group”, portanto ele veio nulo. Mas não removemos ele do schema (o que é possível, apagando o campo lá no JSON gerado), portanto ele ainda aparece no schema do GraphQL sendo possível selecioná-lo.

Criando as permissões:

Agora sim vamos filtrar por grupos! Voltamos para a página de custom JWT e vamos mexer nos Metadados e, agora, depende de como seu Cognito está configurado. No meu caso uma das coisas que vem no JWT é um custom rule do cognito que chamei de group. Se você decodificar o JWT, ele aparece com o seguinte nome: “custom:group”. Para ficar mais fácil, e como JSON não aceita: como nome de campo, renomeei somente para group. Não esqueça de salvar e dar deploy.

Agora que já estamos lendo esse atributo que vem no JWT, vamos adicioná-los às regras. Na aba “rules”, já temos a primeira regra de permissão que, no caso, autoriza todos a lerem os dados. Vamos alterá-la, clicando no lápis.

Aqui temos a tela de configuração das regras. Podemos colocar um nome, já que podem existir várias regras. Apply-When é o JSON de configuração, na documentação do Realm tem vários casos de como usar. Document-level permissions é o que essa regra permite (inserir, deletar, fazer busca nos campos).

Como queremos que retornem somente os documentos do grupo de cada usuário, vamos filtrar por grupo (que está no JWT).

Group”: campo em nosso schema onde está a informação a ser comparada.

%%user”: dados do usuário que retornam do JWT, mesmo sendo de outros providers. Para acessar os dados que configuramos no metadados do nosso custom JWT usamos “%%user.data.<nome do campo>”.

Aqui, você pode filtrar por igual, maior, menor, e todas as outras operações disponíveis na documentação.

testar no insomnia

Depois do deploy com as regras, voltamos ao insomnia. Primeiro o teste com o jwt gerado para um usuário do group1.

Repare na listagem dos livros, como reduziu:

E agora com um JWT gerado para um usuário do group2:

E é isso. Espero ter ajudado. =D

--

--

Leticia Godoy
Legiti
Writer for

Frontend Engineer. React and GraphQL enthusiast.