Enfrentando a autorização do usuário no GraphQL com o AWS AppSync

Como implementar a autorização do usuário e o controle de acesso refinado em um aplicativo GraphQL usando o AWS AppSync com o Amazon Cognito e o AWS Amplify.

Se esta é a primeira vez que você usa o AWS AppSync, provavelmente recomendo que você verifique este tutorial antes de seguir adiante.

Se você já estiver familiarizado com o AWS AppSync e quiser aprofundar-se em exemplos mais complexos de autorização do usuário, confira este post recente de Richard Threlkeld .

Neste post, veremos como permitir que usuários autorizados acessem dados em uma API do GraphQL. Também mostraremos como identificar adequadamente o usuário atualmente autenticado de maneira segura no AWS AppSync, armazenando seu nome de usuário no banco de dados como seu identificador exclusivo quando criam recursos.

Embora façamos isso no contexto de um aplicativo React, as técnicas que estamos analisando funcionarão com a maioria das estruturas JavaScript, incluindo Vue, React, React Native, Ionic e Angular.

O fluxo com o qual estaremos trabalhando é assim:

  1. Como usuário, efetuamos login no aplicativo e recebemos um token de identidade.
  2. Invocamos uma consulta ou mutação GraphQL a partir do aplicativo cliente, passando o token de identidade do usuário junto com a solicitação em um cabeçalho de autorização (a identidade transmitida automaticamente pelo cliente AWS AppSync).
  3. Em nosso resolvedor, procuramos por determinados dados, em nosso caso o nome de usuário do usuário, para executar operações condicionalmente, consultar com base no usuário atual ou criar mutações usando o nome de usuário do usuário conectado no momento.
  4. A operação é executada ou rejeitada como não autorizada, dependendo da lógica declarada em nosso resolvedor.

O fluxo de dados para uma mutação pode ser algo como isto:

  1. O usuário executa uma operação GraphQL enviando seus dados como uma mutação.
  2. O resolvedor atualiza os dados para adicionar as informações do usuário que são decodificadas do JWT. O JWT é enviado no cabeçalho de autorização e está disponível no resolvedor.
  3. Os dados são armazenados no banco de dados juntamente com informações do usuário.

Neste exemplo, podemos agora consultar com base no índice do autor.

Começando

As ferramentas que usaremos para realizar isso são o AWS Amplify como nosso cliente SDK para autenticação, bem como nosso cliente GraphQL e AWS Mobile CLI para criar novos serviços do AWS Mobile Hub.

Se você ainda não estiver familiarizado com o uso do AWS Amplify com o Cognito para autenticar um usuário e quiser saber mais, confira React Authentication in Depth ou React Native Authentication in Depth .

Eu também criei um tutorial em vídeo sobre tudo o que iremos discutir neste post, para vê-lo clique aqui .

Para começar, copie o clichê que usaremos neste exemplo:

 git clone https://github.com/dabit3/appsync-react-native-with-user-authorization 

Então, cd no diretório e instale as dependências usando yarn ou npm:

 cd appsync-react-native-with-user-authorization 
 fios || npm i 

Agora que as dependências estão instaladas, usaremos o AWS Mobile CLI para inicializar um novo projeto.

Primeiro, instale o CLI do awsmobile se você ainda não o tiver instalado:

 npm i -g awsmobile-cli 

Em seguida, configure o CLI com suas credenciais corretas:

Se esta é a primeira vez que você usa a AWS, confira este vídeo para saber como obter essas credenciais e configurar a CLI.

 configuração do awsmobile 

Agora podemos criar um novo projeto:

 awsmobile init 

Você será solicitado com algumas opções de configuração, sinta-se à vontade para aceitar os padrões para todos eles ou escolha um nome de projeto personalizado quando tiver a opção.

Em seguida, adicionaremos recursos de login de usuário ao aplicativo com o Amazon Cognito:

 awsmobile user-signin enable 

Em seguida, envie a configuração atualizada para o console da AWS

 empurrão do awsmobile 

Agora, você deve poder visitar o console e ver o novo projeto. Vá para https://console.aws.amazon.com/mobilehub/home e clique no nome do seu projeto para ver sua configuração atual.

Criando a API do AWS AppSync

Agora que o nosso projeto do AWS Mobile está criado e pronto para funcionar, vamos criar nossa API do AWS AppSync.

Primeiro, acesse o AWS AppSync console visitando https://console.aws.amazon.com/appsync/home e clicando em Create API .

Criando o esquema

Selecione Custom Schema e dê um nome à sua API.

Agora que a API foi criada, clique em Configurações e atualize o tipo de autorização para ser o conjunto de usuários do Amazon Cognito . Na configuração do pool de usuários, escolha o pool de usuários que foi criado quando criamos nosso projeto do AWS Mobile usando a CLI junto com sua região e definimos a ação padrão como Permitir .

Em seguida, crie o esquema a seguir e clique em Salvar :

 type City { 
eu fiz!
nome: String!
país: String!
autor: String
}
 tipo Consulta { 
fetchCity (id: ID): cidade
}

Observe que o autor é o único campo não obrigatório.

Recursos de provisionamento

Em seguida, clique no botão Criar recursos.

Nessa tela, escolha Cidade como o tipo e crie um índice adicional com um nome de author-index de author-index e uma chave primária do author . Em seguida, role até a parte inferior e clique em Criar .

Atualizando os resolvedores

Em seguida, vamos atualizar alguns resolvedores. Primeiro, queremos garantir que, quando criarmos uma nova cidade, o nome de usuário do usuário seja armazenado no campo do autor. Esses dados de nome de usuário estão disponíveis como parte do token de identidade do usuário transmitido junto com a solicitação em um cabeçalho de autorização, e podemos acessar isso em nosso resolvedor como a identidade no campo context.identity disponível no resolvedor.

Identificando o usuário atualmente conectado em uma mutação

No campo resolvedor em Tipos de Dados de Mutação no painel, clique no resolvedor para createCity :

Atualize o modelo de mapeamento de solicitação createCity para o seguinte:

 #set ($ attribs = $ util.dynamodb.toMapValues ??($ ctx.args.input)) 
#set ($ attribs.author = $ util.dynamodb.toDynamoDB ($ ctx.identity.username))
{
"version": "2017-02-28",
"operação": "PutItem",
"chave": {
"id": $ util.dynamodb.toDynamoDBJson ($ ctx.args.input.id),
}
"attributeValues": $ util.toJson ($ attribs),
"condição": {
"expression": "attribute_not_exists (#id)",
"expressionNames": {
"#eu fiz",
}
}
}

Há algumas coisas a serem observadas:

  1. Na primeira linha de código, estamos criando um novo map / object chamado $attribs e adicionando os valores existentes como $ctx.args.input
  2. Na segunda linha de código, estamos adicionando outro campo ao objeto chamado author com o valor de $ctx.identity.username . $ctx.identity é a informação de identidade proveniente do token de identidade do usuário.
  3. No campo attributeValues , estamos passando o novo objeto $attribs que acabamos de criar.

Agora, quando criamos uma nova cidade, a identidade do usuário será automaticamente armazenada como outro campo na tabela do DynamoDB.

Permitir acesso de leitura apenas ao autor do item

Agora que temos uma maneira de identificar o usuário em uma mutação, vamos para onde quando um usuário solicita os dados, os únicos campos que eles podem acessar são os seus.

Para adicionar essa funcionalidade usando nossa configuração existente, precisamos apenas fazer uma coisa: atualizar o resolvedor listCities para consultar apenas os dados criados pelo usuário atualmente conectado.

O DynamoDB permite que você execute operações de Query diretamente em um índice. Utilizaremos isso consultando os dados da tabela usando o author-index e novamente usando o $context.identity.username para identificar o usuário.

Atualize o request mapping template listCities para o seguinte:

 { 
"version": "2017-02-28",
"operação": "Consulta",
"index": "autor-index",
"inquerir" : {
"expression": "author =: author",
"expressionValues": {
": author": {
"S": "$ {ctx.identity.username}"
}
}
}
"nextToken": $ util.toJson ($ util.defaultIfNullOrEmpty ($ ctx.args.after, null)),
}

Algumas coisas mudaram aqui:

  1. Atualizamos a operação para ser uma Query versus uma Scan . A diferença é que uma scan operação verifica a tabela inteira enquanto uma query operação de procura valores de atributos-chave única primárias e suporta um subconjunto de operadores de comparação em valores chave de atributo (no nosso caso autor) para refinar o processo de pesquisa.
  2. Adicionamos um campo de index especificando o índice no qual gostaríamos de consultar
  3. Adicionamos outro novo campo, query , definindo o valor que gostaríamos de usar como valor da consulta (no nosso caso, autor).

Agora, a API está completa e podemos começar a testá-la.

Testando o aplicativo

Você deve ser capaz de rodar o aplicativo executando react-native run-ios react-native run-android ou o react-native run-android .

Na tela de abertura, escolha “Inscreva-se” e crie um novo usuário. Confirme o novo usuário com autenticação de fator 2 (certifique-se de adicionar +1 ou o código do seu país ao inserir seu número de telefone).

Depois de se inscrever, faça login, clique em Adicionar cidade e crie uma nova cidade:

Depois de criar uma cidade, você poderá clicar na guia Cidades para visualizar essa nova cidade.

Agora, vamos voltar ao painel do AWS AppSync. Clique em Origens de Dados e no nome da tabela. Isso levará você ao DynamoDB.

Na aba de itens, você deve conseguir ver os campos junto com o novo campo Autor.

Se você adicionar manualmente uma nova entrada ao banco de dados com outro nome de autor ou atualizar um campo existente que altere o nome do autor para um que não seja seu e atualizar seu aplicativo, essas cidades com os campos atualizados não serão exibidas em seu aplicativo como o resolvedor retornará apenas os campos que você escreveu!

Conclusão

Ao criar um aplicativo do mundo real, há muitas coisas importantes e complexas que precisam ser levadas em consideração, sendo que uma das mais importantes é um mundo real escalável e fácil de implementar.

Ao usar o GraphQL, você também deve levar em consideração as melhores práticas em relação não apenas à escalabilidade, mas também à segurança.

O GraphQL oferece a você o poder de aplicar diferentes controles de autorização para casos de uso como:

  • Uma API pública
  • Acesso privado e público a seções de uma API
  • Registros privados e públicos, verificados em tempo de execução nos campos
  • Um ou mais usuários podem gravar / ler em um registro (s)
  • Um ou mais grupos podem gravar / ler em um registro (s)
  • Todos podem ler, mas somente os criadores de registro podem editar ou excluir

Uma das coisas mais interessantes sobre o AWS AppSync são seus poderosos recursos integrados de autorização de usuário, que permitem que todos esses casos de uso de autorização de usuário GraphQL sejam gerenciados fora da caixa.

Meu nome é Nader Dabit . Sou um desenvolvedor advogado na AWS Mobile trabalhando com projetos como o AWS AppSync e o AWS Amplify e o fundador do React Native Training .

Se você gostou deste artigo, por favor , aplique o número de vezes e compartilhe! Obrigado pelo seu tempo.

Imagens cortesia da Amazon Web Services, Inc