Como trabalhar com escopos do OAuth2

Você está vendo a documentação do Apigee Edge.
Acesse a documentação da Apigee X.
informações

Neste tópico, discutimos como usar os escopos do OAuth 2.0 na Apigee Edge.

O que é o escopo OAuth2?

Os escopos do OAuth 2.0 oferecem uma maneira de limitar a quantidade de acesso concedida a um token de acesso. Por exemplo, um token de acesso emitido para um app cliente pode receber acesso de READ e WRITE a recursos protegidos ou apenas a acesso READ. É possível implementar as APIs para impor qualquer escopo ou combinação de escopos desejados. Portanto, se um cliente receber um token com escopo READ e tentar chamar um endpoint de API que exija acesso WRITE, a chamada falhará.

Neste tópico, discutiremos como os escopos são atribuídos aos tokens de acesso e como a Apigee Edge aplica os escopos do OAuth 2.0. Depois de ler este tópico, será possível usar os escopos com confiança.

Como os escopos são atribuídos aos tokens de acesso?

Quando o Edge gera um token de acesso, ele atribui um escopo a esse token. Para entender como isso acontece, você precisa primeiro estar familiarizado com estas entidades da Apigee Edge: produtos de API, desenvolvedores e apps de desenvolvedor. Para uma introdução, consulte Introdução à publicação. Recomendamos que você revise esse material, caso necessário, antes de continuar.

Um token de acesso é uma string longa de caracteres de aparência aleatória que permite que o Edge verifique solicitações de API recebidas. Ele funciona como um complemento para credenciais típicas de nome de usuário/senha. Tecnicamente, o token é uma chave que se refere a uma coleção de metadados com esta aparência:

{
  "issued_at" : "1416962591727",
  "application_name" : "0d3e1d41-a59f-4d74-957e-d4e3275d4781",
  "scope" : "A",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-bs0cSuqS9y]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-AdBmANhsag@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "eTtB7w5lvk3DnOZNGReBlvGvIAeAywun",
  "access_token" : "ODm47ris5AlEty8TDc1itwYPe5MW",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

Os metadados do token incluem a string do token de acesso real, informações de vencimento, identificação do aplicativo do desenvolvedor, desenvolvedor e produtos associados ao token. Você também verá que os metadados incluem o "escopo".

Como o token recebe o escopo?

Para entender o escopo é preciso lembrar que cada produto em um app do desenvolvedor pode ter zero ou mais escopos atribuídos. É possível atribuir esses escopos ao criar o produto ou adicioná-los posteriormente. Eles existem como uma lista de nomes e são incluídos nos "metadados" associados a cada produto.

Quando você cria um app de desenvolvedor e adiciona produtos a ele, o Edge analisa todos os produtos no app do desenvolvedor e cria uma lista de todos os escopos desses produtos (a lista de escopo principal ou global do app, uma união de todos os escopos reconhecidos).

Quando um aplicativo cliente solicita um token de acesso da Apigee Edge, ele tem a opção de especificar quais escopos ele quer associar a esse token. Por exemplo, a solicitação a seguir solicita o escopo "A". Ou seja, o cliente está solicitando que o servidor de autorização (Edge) gere um token de acesso que tenha o escopo "A", autorizando o aplicativo a chamar APIs que tenham o escopo "A". O aplicativo envia uma solicitação POST desta forma:

curl -i -X POST -H Authorization: Basic Mg12YTk2UkEIyIBCrtro1QpIG -H content-type:application/x-www-form-urlencoded http://myorg-test.apigee.net/oauth/token?grant_type=client_credentials&scope=A

O que acontece?

Quando o Edge recebe essa solicitação, ele sabe qual app está fazendo a solicitação e sabe qual app do desenvolvedor o cliente registrou. O ID e as chaves secretas do cliente são codificados no cabeçalho de autenticação básico. Como o parâmetro de consulta scope está incluído, o Edge precisa decidir se qualquer um dos produtos da API associados ao app do desenvolvedor tem o escopo "A". Nesse caso, um token de acesso é gerado com o escopo "A". Outra maneira de analisar isso é que o parâmetro de consulta de escopo é um tipo de filtro. Se o aplicativo do desenvolvedor reconhecer os escopos "A, B, X" e o parâmetro de consulta especificar "scope=X Y Z", somente o escopo "X" será atribuído ao token.

E se o cliente não anexar um parâmetro de escopo? Nesse caso, o Edge gera um token que inclui todos os escopos reconhecidos pelo app do desenvolvedor. É importante entender que o comportamento padrão é retornar um token de acesso que contenha a união de todos os escopos de todos os produtos incluídos no app do desenvolvedor.

Se nenhum dos produtos associados a um app de desenvolvedor especificar escopos e um token tiver um escopo, as chamadas feitas com esse token falharão.

Digamos que um aplicativo de desenvolvedor reconheça estes escopos: A B C D. Esta é a lista mestre de escopos do aplicativo. Pode ser que um produto no app tenha o escopo A e B e o outro tenha o escopo C e D ou qualquer combinação. Se o cliente não especificar um parâmetro scope (ou se especificar um parâmetro de escopo sem valor), o token receberá os quatro escopos: A, B, C e D. O token recebe um conjunto de escopos que são a união de todos os escopos reconhecidos pelo app do desenvolvedor.

Existe mais um caso em que o comportamento padrão é retornar um token de acesso com todos os escopos reconhecidos. Isso acontece quando a política GenerateAccessToken da Apigee Edge que gera tokens de acesso não especifica um elemento <Scope>. Por exemplo, veja uma política GenerateAccessToken em que <Scope> é especificado. Se o elemento <Scope> estiver ausente (ou se estiver presente, mas estiver vazio), o comportamento padrão será executado.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-GenerateAccessToken">
    <DisplayName>OAuthV2 - Generate Access Token</DisplayName>
    <Attributes>
      <Attribute name='hello' ref='system.time' display='false'>value1</Attribute>
    </Attributes>
    <Scope>request.queryparam.scope</Scope> 
    <GrantType>request.formparam.grant_type</GrantType>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>GenerateAccessToken</Operation>
    <SupportedGrantTypes>
      <GrantType>client_credentials</GrantType>
    </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
</OAuthV2>

Como os escopos são aplicados?

Primeiro, lembre-se de que, na Apigee Edge, os tokens de acesso são validados com a política OAuthV2 (normalmente colocada no início de um fluxo de proxy). A política precisa ter a operação VerifyAccessToken especificada. Vamos analisar essa política:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope> <!-- Optional: space-separated list of scope names. -->
    <GenerateResponse enabled="true"/>
</OAuthV2>

Observe o elemento <Scope>. Ele é usado para especificar quais escopos a política aceita.

Neste exemplo, a política terá êxito somente se o token de acesso incluir o escopo "A". Se o elemento <Scope> for omitido ou se ele não tiver um valor, a política ignorará o escopo do token de acesso.

Agora, com a capacidade de validar tokens de acesso com base no escopo, é possível projetar suas APIs para aplicar escopos específicos. Para fazer isso, projete fluxos personalizados com políticas VerifyAccessToken de escopo anexadas a eles.

Digamos que a API tenha um fluxo definido para o endpoint /resourceA:

<Flow name="resourceA">
            <Condition>(proxy.pathsuffix MatchesPath "/resourceA") and (request.verb = "GET")</Condition>
            <Description>Get a resource A</Description>
            <Request>
                <Step>
                    <Name>OAuthV2-VerifyAccessTokenA</Name>
                </Step>
            </Request>
            <Response>
                <Step>
                    <Name>AssignMessage-CreateResponse</Name>
                </Step>
            </Response>
        </Flow>

Quando esse fluxo é acionado e a solicitação tem /resourceA no sufixo do caminho, a política OAuthV2-VerifyAccessTokenA é chamada imediatamente. Essa política verifica se o token de acesso é válido e se parece com os escopos compatíveis com o token. Se a política estiver configurada como exemplo abaixo, com <Scope>A</Scope>, ela só será bem-sucedida se o token de acesso tiver o escopo "A". Caso contrário, um erro será exibido.

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

Resumindo, os desenvolvedores de API são responsáveis por projetar a aplicação de escopo nas APIs. Isso é feito por meio da criação de fluxos personalizados para lidar com escopos específicos e da adição de políticas VerifyAccessToken para aplicar esses escopos.

Exemplos de código

Por fim, vamos dar uma olhada em algumas chamadas de API de exemplo para ajudar a ilustrar como os tokens recebem escopos e como os escopos são aplicados.

Caso padrão

Digamos que você tenha um app de desenvolvedor com produtos e que a união dos escopos dele seja: A, B e C. Essa chamada de API solicita um token de acesso, mas não especifica um parâmetro de consulta de escopo.

curl -X POST -H content-type:application/x-www-form-urlencoded http://wwitman-test.apigee.net/scopecheck1/token?grant_type=client_credentials

Nesse caso, o token gerado receberá os escopos A, B e C (o comportamento padrão). Os metadados do token seriam assim:

{
  "issued_at" : "1417016208588",
  "application_name" : "eb1a0333-5775-4116-9eb2-c36075ddc360",
  "scope" : "A B C",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-yEgQbQqjRR]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-yxiuHuZcDW@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "atGFvl3jgA0pJd05rXKHeNAC69naDmpW",
  "access_token" : "MveXpj4UYXol38thNoJYIa8fBGlI",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

Digamos que você tenha um endpoint da API com o escopo "A" (ou seja, que VerifyAccessToken requer o escopo "A"). Veja a política VerifyAccessToken:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

Veja um exemplo de chamada e um endpoint que aplica o escopo A:

curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI http://wwitman-test.apigee.net/scopecheck1/resourceA 

Essa chamada GET funciona:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }

Ela funciona porque a política VerifyAccessToken, que é acionada quando o endpoint é chamado, exige o escopo A e o token de acesso recebeu os escopos A, B e C, o comportamento padrão.

Como filtrar maiúsculas e minúsculas

Digamos que você tenha um app de desenvolvedor com produtos que tenham os escopos A, B, C e X. Você solicita um token de acesso e inclui o parâmetro de consulta scope desta forma:

curl -i -X POST -H content-type:application/x-www-form-urlencoded 'http://myorg-test.apigee.net/oauth/token?grant_type=client_credentials&scope=A X'

Nesse caso, o token gerado receberá os escopos A e X, porque A e X são escopos válidos. Lembre-se de que o aplicativo do desenvolvedor reconhece os escopos A, B, C e X. Neste caso, você está filtrando a lista de produtos de API com base nesses escopos. Se um produto tiver o escopo A ou X, será possível configurar os endpoints da API que aplicarão esses escopos. Se um produto não tiver o escopo A ou X (por exemplo, ele tem B, C e Z), as APIs que aplicam os escopos A ou X não podem ser chamadas com o token.

Ao chamar a API com o novo token:

curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG http://wwitman-test.apigee.net/scopecheck1/resourceX

O token de acesso é validado pelo proxy da API. Exemplo:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenX">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A X</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

Os gatilhos GET da chamada são bem-sucedidos e retornam uma resposta. Exemplo:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }
 

Ela será bem-sucedida porque a política VerifyAccessToken requer o escopo A ou X, e o token de acesso inclui o escopo A e X. Obviamente, se o elemento <Scope> for definido como "B", essa chamada falhará.

Resumo

É importante entender como a Apigee Edge lida com os escopos do OAuth 2.0. Veja os pontos principais:

  • Um aplicativo do desenvolvedor "reconhece" a união de todos os escopos definidos de todos os produtos correspondentes.
  • Quando um app solicita um token de acesso, ele tem a chance de especificar quais escopos ele quer ter. Cabe ao servidor de autorização da Apigee Edge descobrir quais escopos efetivamente serão atribuídos ao token de acesso com base (a) nos escopos solicitados e (b) nos que são reconhecidos pelo aplicativo do desenvolvedor.
  • Se a Apigee Edge não estiver configurada para verificar o escopo (o elemento <Scope> não está na política VerifyAccessToken ou está vazio), a chamada de API será bem-sucedida, desde que o escopo incorporado no acesso corresponda a um dos escopos reconhecidos pelo app do desenvolvedor registrado. Um dos escopos na lista de escopos "mestre" do app.
  • Se um token de acesso não tiver nenhum escopo associado a ele, ele só terá êxito nos casos em que o Edge não considerar o escopo (o elemento <Scope> não está na política VerifyAccessToken ou está vazio).