Esta página aplica-se ao Apigee e ao Apigee Hybrid.
Veja a documentação do
Apigee Edge.
Este tópico aborda como usar e aplicar âmbitos do OAuth 2.0 com o Apigee.
O que é o âmbito do OAuth2?
Os âmbitos do OAuth 2.0 oferecem uma forma de limitar a quantidade de acesso concedido a uma chave de acesso. Por exemplo, um token de acesso emitido para uma app cliente pode receber acesso de LEITURA e ESCRITA a recursos protegidos ou apenas acesso de LEITURA. Pode implementar as suas APIs para aplicar qualquer âmbito ou combinação de âmbitos que quiser. Assim, se um cliente receber um token com o âmbito READ e tentar chamar um ponto final da API que requer acesso WRITE, a chamada falha.
Neste tópico, vamos abordar a forma como os âmbitos são atribuídos às chaves de acesso e como o Apigee aplica os âmbitos de OAuth 2.0. Depois de ler este tópico, vai poder usar âmbitos com confiança.
Como são atribuídos âmbitos aos tokens de acesso?
Quando o Apigee gera um token de acesso, pode atribuir um âmbito a esse token. Para compreender como isto acontece, tem de conhecer primeiro estas entidades do Apigee: produtos de API, programadores e apps de programadores. Para uma introdução, consulte o artigo Introdução à publicação. Recomendamos que reveja este material, se necessário, antes de continuar.
Um token de acesso é uma longa string de carateres com aspeto aleatório que permite ao Apigee validar os pedidos de API recebidos (pense nele como um substituto das credenciais típicas de nome de utilizador/palavra-passe). Tecnicamente, o token é uma chave que se refere a uma coleção de metadados com o seguinte aspeto:
{ "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 validade, identificação da app do programador, do programador e dos produtos associados ao token. Também vai reparar que os metadados também incluem o "âmbito".
Como é que o token obtém o seu âmbito?
A primeira chave para compreender o âmbito é lembrar que cada produto numa app de programador pode ter zero ou mais âmbitos atribuídos. Estes âmbitos podem ser atribuídos quando o produto é criado ou podem ser adicionados mais tarde. Existem como uma lista de nomes e estão incluídos nos "metadados" associados a cada produto.
Quando cria uma app de programador e adiciona produtos à mesma, o Apigee analisa todos os produtos na app de programador e cria uma lista de todos os âmbitos desses produtos (a lista de âmbitos principal ou global da app, uma união de todos os âmbitos reconhecidos).
Quando uma app cliente pede um token de acesso ao Apigee, pode especificar opcionalmente os âmbitos que quer associados a esse token. Por exemplo, o pedido seguinte pede o âmbito "A". Ou seja, o cliente está a pedir que o servidor de autorizações (Apigee) gere um token de acesso com o âmbito "A" (dando à app autorização para chamar APIs com o âmbito "A"). A app envia um pedido POST como este:
curl -i -X POST -H Authorization: Basic Mg12YTk2UkEIyIBCrtro1QpIG \ -H content-type:application/x-www-form-urlencoded \ https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A
O que acontece?
Quando o Apigee recebe este pedido, sabe que app está a fazer o pedido e sabe que app de programador o cliente registou (as chaves de ID de cliente e segredo do cliente estão codificadas no cabeçalho de autenticação básica). Uma vez que o parâmetro de consulta scope
está incluído, o Apigee tem de decidir se algum dos produtos de API associados à app de programador tem o âmbito "A". Se o fizer,
é gerado um token de acesso com o âmbito "A". Outra forma de analisar isto é que o parâmetro de consulta
scope é um tipo de filtro. Se a app do programador reconhecer os âmbitos "A, B, X" e o parâmetro de consulta especificar "scope=X Y Z", apenas o âmbito "X" é atribuído ao token.
E se o cliente não anexar um parâmetro de âmbito? Neste caso, o Apigee gera um token que inclui todos os âmbitos reconhecidos pela app do programador. É importante compreender que o comportamento predefinido é devolver um token de acesso que contém a união de todos os âmbitos para todos os produtos incluídos na app do programador.
Se nenhum dos produtos associados a uma app de programador especificar âmbitos e um token tiver um âmbito, as chamadas feitas com esse token falham.
Suponhamos que uma app de programador reconhece estes âmbitos: A B C D. Esta é a lista principal de âmbitos da app. Pode acontecer que um produto na app tenha o âmbito A e B, e um segundo produto tenha o âmbito C e D, ou qualquer combinação. Se o cliente não especificar um parâmetro scope
(ou se especificar um parâmetro de âmbito sem valor), o token vai receber todos os quatro âmbitos: A, B, C e D. Mais uma vez, o token recebe um conjunto de âmbitos que é a união de todos os âmbitos reconhecidos pela app do programador.
Existe mais um caso em que o comportamento predefinido é devolver um token de acesso com todos os âmbitos reconhecidos, e é quando a política GenerateAccessToken (a política do Apigee que gera tokens de acesso) não especifica um elemento <Scope>
.
Por exemplo, aqui está uma política GenerateAccessToken onde <Scope>
está especificado. Se esse elemento <Scope>
estiver em falta (ou se estiver
presente, mas vazio), o comportamento predefinido é 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 são aplicados os âmbitos?
Primeiro, lembre-se de que, no Apigee, 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 tem de ter a operação VerifyAccessToken especificada. Vejamos esta 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>
Tenha em atenção o elemento <Scope>
. É usado para especificar os âmbitos que a política
vai aceitar.
Neste exemplo, a política só é bem-sucedida se o token de acesso incluir o âmbito "A". Se este elemento <Scope> for omitido ou não tiver um valor, a política ignora o âmbito do token de acesso.
Agora, com a capacidade de validar tokens de acesso com base no âmbito, pode conceber as suas APIs para aplicar âmbitos específicos. Para tal, crie fluxos personalizados com políticas VerifyAccessToken sensíveis ao âmbito anexadas.
Suponhamos que a sua API tem um fluxo definido para o ponto final /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 este fluxo é acionado (é recebido um pedido com /resourceA
no sufixo do caminho), a política OAuthV2-VerifyAccessTokenA é chamada imediatamente. Esta política verifica se o token de acesso é válido e procura os âmbitos que o token suporta. Se a política for configurada como no exemplo abaixo, com <Scope>A</Scope>, a política só é bem-sucedida se o token de acesso tiver o âmbito "A". Caso contrário, devolve um erro.
<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>
Em resumo, os programadores de APIs são responsáveis por conceber a aplicação de âmbito nas respetivas APIs. Para tal, criam fluxos personalizados para processar âmbitos específicos e anexam políticas VerifyAccessToken para aplicar esses âmbitos.
Exemplos de código
Por último, vamos analisar alguns exemplos de chamadas da API para ajudar a ilustrar como os tokens recebem âmbitos e como os âmbitos são aplicados.
Caso predefinido
Suponhamos que tem uma app de programador com produtos e que a união dos âmbitos desses produtos é: A, B e C. Esta chamada API pede um token de acesso, mas não especifica um parâmetro de consulta de âmbito.
curl -X POST -H content-type:application/x-www-form-urlencoded \ https://apitest.acme.com/scopecheck1/token?grant_type=client_credentials
Neste caso, o token gerado recebe os âmbitos A, B e C (o comportamento predefinido). Os metadados do token têm um aspeto semelhante a este:
{ "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" }
Agora, suponhamos que tem um ponto final da API com o âmbito "A" (ou seja, VerifyAccessToken requer o âmbito "A"). Segue-se 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>
Segue-se um exemplo de uma chamada para um ponto final que aplica o âmbito A:
curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI \ https://apitest.acme.com/scopecheck1/resourceA
Esta chamada GET é bem-sucedida:
{ "hello" : "Tue, 25 Nov 2014 01:35:53 UTC" }
Tem êxito porque a política VerifyAccessToken acionada quando o ponto final é chamado requer o âmbito A e o token de acesso recebeu os âmbitos A, B e C, que é o comportamento predefinido.
Registo relativo à filtragem
Suponhamos que tem uma app de programador com produtos que têm âmbitos A, B, C e X. Pede
um token de acesso e inclui o parâmetro de consulta scope
, da seguinte forma:
curl -i -X POST -H content-type:application/x-www-form-urlencoded \ 'https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A X'
Neste caso, o token gerado recebe os âmbitos A e X, porque ambos são âmbitos válidos. Lembre-se de que a app do programador reconhece os âmbitos A, B, C e X. Neste caso, está a filtrar a lista de produtos da API com base nestes âmbitos. Se um produto tiver o âmbito A ou X, pode configurar pontos finais da API que aplicam estes âmbitos. Se um produto não tiver o âmbito A ou X (por exemplo,tem B, C e Z), não é possível chamar as APIs que aplicam os âmbitos A ou X com o token.
Quando chama a API com o novo token:
curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG \ https://apitest.acme.com/scopecheck1/resourceX
O token de acesso é validado pelo proxy da API. Por 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>
A chamada GET é acionada com êxito e devolve uma resposta. Por exemplo:
{ "hello" : "Tue, 25 Nov 2014 01:35:53 UTC" }
Tem êxito porque a política VerifyAccessToken requer o âmbito A ou X, e o token de acesso inclui o âmbito A e X. Claro que, se o elemento <Scope>
fosse definido como "B",
esta chamada falharia.
Resumo
É importante compreender como o Apigee processa os âmbitos do OAuth 2.0. Seguem-se os principais pontos a reter:
- Uma app de programador "reconhece" a união de todos os âmbitos definidos para todos os respetivos produtos.
- Quando uma app pede um token de acesso, tem a oportunidade de especificar os âmbitos que quer ter. Cabe ao Apigee (o servidor de autorização) determinar os âmbitos que vai atribuir ao token de acesso com base (a) nos âmbitos pedidos e (b) nos âmbitos reconhecidos pela app do programador.
- Se o Apigee não estiver configurado para verificar o âmbito (o elemento
<Scope>
está em falta na política VerifyAccessToken ou está vazio), a chamada API é bem-sucedida desde que o âmbito incorporado no token de acesso corresponda a um dos âmbitos reconhecidos pela app de programador registada (um dos âmbitos na lista "principal" de âmbitos da app). - Se um token de acesso não tiver âmbitos associados, só é bem-sucedido nos casos em que o Apigee não considera o âmbito (o elemento
<Scope>
está em falta na política VerifyAccessToken ou está vazio).