Note:

Acceso seguro a Oracle Integration mediante tokens de acceso desde el ID de Microsoft Entra

Introducción

A medida que los clientes de Oracle Integration adoptan estrategias multinube, a menudo necesitan conectar aplicaciones y procesos de negocio en diferentes proveedores de nube. Por ejemplo, una compañía puede tener una aplicación que se ejecute en Microsoft Azure que necesite acceder a los datos de las aplicaciones de Oracle Cloud Infrastructure. Normalmente, obtendría un token de Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) para recuperar estos datos. Sin embargo, el uso de múltiples proveedores de nube significa tratar con múltiples tokens, lo que puede ser complejo y plantear riesgos de seguridad.

Imagínese lo práctico que sería si pudiera utilizar un token OAuth para la integración con aplicaciones en distintos proveedores de nube. Este tutorial trata sobre el uso de un proveedor OAuth de terceros para llamar al flujo de Oracle Integration.

Arquitectura

Visualicemos el flujo de soluciones:

Imagen 1

El proceso comienza con el usuario o la aplicación de negocio que obtiene un token OAuth del ID de Microsoft Entra. Una vez adquirido, este token se utiliza para llamar al punto final expuesto a través de OCI API Gateway. OCI API Gateway, configurado para utilizar OCI Functions de autorizador personalizado (anteriormente conocido como Oracle Functions), llama primero a esta función de autorizador para validar el token. Una vez validada correctamente, llama al punto final de backend real, que es el flujo de Oracle Integration.

Ahora, profundicemos en los detalles de la implementación de este proceso. Para simplificarlo, lo dividiremos en tres pasos:

¿Por qué utilizar los tipos de permiso de afirmación Credenciales de contraseña de propietario de recursos (ROPC) y Token web de JSON (JWT) respectivamente para obtener el token de acceso de Microsoft Entra ID y OCI IAM?

El uso conjunto de permisos de afirmación de ROPC y JWT proporciona un enfoque optimizado y seguro para manejar la autenticación y el intercambio de tokens en un entorno multinube.

Público Objetivo

Objetivos

Requisitos

Tarea 1: Registro de una aplicación con el ID de Microsoft Entra

Para utilizar las capacidades de IAM de Microsoft Entra ID, incluido el acceso a recursos protegidos (API de Graph), debe registrar una aplicación.

  1. Registro de una Aplicación. Para obtener más información, consulte Registro de una aplicación con la plataforma de identidad de Microsoft.

  2. Anote el valor Application (client) ID de la sección Visión general.

    Imagen 2

  3. Vaya a Gestionar, Certificados y secretos y agregue un secreto de cliente. Anote el valor secreto, ya que se utilizará en una tarea posterior.

    Imagen 3

Tarea 2: Pasos de Requisitos para la Afirmación de Usuario JWT en el Dominio de Identidad de OCI

  1. Complete las tareas de requisitos desde aquí: Requisitos para la afirmación de usuario de JWT.

  2. Una vez que la aplicación Oracle Integration se valida para los ámbitos necesarios, se generan pares de claves autofirmadas y se configura una aplicación confidencial. Tenga en cuenta el valor de ámbito, private_key.pem, ID de cliente y Secreto de cliente.

    Nota: Al importar la clave privada como partner de confianza en la aplicación confidencial, utilice la misma alias que se utiliza durante la creación de los pares de claves autofirmadas y anote alias para tareas posteriores.

  3. Cree un grupo dinámico para permitir que el tipo de recurso function de un compartimento específico pueda leer secretos del servicio OCI Vault.

    Imagen 6

Tarea 3: Creación de los secretos en OCI Vault

Utilice la opción de generación manual de secretos de OCI Vault para almacenar los secretos recopilados de la tarea 1 y la tarea 2. Para obtener más información, consulte Creación de un secreto en un almacén.

Imagen 4

Una vez creados los secretos, copie el valor de OCID de la sección Información secreta y almacénelo para tareas posteriores.

Imagen 5

Tarea 4: Creación y configuración del archivo func.py

Utilizaremos OCI Functions como autorizador personalizado para validar el token de acceso de Microsoft Entra ID y generar el token de acceso de OCI IAM como back_end_token.

  1. Para comenzar, cree una aplicación. En OCI Functions, una aplicación es una agrupación lógica de funciones. Las propiedades especificadas para una aplicación determinan la asignación y configuración de recursos para todas las funciones de esa aplicación. Para obtener más información, consulte Creación de Aplicaciones.

  2. Una vez creada la aplicación, agregue la configuración a la aplicación. Recuperaremos los siguientes elementos de nuestro código de función, haciéndolo más portátil y configurable sin modificar el código. Introduzca Clave y Valor y haga clic en +.

    Agregue los ID de cliente de Microsoft Entra ID, los dominios de identidad de OCI, el OCID de los secretos recopilados en la tarea 3, el alias, el ámbito recopilado de la tarea 2 y un punto final de gráfico https://graph.microsoft.com/v1.0/me con el que se validará el token microsoft Entra ID.

    Imagen 7

  3. Para crear la función, vaya a Introducción y haga clic en Iniciar OCI Cloud Shell para abrir el shell en la nube interactivo de estilo Linux en el explorador. Una vez que se haya cargado OCI Cloud Shell, puede crear, desarrollar y desplegar la función de Oracle del autorizador personalizado directamente desde OCI Cloud Shell.

  4. Para crear la función mediante la interfaz de línea de comandos (CLI) de Fn Project, introduzca el siguiente comando para una función de Python fn init --runtime python MyCustomAuthorizer y haga clic en Intro.

    Imagen 8

  5. Se crea el texto fijo de la función, ahora se puede editar según corresponda para incluir la lógica de autorizador personalizada. Cambie el directorio a la carpeta de funciones y edite el archivo func.py. Copia y pega el siguiente fragmento de código.

    Imagen 9

    import io
    import json
    import logging
    import jwt
    import datetime
    from datetime import timedelta
    import time
    import base64
    
    from fdk import response
    import requests
    from requests.auth import HTTPBasicAuth
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.backends import default_backend
    import ociVault
    
    oauth_apps = {}
    
    def initContext(context):
        # This method takes elements from the Application Context and from OCI Vault to create the OAuth App Clients object.
        if (len(oauth_apps) < 2):
            try:
                logging.getLogger().info("initContext: Initializing context")
    
                oauth_apps['idcs'] = {'introspection_endpoint': context['idcs_token_endpoint'],
                                    'client_id': context['idcs_app_client_id'],
                                    'scope':context['idcs_oauth_scope'],
                                    'alias':context['alias'],
                                    'client_secret': ociVault.getSecret(context['idcs_client_secret_ocid'])}
                oauth_apps['AD'] = {'token_endpoint': context['ad_endpoint'],
                                    'client_id': context['ad_app_client_id'],
                                    'client_secret': ociVault.getSecret(context['ad_client_secret_ocid'])}
    
            except Exception as ex:
                logging.getLogger().error("initContext: Failed to get config or secrets" + str(ex))
                raise
    
    
    def getAuthContext(token, client_apps):
        # This method populates the Auth Context that will be returned to the gateway.
        auth_context = {}
        access_token = token[len('Bearer '):]
        jwtToken = json.loads(json.dumps(jwt.decode(access_token, options={"verify_signature": False})))
        # Calling MSFT to validate the token
        try:
        logging.getLogger().info("getAuthContext: Calling Token Introspection function") 
        respIntrospectToken = introspectToken(access_token, client_apps['AD']['token_endpoint'], client_apps['AD']['client_id'], client_apps['AD']['client_secret'])
        except Exception as ex:
                logging.getLogger().error("getAuthContext: Failed to introspect token" + str(ex))
                raise
    
        # If AD confirmed the token valid and active, we can proceed to populate the auth context
        if (respIntrospectToken.status_code == 200):
            auth_context['active'] = True
            auth_context['principal'] = jwtToken['upn']
            auth_context['scope'] = 'https://graph.microsoft.com/.default'
            # Retrieving the back-end Token
            backend_token = getBackEndAuthToken(client_apps['idcs']['introspection_endpoint'], client_apps['idcs']['client_id'], client_apps['idcs']['client_secret'],client_apps['idcs']['scope'],client_apps['idcs']['alias'],auth_context['principal'])
    
            # The maximum TTL for this auth is the lesser of the API Client Auth (Entra ID) and the Gateway Client Auth (OCI IAM)
            if (datetime.datetime.fromtimestamp(jwtToken['exp']) < (datetime.datetime.utcnow() + timedelta(seconds=backend_token['expires_in']))):
                auth_context['expiresAt'] = (datetime.datetime.fromtimestamp(jwtToken['exp'])).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
            else:
                auth_context['expiresAt'] = (datetime.datetime.utcnow() + timedelta(seconds=backend_token['expires_in'])).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
            # Storing the back_end_token in the context of the auth decision so we can map it to Authorization header using the request/response transformation policy
            auth_context['context'] = {'back_end_token': ('Bearer ' + str(backend_token['access_token']))}
    
        else:
            # API Client token is not active, so we will go ahead and respond with the wwwAuthenticate header
            auth_context['active'] = False
            auth_context['wwwAuthenticate'] = 'Bearer realm=\"identity.oraclecloud.com\"'
    
        return(auth_context)
    
    def introspectToken(access_token, introspection_endpoint, client_id, client_secret):
        # This method simply invokes the introspection api as configured in the configuration screen.  
        # The real validation happens in the getAuthContext function.  
        #payload = {'token': access_token}
        headers = {'Accept': 'application/json',
                'Authorization':'Bearer '+access_token}
        try:
            logging.getLogger().info("introspectToken: Introspecting Token") 
            resp = requests.get(introspection_endpoint,
                                headers=headers)
            print(resp)
    
        except Exception as ex:
            logging.getLogger().error("introspectToken: Failed to introspect token" + str(ex))
            raise
    
        return resp
    
    def getBackEndAuthToken(token_endpoint, client_id, client_secret, scope, alias, principal):
        # This method gets the token from the back-end system (ORDS in this case)
        try:
            logging.getLogger().info("getBackEndAuthToken: Getting Backend Token") 
            print("Sub is " + principal)
    
            with open("private_key.pem", "rb") as key_file:
                private_key = serialization.load_pem_private_key(
                    key_file.read(),
                    password=None,
                    backend=default_backend()
                )
    
            headers = {
                "alg": "RS256",
                "typ": "JWT",
                "kid": "abc"
            }
    
            claims = {
                "sub": principal,
                "aud": "https://identity.oraclecloud.com/",
                "iss": client_id,
                "iat": int(time.time()),
                "exp": int(time.time()) + 3600,  # 1 hour expiration
                "jti": "8c7df446-bfae-40be-be09-0ab55c655436"  # random number
            }
    
            logging.getLogger().info("Claims : ")
            logging.getLogger().info(claims) 
    
            jwt_assertion = jwt.encode(
                payload=claims,
                key=private_key,
                algorithm="RS256",
                headers=headers
            )
            logging.getLogger().info("Assertion is :") 
            logging.getLogger().info(jwt_assertion) 
    
            encoded = client_id + ":" + client_secret
            baseencoded = base64.urlsafe_b64encode(encoded.encode('UTF-8')).decode('ascii')
            payload = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                    'scope':scope, 'assertion':jwt_assertion}
            headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Authorization': 'Basic %s' % baseencoded, 'Accept': '*/*'}
            backend_token = json.loads(requests.post(token_endpoint,
                                                    data=payload,
                                                    headers=headers).text)
            logging.getLogger().info("Backend token in generated :") 
            logging.getLogger().info(backend_token) 
    
        except Exception as ex:
            logging.getLogger().error("getBackEndAuthToken: Failed to get ORDS token" + str(ex))
            raise
    
        return backend_token
    
    
    
    def handler(ctx, data: io.BytesIO = None):
        initContext(dict(ctx.Config()))
        logging.getLogger().info(oauth_apps)
    
        auth_context = {}
        try:
            logging.getLogger().info("handler: Started Function Execution") 
            gateway_auth = json.loads(data.getvalue())
            auth_context = getAuthContext(gateway_auth['token'], oauth_apps)
            if (auth_context['active']):
                logging.getLogger().info('Authorizer returning 200...')
                return response.Response(
                    ctx,
                    response_data=json.dumps(auth_context),
                    status_code = 200,
                    headers={"Content-Type": "application/json"}
                    )
            else:
                logging.getLogger().info('Authorizer returning 401...')
                return response.Response(
                    ctx,
                    response_data=json.dumps(str(auth_context)),
                    status_code = 401,
                    headers={"Content-Type": "application/json"}
                    )
    
        except (Exception, ValueError) as ex:
            logging.getLogger().info('error parsing json payload: ' + str(ex))
    
            return response.Response(
                ctx,
                response_data=json.dumps(str(auth_context)),
                status_code = 500,
                headers={"Content-Type": "application/json"}
                )
    
    
    • Importaciones

      • io, json, logging, datetime, time, base64: bibliotecas Python estándar para manejar operaciones de E/S, datos JSON, registro, fecha y hora y codificación base64.
      • jwt: biblioteca para codificar y decodificar tokens web JSON (JWT).
      • requests: biblioteca para realizar solicitudes HTTP.
      • HTTPBasicAuth: clase para manejar la autenticación básica HTTP.
      • serialization, default_backend: desde la biblioteca de criptografía, que se utiliza para manejar operaciones criptográficas.
      • ociVault: módulo personalizado para interactuar con OCI Vault.
    • Variable Global

      • Diccionario oauth_apps: para almacenar la configuración de la aplicación.
    • Functions

      • initContext(context): El objetivo de esta función es inicializar la configuración de la aplicación mediante los datos de contexto y los secretos de OCI Vault. Recibe el objeto de diccionario de contexto que se llama como lo primero en el método de manejador principal y recupera los secretos de OCI Vault mediante la función getSecret() que se explica en la tarea 5.

      • getAuthContext(token, client_apps): rellena y devuelve el contexto de autenticación para el gateway de API de OCI. Extrae y decodifica el token de acceso. Llama a la función introspectToken() para validar el token con el ID de Entra. Si el token es válido, define el contexto de autenticación, llama a la función getBackEndAuthToken() para recuperar el token de backend de OCI IAM y define la hora de caducidad. Si el token no es válido, define la cabecera wwwAuthenticate para indicar un error de autenticación.

      • introspectToken(access_token, introspection_endpoint, client_id, client_secret): valida el token con el introspection_endpoint proporcionado. Realiza una solicitud GET al punto final de introspección con el token. Devuelve la respuesta del punto final de introspección o validación. Como el ID de Microsoft Entra no tiene el punto final de API de introspección OAuth, estamos llamando al punto final configurado mediante el token recibido como entrada.

      • getBackEndAuthToken(token_endpoint, client_id, client_secret, scope, alias, principal): Carga la clave privada desde un archivo PEM. Crea reclamaciones de JWT y las codifica en una afirmación de JWT. Prepara la carga útil y las cabeceras para la solicitud de token. Realiza una solicitud POST al punto final de token para obtener el token de backend y devuelve el token de backend a la función getAuthContext().

      • Función principal handler(ctx, data: io.BytesIO = None): que gestiona la ejecución de la función. Inicializa el contexto OAuth mediante la función initContext() y llama a la función getAuthContext() para obtener el contexto de autenticación. Devuelve una respuesta 200 si el token es válido; de lo contrario, devuelve una respuesta 401. Registra y devuelve una respuesta 500 en caso de errores.

Tarea 5: Creación y configuración del archivo ociVault.py

Cree un archivo ociVault.py en la misma carpeta y pegue el siguiente fragmento de código. Esta función de utilidad lee los secretos del servicio OCI Vault.

# Utility Function to get secrets from OCI Vault
import logging
import oci
import base64

def getSecret(ocid):
    signer = oci.auth.signers.get_resource_principals_signer()
    try:
        client = oci.secrets.SecretsClient({}, signer=signer)
        secret_content = client.get_secret_bundle(ocid).data.secret_bundle_content.content.encode('utf-8')
        decrypted_secret_content = base64.b64decode(secret_content).decode('utf-8')
    except Exception as ex:
        logging.getLogger().error("getSecret: Failed to get Secret" + ex)
        print("Error [getSecret]: failed to retrieve", ex, flush=True)
        raise
    return decrypted_secret_content

Nota: Mantenga el archivo private_key.pem de la tarea 2 en la misma carpeta.

Imagen 10

Tarea 5: Prueba de la función

Para probar la función, necesitamos desplegar la función y, a continuación, llamarla transfiriendo el token de ID de Microsoft Entra como entrada.

  1. Vaya a la carpeta de funciones y ejecute el siguiente comando fn -v deploy --app MyCustomAuthorizer para desplegarla. El comando de la CLI de Fn Project creará la función y desplegará la misma en la aplicación OCI Functions.

    Imagen 11

    Nota: Incluya fdk>=0.1.74, requests, oci, pyjwt, serialization en el archivo requirements.txt antes de desplegar la aplicación de funciones.

  2. Genere un token de acceso desde el ID de Microsoft Entra mediante el flujo de ROPC OAuth 2.0 mediante el cliente Postman.

    Imagen 12

  3. Anote el token de acceso para generar un payload.json, que se transferirá como entrada para probar OCI Functions. Mantenga el archivo JSON en el mismo directorio de funciones.

    Imagen 13

  4. Una vez que haya guardado la carga útil, puede ejecutar el siguiente comando para imitar la ejecución de la función, ya que se llamará a través del gateway de API de OCI, cat payload.json | fn invoke <AppName> <function name>, como se muestra en la siguiente imagen.

    Imagen 14

    Si el token de ID de Microsoft Entra es válido, verá una respuesta como se muestra en la siguiente imagen, donde verá el valor del token de OCI IAM en el valor back_end_token de la estructura de contexto.

Tarea 6: Configuración de OCI API Gateway

OCI API Gateway es una plataforma de gestión de API nativa en la nube totalmente gestionada, escalable y que ofrece un conjunto de servicios que van desde el rápido despliegue de API hasta la gestión del ciclo de vida y la integración de servicios backend. Utilizaremos el gateway de API para mediar en la autorización de Oracle Integration mediante un proveedor de identidad externo como Microsoft Entra ID.

Empiece por crear un nuevo gateway de API y, a continuación, cree un nuevo despliegue en el gateway de API.

  1. Vaya a Developer Services, API Management y Gateways. Introduzca la siguiente información y haga clic en Crear gateway.

    Imagen 15

    Imagen 16

  2. En la página Detalles de gateway, haga clic en Crear despliegue e introduzca la siguiente información necesaria para el despliegue de API.

    • Nombre: introduzca un nombre.
    • Prefijo de Ruta de Acceso: Defina la ruta de acceso.
    • Compartimento: seleccione el compartimento adecuado para el despliegue de API.

    Imagen 17

  3. Agregue los detalles de la política de autenticación. Aquí es donde configura OCI Functions, que se llamará como autorizador personalizado. Seleccione la función creada en la tarea 4.

    Imagen 18

  4. En la página Rutas, configure el enrutamiento de API al servicio de backend. En este tutorial, definiremos el enrutamiento al punto final de Oracle Integration.

    Imagen 19

  5. Haga clic en Mostrar políticas de solicitud de ruta, donde el usuario realizará el intercambio de tokens de autenticación de la respuesta de OCI Functions a la cabecera de autenticación de la solicitud.

    Imagen 20

    Este paso implica definir el token de autenticación para el servicio de backend según el proveedor de identidad de backend. En nuestro escenario, estamos definiendo el token de portador para OCI IAM tal como se recibió del autorizador personalizado OCI Functions. Aquí, configuramos la cabecera de autorización para que se sustituya con el valor ${request.auth[back_end_token]}. Tenga en cuenta que back_end_token es parte del contexto en la estructura de respuesta de función de Oracle. Asegúrese de que esta expresión se evalúa correctamente después de que finalice el autorizador personalizado OCI Functions.

  6. Después de revisar correctamente la configuración, haga clic en Guardar cambios para guardar el despliegue y espere hasta que el estado del despliegue cambie a Activo.

    Imagen 21

    Después de activar el despliegue de API, copie el punto final (URL base) de la sección Información de despliegue. Esta URL sirve como punto final para el despliegue, donde el proceso de negocio o la aplicación llamarán a los puntos finales de Oracle Integration mediante el token de portador de ID de Microsoft Entra. Utilizaremos la URL base en la siguiente tarea.

    Imagen 22

Tarea 7: Prueba de la API

En primer lugar, obtenga un token de acceso de Microsoft Entra ID mediante el cliente Postman. Utilizaremos el flujo de ROPC para garantizar que el token de acceso incluya la información de identidad necesaria.

  1. Copie el token de acceso, ya que lo utilizaremos al llamar a la API desde el gateway de API.

    Imagen 12

  2. Cree una nueva solicitud REST que combine la URL de punto final base copiada en la tarea 6 desde el gateway de API y el punto final de Oracle Integration, como se muestra en la siguiente imagen. Utilice el token de portador en la cabecera de solicitud.

    Imagen 23

  3. Haga clic en Enviar para llamar a la solicitud de API, ejecutará Oracle Integration y proporcionará una salida correcta.

    Imagen 24

Pasos Siguientes

Llamamos correctamente a la API en Oracle API Gateway mediante un token OAuth del ID de Microsoft Entra y recibimos una respuesta del flujo de disparador de REST de Oracle Integration. Esta integración es fundamental para que los clientes conecten servicios digitales entre distintos proveedores de servicios en la nube.

Confirmaciones

Más recursos de aprendizaje

Explore otros laboratorios en docs.oracle.com/learn o acceda a más contenido de formación gratuita en el canal YouTube de Oracle Learning. Además, visita education.oracle.com/learning-explorer para convertirte en un Oracle Learning Explorer.

Para obtener documentación sobre el producto, visite Oracle Help Center.