Esta página se aplica a Apigee y Apigee Hybrid.
Consulta la documentación de
Apigee Edge.
En este tema se explica cómo usar y aplicar los ámbitos de OAuth 2.0 con Apigee.
¿Qué es un permiso de OAuth2?
Los permisos de OAuth 2.0 permiten limitar el nivel de acceso que se concede a un token de acceso. Por ejemplo, un token de acceso emitido para una aplicación cliente puede conceder acceso de LECTURA y ESCRITURA a recursos protegidos, o solo acceso de LECTURA. Puedes implementar tus APIs para aplicar cualquier permiso o combinación de permisos que quieras. Por lo tanto, si un cliente recibe un token que tiene el ámbito READ e intenta llamar a un endpoint de API que requiere acceso WRITE, la llamada fallará.
En este tema, hablaremos sobre cómo se asignan los permisos a los tokens de acceso y cómo aplica Apigee los permisos de OAuth 2.0. Después de leer este tema, podrás usar los ámbitos con confianza.
¿Cómo se asignan los ámbitos a los tokens de acceso?
Cuando Apigee genera un token de acceso, puede asignarle un ámbito. Para entender cómo funciona, primero debes familiarizarte con estas entidades de Apigee: productos de API, desarrolladores y aplicaciones para desarrolladores. Para obtener una introducción, consulta el artículo Introducción a la publicación. Te recomendamos que revises este material si lo necesitas antes de continuar.
Un token de acceso es una cadena larga de caracteres de aspecto aleatorio que permite a Apigee verificar las solicitudes de API entrantes (piensa en él como un sustituto de las credenciales típicas de nombre de usuario y contraseña). Técnicamente, el token es una clave que hace referencia a una colección de metadatos que tiene este aspecto:
{ "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" }
Los metadatos del token incluyen la cadena del token de acceso, información sobre la caducidad, la identificación de la aplicación del desarrollador, el desarrollador y los productos asociados al token. También verás que los metadatos incluyen "scope".
¿Cómo obtiene el token su ámbito?
La primera clave para entender el concepto de ámbito es recordar que cada producto de una aplicación de desarrollador puede tener cero o más ámbitos asignados. Estos ámbitos se pueden asignar cuando se crea el producto o se pueden añadir más adelante. Se trata de una lista de nombres que se incluye en los "metadatos" asociados a cada producto.
Cuando creas una aplicación de desarrollador y le añades productos, Apigee examina todos los productos de la aplicación de desarrollador y crea una lista de todos los ámbitos de esos productos (la lista de ámbitos principal o global de la aplicación, que es la unión de todos los ámbitos reconocidos).
Cuando una aplicación cliente solicita un token de acceso a Apigee, puede especificar opcionalmente los ámbitos que quiere que estén asociados a ese token. Por ejemplo, la siguiente solicitud pide el ámbito "A". Es decir, el cliente solicita que el servidor de autorización (Apigee) genere un token de acceso que tenga el ámbito "A" (lo que da a la aplicación autorización para llamar a las APIs que tienen el ámbito "A"). La aplicación envía una solicitud POST como esta:
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
¿Qué ocurre?
Cuando Apigee recibe esta solicitud, sabe qué aplicación la está enviando y qué aplicación de desarrollador ha registrado el cliente (las claves de ID de cliente y secreto de cliente están codificadas en el encabezado de autenticación básica). Como se incluye el parámetro de consulta scope
, Apigee debe decidir si alguno de los productos de API asociados a la aplicación del desarrollador tiene el ámbito "A". Si lo hacen, se genera un token de acceso con el permiso "A". Otra forma de verlo es que el parámetro de consulta scope
es un tipo de filtro. Si la aplicación del desarrollador reconoce los ámbitos "A, B, X" y el parámetro de consulta especifica "scope=X Y Z", solo se asignará el ámbito "X" al token.
¿Qué ocurre si el cliente no adjunta un parámetro de ámbito? En este caso, Apigee genera un token que incluye todos los ámbitos reconocidos por la aplicación del desarrollador. Es importante saber que el comportamiento predeterminado es devolver un token de acceso que contenga la unión de todos los ámbitos de todos los productos incluidos en la aplicación del desarrollador.
Si ninguno de los productos asociados a una aplicación de desarrollador especifica ámbitos y un token tiene un ámbito, las llamadas realizadas con ese token fallarán.
Supongamos que una aplicación de desarrollador reconoce estos ámbitos: A, B, C y D. Esta es la lista maestra de ámbitos de la aplicación. Puede que un producto de la aplicación tenga los ámbitos A y B, y otro tenga los ámbitos C y D, o cualquier otra combinación. Si el cliente no especifica el parámetro scope
(o si especifica el parámetro de ámbito sin ningún valor), se le concederán los cuatro ámbitos: A, B, C y D. De nuevo, el token recibe un conjunto de ámbitos que es la unión de todos los ámbitos reconocidos por la aplicación del desarrollador.
Hay otro caso en el que el comportamiento predeterminado es devolver un token de acceso con todos los
ámbitos reconocidos, y es cuando la política GenerateAccessToken (la política de Apigee que
genera tokens de acceso) no especifica un elemento <Scope>
.
Por ejemplo, aquí tienes una política GenerateAccessToken en la que se especifica <Scope>
is. Si falta el elemento <Scope>
(o si está presente pero vacío), se ejecutará el comportamiento predeterminado.
<?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>
¿Cómo se aplican los permisos?
En primer lugar, recuerda que en Apigee los tokens de acceso se validan con la política OAuthV2 (que suele colocarse al principio del flujo de un proxy). La política debe tener especificada la operación VerifyAccessToken. Veamos 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>
Ten en cuenta el elemento <Scope>
. Se usa para especificar los ámbitos que aceptará la política.
En este ejemplo, la política solo se aplicará si el token de acceso incluye el ámbito "A". Si se omite este elemento <Scope> o no tiene ningún valor, la política ignora el ámbito del token de acceso.
Ahora, gracias a la posibilidad de validar tokens de acceso en función del ámbito, puedes diseñar tus APIs para aplicar ámbitos específicos. Para ello, diseña flujos personalizados con políticas VerifyAccessToken que tengan el ámbito adecuado.
Supongamos que tu API tiene un flujo definido para el 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>
Cuando se activa este flujo (se recibe una solicitud con /resourceA
en el sufijo de la ruta), se llama inmediatamente a la política OAuthV2-VerifyAccessTokenA. Esta política verifica que el token de acceso sea válido y comprueba qué ámbitos admite. Si la política se configura como en el ejemplo de abajo, con <Scope>A</Scope>, solo se aplicará si el token de acceso tiene el ámbito "A". De lo contrario, devolverá un error.
<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>
En resumen, los desarrolladores de APIs son los responsables de diseñar la aplicación del ámbito en sus APIs. Para ello, crean flujos personalizados para gestionar ámbitos específicos y adjuntan políticas VerifyAccessToken para aplicar esos ámbitos.
Ejemplos de código
Por último, veamos algunas llamadas a la API de ejemplo para ilustrar cómo reciben los tokens los ámbitos y cómo se aplican.
Caso predeterminado
Supongamos que tienes una aplicación de desarrollador con productos y que la unión de los ámbitos de esos productos es A, B y C. Esta llamada a la API solicita un token de acceso, pero no especifica un 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
En este caso, el token generado tendrá los ámbitos A, B y C (el comportamiento predeterminado). Los metadatos del token tendrían un aspecto similar al siguiente:
{ "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" }
Supongamos que tienes un endpoint de API con el ámbito "A" (es decir, VerifyAccessToken requiere el ámbito "A"). Esta es la 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>
A continuación, se muestra una llamada de ejemplo a un endpoint que aplica el ámbito A:
curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI \ https://apitest.acme.com/scopecheck1/resourceA
Esta llamada GET se realiza correctamente:
{ "hello" : "Tue, 25 Nov 2014 01:35:53 UTC" }
Se completa correctamente porque la política VerifyAccessToken que se activa cuando se llama al endpoint requiere el permiso A y el token de acceso se ha concedido con los permisos A, B y C (el comportamiento predeterminado).
Caso de filtrado
Supongamos que tienes una aplicación de desarrollador con productos que tienen los ámbitos A, B, C y X. Solicitas un token de acceso e incluyes el parámetro de consulta scope
, de la siguiente manera:
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'
En este caso, el token generado tendrá los ámbitos A y X, ya que ambos son ámbitos válidos. Recuerda que la aplicación del desarrollador reconoce los ámbitos A, B, C y X. En este caso, estás filtrando la lista de productos de la API en función de estos ámbitos. Si un producto tiene el ámbito A o X, puedes configurar endpoints de API que apliquen estos ámbitos. Si un producto no tiene los ámbitos A o X (por ejemplo,tiene B, C y Z), no se podrán llamar a las APIs que aplican los ámbitos A o X con el token.
Cuando llames a la API con el nuevo token:
curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG \ https://apitest.acme.com/scopecheck1/resourceX
El proxy de API valida el token de acceso. Por ejemplo:
<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 llamada GET se realiza correctamente y devuelve una respuesta. Por ejemplo:
{ "hello" : "Tue, 25 Nov 2014 01:35:53 UTC" }
Se completa correctamente porque la política VerifyAccessToken requiere los ámbitos A o X, y el token de acceso incluye los ámbitos A y X. Por supuesto, si el elemento <Scope>
tuviera asignado el valor "B", esta llamada fallaría.
Resumen
Es importante entender cómo gestiona Apigee los ámbitos de OAuth 2.0. Estos son los puntos clave:
- Una aplicación de desarrollador "reconoce" la unión de todos los permisos definidos para todos sus productos.
- Cuando una aplicación solicita un token de acceso, puede especificar los permisos que quiere tener. Apigee (el servidor de autorización) debe determinar qué ámbitos asignará al token de acceso en función de (a) los ámbitos solicitados y (b) los que reconoce la aplicación del desarrollador.
- Si Apigee no está configurado para comprobar el ámbito (falta el elemento
<Scope>
en la política VerifyAccessToken o está vacío), la llamada a la API se realizará correctamente siempre que el ámbito insertado en el token de acceso coincida con uno de los ámbitos reconocidos por la aplicación de desarrollador registrada (uno de los ámbitos de la lista "principal" de ámbitos de la aplicación). - Si un token de acceso no tiene ningún ámbito asociado, solo funcionará en los casos en los que Apigee no tenga en cuenta el ámbito (falta el elemento
<Scope>
en la política VerifyAccessToken o está vacío).