Utilizzo degli ambiti OAuth2

Questa pagina si applica ad Apigee e Apigee hybrid.

Visualizza la documentazione di Apigee Edge.

Questo argomento illustra come utilizzare e applicare gli ambiti OAuth 2.0 con Apigee.

Che cos'è l'ambito OAuth2?

Gli ambiti OAuth 2.0 consentono di limitare la quantità di accessi concessa a un token di accesso. Ad esempio, a un token di accesso emesso per un'app client potrebbe essere concesso l'accesso in lettura e in scrittura alle risorse protette o solo l'accesso in lettura. Puoi implementare le API per applicare qualsiasi ambito o combinazione di ambiti che preferisci. Quindi, se un client riceve un token con ambito READ e tenta di chiamare un endpoint API che richiede l'accesso in scrittura, la chiamata avrà esito negativo.

In questo argomento, parleremo di come vengono assegnati gli ambiti ai token di accesso e di come Apigee applica gli ambiti OAuth 2.0. Dopo aver letto questo argomento, potrai utilizzare gli ambiti in tutta sicurezza.

Come vengono assegnati gli ambiti ai token di accesso?

Quando Apigee genera un token di accesso, potrebbe assegnare un ambito a quel token. Per comprendere come accade, devi prima conoscere queste entità Apigee: prodotti API, sviluppatori e app per sviluppatori. Per un'introduzione, consulta la sezione Introduzione alla pubblicazione. Se necessario, ti consigliamo di rivedere questo materiale prima di continuare.

Un token di accesso è una lunga stringa di caratteri dall'aspetto casuale che consente ad Apigee di verificare le richieste API in entrata (pensalo come un sostituto delle tipiche credenziali nome utente/password). Tecnicamente, il token è una chiave che fa riferimento a una raccolta di metadati che ha il seguente aspetto:

{
  "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"
}

I metadati del token includono la stringa del token di accesso effettiva, le informazioni sulla scadenza, l'identificazione dell'app dello sviluppatore, dello sviluppatore e dei prodotti associati al token. Noterai inoltre che i metadati includono anche l'"ambito".

In che modo il token ottiene l'ambito?

La prima chiave per comprendere l'ambito è ricordare che a ogni prodotto di un'app dello sviluppatore non possono essere assegnati zero o più ambiti. Questi ambiti possono essere assegnati al momento della creazione del prodotto oppure aggiunti in un secondo momento. Esistono come un elenco di nomi e sono inclusi nei "metadati" associati a ciascun prodotto.

Quando crei un'app dello sviluppatore e aggiungi prodotti, Apigee controlla tutti i prodotti nell'app e crea un elenco di tutti gli ambiti per questi prodotti (l'elenco degli ambiti principali o globali dell'app, un'unione di tutti gli ambiti riconosciuti).

Quando un'app client richiede un token di accesso ad Apigee, può facoltativamente specificare quali ambiti desidera associare al token. Ad esempio, la seguente richiesta chiede l'ambito "A". In altre parole, il client chiede al server di autorizzazione (Apigee) di generare un token di accesso con ambito "A" (autorizzando l'app a chiamare le API con ambito "A"). L'app invia una richiesta POST come la seguente:

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

Che cosa succede?

Quando Apigee riceve questa richiesta, sa quale app sta effettuando la richiesta e sa quale app dello sviluppatore ha registrato il client (l'ID client e le chiavi client secret sono codificate nell'intestazione dell'autenticazione di base). Poiché il parametro di query scope è incluso, Apigee deve decidere se uno qualsiasi dei prodotti API associati all'app per sviluppatori ha l'ambito "A". In caso affermativo, viene generato un token di accesso con l'ambito "A". Un altro modo per vedere la situazione è che il parametro di query dell'ambito è un tipo di filtro. Se l'app per sviluppatori riconosce gli ambiti "A, B, X" e il parametro di query specifica "scope=X Y Z", al token verrà assegnato solo l'ambito "X".

Che cosa succede se il client non collega un parametro di ambito? In questo caso, Apigee genera un token che include tutti gli ambiti riconosciuti dall'app dello sviluppatore. È importante capire che il comportamento predefinito è restituire un token di accesso che contiene l'unione di tutti gli ambiti per tutti i prodotti inclusi nell'app per sviluppatori.

Se nessuno dei prodotti associati a un'app per sviluppatori specifica gli ambiti e un token ha un ambito, le chiamate effettuate con quel token non andranno a buon fine.

Supponiamo che un'app per sviluppatori riconosca questi ambiti: A B C D. Questo è l'elenco principale degli ambiti dell'app. Potrebbe essere che un prodotto dell'app abbia gli ambiti A e B e che un secondo abbia gli ambiti C e D o qualsiasi combinazione. Se il client non specifica un parametro scope (o se specifica un parametro di ambito senza valore), al token verranno concessi tutti e quattro gli ambiti: A, B, C e D. Anche in questo caso, il token riceve un insieme di ambiti che corrisponde all'unione di tutti gli ambiti riconosciuti dall'app dello sviluppatore.

Esiste un altro caso in cui il comportamento predefinito è restituire un token di accesso con tutti gli ambiti riconosciuti, ovvero quando il criterio ManageAccessToken (il criterio Apigee che genera i token di accesso) non specifica un elemento <Scope>. Ad esempio, ecco un criterio GeneraAccessToken in cui <Scope> è specificato. Se l'elemento <Scope> non è presente (o se è presente ma vuoto), viene eseguito il comportamento predefinito.

<?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>

Come vengono applicati gli ambiti?

Innanzitutto, ricorda che su Apigee, i token di accesso vengono convalidati con il criterio OAuthV2 (in genere posizionato all'inizio di un flusso proxy). Nel criterio deve essere specificata l'operazione VerifyAccessToken. Esaminiamo queste norme:

<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>

Nota l'elemento <Scope>. Viene utilizzato per specificare gli ambiti accettati dal criterio.

In questo esempio, il criterio avrà esito positivo solo se il token di accesso include l'ambito "A". Se questo elemento <Scope> viene omesso o se non ha valori, il criterio ignora l'ambito del token di accesso.

Ora, con la possibilità di convalidare i token di accesso in base all'ambito, puoi progettare le tue API per applicare ambiti specifici. Per farlo, progetta flussi personalizzati a cui sono associati criteri VerificationAccessToken sensibili all'ambito.

Supponiamo che la tua API abbia un flusso definito per l'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 questo flusso viene attivato (una richiesta arriva con /resourceA nel suffisso del percorso), il criterio OAuthV2-VerifyAccessTokenA viene chiamato immediatamente. Questo criterio verifica che il token di accesso sia valido e controlla quali ambiti sono supportati dal token. Se il criterio viene configurato come nell'esempio seguente, con <Scope>A</Scope>, il criterio avrà esito positivo solo se il token di accesso ha l'ambito "A". In caso contrario, verrà restituito un errore.

<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>

Per riepilogare, gli sviluppatori di API sono responsabili della progettazione dell'applicazione forzata dell'ambito nelle loro API. Per farlo, devono creare flussi personalizzati per gestire ambiti specifici e collegare i criteri VerificationAccessToken per applicarli.

Esempi di codice

Infine, diamo un'occhiata ad alcuni esempi di chiamate API per illustrare come i token ricevono gli ambiti e come vengono applicati gli ambiti.

Maiuscole predefinite

Supponiamo che tu abbia un'app per sviluppatori con prodotti e che l'unione degli ambiti di questi prodotti sia A, B e C. Questa chiamata API richiede un token di accesso, ma non specifica un parametro di query dell'ambito.

curl -X POST -H content-type:application/x-www-form-urlencoded \
  https://apitest.acme.com/scopecheck1/token?grant_type=client_credentials

In questo caso, al token generato saranno assegnati gli ambiti A, B e C (il comportamento predefinito). I metadati del token avrebbero un aspetto simile a questo:

{
  "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"
}

Ora, supponiamo che tu abbia un endpoint API con ambito "A" (ovvero, VerificationAccessToken richiede l'ambito "A"). Ecco il criterio di VerificationAccessToken:

<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>

Ecco una chiamata di esempio a un endpoint che applica l'ambito A:

curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI \
  https://apitest.acme.com/scopecheck1/resourceA

Questa chiamata GET ha esito positivo:

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

L'operazione riesce perché il criterio VerificationAccessToken attivato quando viene chiamato l'endpoint richiede l'ambito A e al token di accesso sono stati concessi gli ambiti A, B e C, il comportamento predefinito.

Filtro delle richieste

Supponiamo che tu abbia un'app per sviluppatori con prodotti che hanno gli ambiti A, B, C e X. Richiedi un token di accesso e includi il parametro di query scope, in questo modo:

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'

In questo caso, al token generato verranno assegnati gli ambiti A e X, poiché sia A che X sono ambiti validi. Ricorda che l'app per sviluppatori riconosce gli ambiti A, B, C e X. In questo caso, stai filtrando l'elenco dei prodotti API in base a questi ambiti. Se un prodotto ha l'ambito A o X, puoi configurare gli endpoint API che applicheranno questi ambiti. Se un prodotto non ha gli ambiti A o X (diciamo che ha B, C e Z), le API che applicano gli ambiti A o X non possono essere chiamate con il token.

Quando chiami l'API con il nuovo token:

curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG \
  https://apitest.acme.com/scopecheck1/resourceX

Il token di accesso è convalidato dal proxy API. Ad esempio:

<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>

I trigger di chiamata GET hanno esito positivo e restituisce una risposta. Ad esempio:

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

L'operazione riesce perché il criterio VerificationAccessToken richiede gli ambiti A o X e il token di accesso include gli ambiti A e X. Ovviamente, se l'elemento <Scope> fosse impostato su "B", questa chiamata non andrebbe a buon fine.

Riepilogo

È importante capire in che modo Apigee gestisce gli ambiti OAuth 2.0. Ecco i concetti chiave:

  • Un'app per sviluppatori "riconosce" l'unione di tutti gli ambiti definiti per tutti i suoi prodotti.
  • Quando un'app richiede un token di accesso, ha la possibilità di specificare gli ambiti che desidera avere. Spetta ad Apigee (il server di autorizzazione) capire quali ambiti assegnerà effettivamente al token di accesso in base a (a) gli ambiti richiesti e (b) quelli riconosciuti dall'app sviluppatore.
  • Se Apigee non è configurato per verificare l'ambito (l'elemento <Scope> non è presente nel criterio VerificationAccessToken o è vuoto), la chiamata API avrà esito positivo purché l'ambito incorporato nel token di accesso corrisponda a uno degli ambiti riconosciuti dall'app dello sviluppatore registrata (uno degli ambiti nell'elenco di ambiti "master" dell'app).
  • Se a un token di accesso non sono associati ambiti, avrà esito positivo solo nei casi in cui Apigee non prende in considerazione l'ambito (l'elemento <Scope> non è presente nel criterio VerificationAccessToken o è vuoto).