Utilizzo degli ambiti OAuth2

Questa pagina si applica ad Apigee e Apigee hybrid.

Visualizza la documentazione di Apigee Edge.

Questo argomento spiega 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 accesso concessa a un token di accesso. Ad esempio, a un token di accesso emesso a un'app client può essere concesso l'accesso in LETTURA e SCRITTURA alle risorse protette o solo l'accesso in LETTURA. Puoi implementare le tue API per applicare qualsiasi ambito o combinazione di ambiti che preferisci. Pertanto, se un client riceve un token con ambito LETTURA e tenta di chiamare un endpoint API che richiede l'accesso in scrittura, la chiamata non andrà a buon fine.

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

Come vengono assegnati gli ambiti ai token di accesso?

Quando Apigee genera un token di accesso, potrebbe assegnargli un ambito. Per capire come avviene questo processo, devi prima conoscere queste entità Apigee: prodotti API, sviluppatori e app sviluppatore. Per un'introduzione, consulta Introduzione alla pubblicazione. Ti consigliamo di esaminare questo materiale, se necessario, prima di continuare.

Un token di accesso è una lunga stringa di caratteri apparentemente casuali che consente ad Apigee di verificare le richieste API in entrata (consideralo un sostituto delle credenziali utente/password standard). 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 effettivo, 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 "scope".

Come viene definito l'ambito del token?

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

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

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

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 la sta inviando e quale app per sviluppatori ha registrato il cliente (le chiavi ID client e client secret sono codificate nell'header di autenticazione di base). Poiché il parametro di query scope è incluso, Apigee deve decidere se uno dei prodotti API associati all'app per sviluppatori ha l'ambito "A". In questo caso, viene generato un token di accesso con ambito "A". Un altro modo per interpretarlo è che il parametro di query scopo è 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".

Cosa succede se il cliente non allega un parametro di ambito? In questo caso, Apigee genera un token che include tutti gli ambiti riconosciuti dall'app per sviluppatori. È importante capire che il comportamento predefinito è restituire un token di accesso contenente 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 ne ha uno, 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. È possibile che un prodotto nell'app abbia l'ambito A e B, un secondo l'ambito C e D o qualsiasi combinazione. Se il cliente non specifica un parametro scope (o se specifica un parametro 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 è l'unione di tutti gli ambiti riconosciuti dall'app per sviluppatori.

Esiste un altro caso in cui il comportamento predefinito è restituire un token di accesso con tutti gli scopi riconosciuti, ovvero quando il criterio GenerateAccessToken (il criterio Apigee che genera i token di accesso) non specifica un elemento <Scope>. Ad esempio, di seguito è riportato un criterio GenerateAccessToken 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). Il criterio deve avere specificato l'operazione VerifyAccessToken. Esamina questa norma:

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

Prendi nota dell'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 valore, 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 in modo da applicare ambiti specifici. A tal fine, progetta flussi personalizzati con criteri VerifyAccessToken sensibili all'ambito associati.

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 viene attivato questo flusso (viene inviata una richiesta 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 gli ambiti supportati dal token. Se il criterio è configurato come nell'esempio seguente, con <Scope>A</Scope>, avrà esito positivo solo se il token di accesso ha l'ambito "A". In caso contrario, restituirà 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>

In sintesi, gli sviluppatori di API sono responsabili della progettazione dell'applicazione dell'ambito nelle loro API. A questo scopo, creano flussi personalizzati per gestire ambiti specifici e associano i criteri VerifyAccessToken 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.

Maiuscolo predefinito

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 di 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 verranno assegnati gli ambiti A, B e C (il comportamento predefinito). I metadati del token saranno simili al seguente:

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

Supponiamo ora di avere un endpoint API con ambito "A" (ovvero VerifyAccessToken richiede l'ambito "A"). Ecco il criterio 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>

Di seguito è riportata 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 è riuscita:

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

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

Filtrare le richieste

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

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, perché entrambi sono ambiti validi. Ricorda che l'app per sviluppatori riconosce gli ambiti A, B, C e X. In questo caso, filtri l'elenco dei prodotti API in base a questi ambiti. Se un prodotto ha l'ambito A o X, puoi configurare endpoint API che applicheranno questi ambiti. Se un prodotto non ha l'ambito 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 viene 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>

La chiamata GET viene attivata correttamente e restituisce una risposta. Ad esempio:

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

L'operazione riesce perché il criterio VerifyAccessToken richiede l'ambito A o X e il token di accesso include gli ambiti A e X. Naturalmente, 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 punti 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 vorrebbe avere. Spetta ad Apigee (il server di autorizzazione) stabilire quali ambiti effettivamente assegnare al token di accesso in base (a) agli ambiti richiesti e (b) a quelli riconosciuti dall'app per sviluppatori.
  • Se Apigee non è configurato per verificare l'ambito (l'elemento <Scope> non è presente nel criterio VerifyAccessToken o è vuoto), la chiamata all'API andrà a buon fine a condizione che l'ambito incorporato nell'accesso il token corrisponda a uno degli ambiti riconosciuti dall'app sviluppatore registrata (uno degli ambiti nell'elenco "principale" degli ambiti dell'app).
  • Se a un token di accesso non sono associati ambiti, l'operazione andrà a buon fine solo nei casi in cui Apigee non prende in considerazione l'ambito (l'elemento <Scope> non è presente nel criterio VerifyAccessToken o è vuoto).