Autorização é o processo de conceder aos usuários acesso às diferentes partes e recursos de um aplicativo da web. Uma forma comum de autorizar usuários é por meio do controle de acesso, no qual o administrador do site define quais permissões devem ser concedidas aos usuários e outras entidades para acessar quais recursos.
A autorização não deve ser confundida com autenticação , que é o processo de validação de que um determinado usuário é quem afirma ser, geralmente realizado com o fornecimento de credenciais de conta. Depois que o usuário é autenticado, o processo de autorização ainda deve ser executado em todas as solicitações, para garantir que o usuário tenha acesso ao recurso solicitado.
Qual é a melhor maneira de implementar o controle de acesso no GraphQL? Neste artigo, vamos descobrir.
Colocando o controle de acesso no GraphQL
Ao acessar a aplicação via GraphQL, devemos validar se o usuário tem ou não acesso aos elementos solicitados de o esquema. A lógica de autorização deve ser codificada na camada GraphQL?
A resposta é não. Como a documentação do GraphQL deixa claro, a lógica de autorização pertence à camada de lógica de negócios e, a partir daí, é acessada por GraphQL. Dessa forma, o aplicativo pode ter uma única fonte de verdade para autorização, que pode então ser usada para outros pontos de acesso, como de um ponto de extremidade REST:
Políticas de controle de acesso
Entre as várias políticas de controle de acesso que podemos implementar em nosso aplicativo , os dois mais populares são o controle de acesso baseado em funções (RBAC) e o controle de acesso baseado em atributos (ABAC).
Com o controle de acesso baseado em funções, concedemos permissões com base nas funções e, em seguida, atribuímos o funções para os usuários. Por exemplo, o WordPress tem uma função de administrador com acesso a todos os recursos, e as funções de editor, autor, contribuidor e assinante, cada uma das quais restringe as permissões em vários graus, como ser capaz de criar e publicar uma postagem de blog, basta criá-la, ou apenas lê-lo.
Com o controle de acesso baseado em atributos, as permissões são concedidas com base em metadados que podem ser atribuídos a diferentes entidades, incluindo usuários, ativos e condições ambientais (como a hora do dia ou o endereço IP do visitante). Por exemplo, no WordPress, o recurso edit_others_posts é usado para validar se o usuário pode editar as postagens de outros usuários.
Em termos gerais, ABAC é preferível a RBAC porque nos permite configurar permissões com controle refinado , e a permissão é inequívoca em seu objetivo.
Para usar o exemplo do WordPress mais uma vez, a função de editor tem o recurso edit_others_posts, mas podemos permitir que uma pessoa com a função de autor edite a postagem de outro autor. No entanto, não podemos gerenciar isso sem conceder a eles o restante das permissões que um editor recebe, como excluir as postagens de outros autores.
Portanto, conceder o recurso edit_others_posts e verificar essa condição é mais adequado do que verificar a função do editor.
Definindo permissões para o esquema GraphQL
Como disse antes, a lógica de autorização é colocada na camada de negócios, e é onde ela será acessado pela camada GraphQL. Portanto, é na camada de negócios que decidiremos qual política implementar para o aplicativo: ABAC, RBAC, uma combinação deles ou outra inteiramente.
Com base na política empregada, a camada GraphQL irá valide se o usuário tem acesso aos campos solicitados do esquema, perguntando sobre condições como:
O usuário está logado? O usuário é o administrador? O usuário é um indivíduo específico? O usuário tem uma determinada função exigida? O usuário possui um determinado recurso necessário? O usuário está acessando o site de um intervalo de IP específico? O usuário está acessando o site de um determinado país? O usuário tem crédito restante em sua conta?
A validação também pode ser colocada no próprio campo, ou estar sujeita a algum fator de ambiente. Nesse caso, as seguintes perguntas adicionais podem ser feitas:
A API está sendo acessada após o expediente? O campo é acessado após uma determinada data? (para habilitar sinalizadores de recurso) O recurso está disponível apenas para o ambiente DEV?
Definindo a visibilidade
Quando o usuário não possui as permissões necessárias para acessar um campo solicitado do esquema GraphQL, qual erro deve ser retornado?
Existem duas possibilidades, em conformidade com a visibilidade desejada para o esquema: público ou privado.
Para o esquema público, o esquema GraphQL exposto é o mesmo para todos os usuários, e cada campo descreve quais permissões são necessárias para acessá-lo. Ao solicitar um campo inacessível, a mensagem de erro explicará por que o usuário não obteve acesso.
Esquema público: quando o acesso ao campo falha, a mensagem de erro explica o motivo
Para o esquema privado, o esquema GraphQL é personalizado para cada usuário e apenas os campos que eles podem acessar serão expostos. Ao solicitar um campo inacessível, a mensagem de erro indicará que o campo não existe.
Esquema privado: o campo não existe no esquema
Desacoplamento da lógica de controle de acesso do GraphQL
É uma boa ideia evitar acoplando fortemente a camada GraphQL e a estratégia de autorização que você escolheu atualmente para o aplicativo. Isso ocorre porque, se uma estratégia diferente for introduzida no futuro, a camada GraphQL pode usá-la imediatamente, sem a necessidade de atualizações generalizadas em toda a base de código.
Considere o seguinte exemplo de colocação de autorização em resolvers usando o servidor Apollo:
const resolvers={users: (raiz, argumentos, contexto)=> {if (! context.user ||! context.user.roles.includes (‘admin’)) return null; retornar context.models.User.getAll (); }}
Com este código, se mudarmos a política de controle de acesso para usar recursos em vez de funções no futuro, precisaremos atualizar todos os campos correspondentes em toda a base de código.
Uma estratégia melhor é para manter o controle de acesso em um local específico , como neste exemplo usando módulos GraphQL:
//validate-role.ts export const validateRole=(role)=> (next)=> (root, args, context, info)=> {if (context.currentUser.role!==role) { lançar um novo erro (`Não autorizado!`); } retorna próximo (root, args, contexto, info); };//articles-module.ts import {GraphQLModule} de’@ graphql-modules/core’; export const articlesModule=new GraphQLModule ({typeDefs: gql` type Mutation {publishArticle (title: String !, content: String!): Article!} `, resolvers: {Mutation: {publishArticle: (root, {title, content}, {currentUser})=> createNewArticle (título, conteúdo, currentUser),},},});//resolvers-composition.ts import {authenticated} from’./authenticated-guard’; import {validateRole} de’./validate-role’; export const resolversComposition={‘Mutation.publishArticle’: [authenticated, validateRole],};
Ou, em sua iteração posterior, usando diretivas para colocar as restrições ao definir o esquema via SDL:
digite Mutation {publishArticle (title: String !, content: String!): Artigo! @auth @protect (role:”EDITOR”)}
Nestes outros exemplos, quando a política muda, você pode atualizar de validateRole para validateCapability ou de @protect (função:”EDITOR”) para @protect (capacidade:”CAN_EDIT”) em um único lugar e ainda afetará todo o esquema GraphQL.
Desacoplando completamente a lógica de controle de acesso do GraphQL
Se o servidor GraphQL seguir o abordagem code-first , as regras de controle de acesso podem ser completamente desacopladas do GraphQL camada e injetada no tempo de execução como configuração por meio de listas de controle de acesso (ACLs).
Essa estratégia se baseia na abordagem do código porque o esquema GraphQL é um artefato do código, portanto, pode ser gerado e modificado em tempo de execução. Isso é necessário para definir a visibilidade como privada, onde diferentes esquemas são expostos com base nas permissões concedidas ao usuário. Em contraste, com a abordagem SDL primeiro, o esquema já está determinado e não pode ser modificado ao executar o aplicativo.
Ao tratar o controle de acesso como configuração em vez de código, a camada GraphQL refletirá imediatamente as mudanças de sua política de controle de acesso e você não precisará atualizar nenhum código ou recompilar o esquema.
Esta é a abordagem usada pelo API GraphQL para WordPress . Ele satisfaz o controle de acesso por meio das seguintes diretivas:
@validateIsUserLoggedIn @validateIsUserNotLoggedIn @validateDoesLoggedInUserHaveAnyRole @validateDoesLoggedInUserHaveAnyCapability
Essas diretivas podem ser aplicadas dinamicamente ao esquema, graças ao estratégia IFTTT via diretivas . Ele permite que você especifique quais diretivas devem ser aplicadas a quais campos e quais argumentos eles receberão. O esquema GraphQL resultante é montado em tempo de execução, logo antes da resolução da consulta.
Quando aplicado à autorização, a decisão de quais diretivas são injetadas no esquema é feita por meio de listas de controle de acesso , que são criadas e editadas por meio de uma interface de usuário:
Controle de acesso por meio da interface do usuário
O administrador do site configura a ACL, selecionando:
Os campos para validar Uma regra para validar, entre: o usuário deve estar logado? o usuário deve estar desconectado? o usuário deve ter uma determinada função? o usuário deve ter uma certa capacidade? A configuração específica da regra: quais funções? quais recursos? A visibilidade: padrão (ou seja, aquele que for atribuído ao esquema)? público? privado? Configurando um acesso lista de controle
Esta configuração é então armazenada em uma entrada no banco de dados. Por exemplo, a entrada abaixo define que os campos Media.author, Post.title e MutationRoot.addCommentToCustomPost são acessíveis apenas para usuários com a função de contribuidor ou com qualquer recurso entre edit_users, edit_others_posts e read_private_posts:
Finalmente, um serviço AccessControlGraphQLQueryConfigurator lê a configuração do banco de dados e a aplica ao esquema GraphQL no tempo de execução.
Agora, o usuário pode criar uma nova regra de controle de acesso, clique em Atualizar e veja as alterações aplicadas imediatamente:
Carregando dinamicamente uma lista de controle de acesso no esquema GraphQL
Conclusão
Ao trabalhar com GraphQL, a menos que a API seja totalmente pública, precisaremos para implementar a autenticação e a autorização, para validar se o usuário é quem afirma ser e para garantir que o usuário tenha acesso aos recursos solicitados, respectivamente.
Neste artigo, analisamos como autorizar usuários com base em diferentes políticas de controle de acesso, como estruturar o aplicativo para usar a funcionalidade de autenticação da camada de negócios e como desacoplar o GraphQL l ayer da camada de negócios para primeiro minimizar, e depois remover completamente, o impacto da atualização das políticas de controle de acesso.