Nota

Accesso sicuro a Oracle Integration mediante token di accesso da ID Entra Microsoft

Introduzione

Man mano che i clienti di Oracle Integration adottano strategie multicloud, spesso devono connettere applicazioni e processi aziendali tra diversi fornitori di servizi cloud. Ad esempio, un'azienda potrebbe avere un'applicazione in esecuzione su Microsoft Azure che deve accedere ai dati dalle applicazioni Oracle Cloud Infrastructure. In genere, è possibile ottenere un token da Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) per recuperare questi dati. Tuttavia, l'utilizzo di più provider cloud significa gestire più token, che possono essere complessi e comportare rischi per la sicurezza.

Immagina quanto sarebbe conveniente se potessi utilizzare un token OAuth per l'integrazione con le applicazioni di diversi provider cloud. Questa esercitazione descrive l'utilizzo di un provider OAuth di terze parti per richiamare il flusso di Oracle Integration.

Architettura

Visualizziamo il flusso della soluzione:

Immagine 1

Il processo inizia con l'utente o l'applicazione business che ottiene un token OAuth dall'ID di Microsoft Entra. Una volta acquisito, questo token viene utilizzato per richiamare l'endpoint esposto tramite il gateway API OCI. Il gateway API OCI, configurato per utilizzare un provider di autorizzazioni personalizzato OCI Functions (precedentemente noto come Oracle Functions), chiama prima questa funzione del provider di autorizzazioni per convalidare il token. Al termine della convalida, richiama l'endpoint backend effettivo, ovvero il flusso di Oracle Integration.

Ora, analizziamo i dettagli dell'implementazione di questo processo. Per semplicità lo divideremo in tre passaggi:

Perché utilizzare rispettivamente i tipi di privilegio di asserzione ROPC (Resource Owner Password Credential) e JWT (JSON Web Token) per ottenere il token di accesso da Microsoft Entra ID e da OCI IAM?

L'uso congiunto dei privilegi di asserzione ROPC e JWT fornisce un approccio semplificato e sicuro per gestire l'autenticazione e lo scambio di token in un ambiente multi-cloud.

Destinatari

Obiettivi

Prerequisiti

Task 1: Registrare un'applicazione con Microsoft Entra ID

Per utilizzare le funzionalità IAM di Microsoft Entra ID, incluso l'accesso alle risorse protette (API grafiche), è necessario registrare un'applicazione.

  1. Registrare un'applicazione. Per ulteriori informazioni, vedere Registrare un'applicazione con la piattaforma di identità Microsoft.

  2. Prendere nota del valore Application (client) ID nella sezione Panoramica.

    Immagine 2

  3. Andare a Gestisci, Certificati e segreti e aggiungere un segreto client. Prendere nota del valore segreto così come verrà utilizzato in un task successivo.

    Immagine 3

Task 2: Passi dei prerequisiti per l'asserzione utente JWT nel dominio di Identity OCI

  1. Completare i task dei prerequisiti da qui: Prerequisiti per l'asserzione utente JWT.

  2. Una volta convalidata l'applicazione Oracle Integration per gli ambiti richiesti, vengono generate coppie di chiavi con firma automatica e viene configurata un'applicazione riservata. Si noti il valore scope, private_key.pem, Client ID e Client Secret.

    Nota: durante l'importazione della chiave privata come partner sicuro nell'applicazione riservata, utilizzare lo stesso valore alias utilizzato durante la creazione delle coppie di chiavi con firma automatica e annotare il valore alias per i task successivi.

  3. Creare un gruppo dinamico per consentire al tipo di risorsa function da un compartimento specifico di leggere i segreti dal servizio OCI Vault.

    Immagine 6

Task 3: Creare i segreti in OCI Vault

Utilizzare l'opzione Generazione manuale dei segreti di OCI Vault per memorizzare i segreti raccolti dai task 1 e 2. Per ulteriori informazioni, vedere Creazione di un segreto in un vault.

Immagine 4

Una volta creati i segreti, copiare il valore OCID dalla sezione Informazioni segrete e memorizzarlo per i task successivi.

Immagine 5

Task 4: creare e configurare il file func.py

Verrà utilizzato OCI Functions come provider di autorizzazione personalizzato per convalidare il token di accesso ID Microsoft Entra e generare il token di accesso IAM OCI come back_end_token.

  1. Per avviare, creare un'applicazione. Nelle funzioni OCI, un'applicazione è un raggruppamento logico di funzioni. Le proprietà specificate per un'applicazione determinano l'allocazione e la configurazione delle risorse per tutte le funzioni dell'applicazione. Per ulteriori informazioni, vedere Creazione di richieste.

  2. Una volta creata l'applicazione, aggiungere la configurazione all'applicazione. Recupereremo i seguenti elementi dal nostro codice funzione, rendendolo più portatile e configurabile senza modificare il codice. Immettere Chiave e Valore e fare clic sul segno +.

    Aggiungere gli ID client dall'ID Entra Microsoft, dai domini di Identity OCI, dall'OCID dei segreti raccolti nel Task 3, dall'alias, dall'ambito raccolti dal Task 2 e da un endpoint grafico https://graph.microsoft.com/v1.0/me in base al quale verrà convalidato il token ID Entra microsoft.

    Immagine 7

  3. Per creare la funzione, andare alla Guida introduttiva e fare clic su Avvia OCI Cloud Shell per aprire la cloud shell interattiva in stile Linux nel browser. Una volta caricata OCI Cloud Shell, puoi creare, sviluppare e distribuire la funzione Oracle del responsabile autorizzazioni personalizzato direttamente da OCI Cloud Shell.

  4. Per creare la funzione utilizzando l'interfaccia CLI (Fn Project Command Line Interface), immettere il comando seguente per una funzione Python fn init --runtime python MyCustomAuthorizer e fare clic su Invio.

    Immagine 8

  5. Viene creato il boilerplate della funzione, ora può essere modificato di conseguenza per includere la logica del responsabile autorizzazioni personalizzato. Spostarsi nella directory delle funzioni e modificare il file func.py. Copia e incolla il seguente frammento di codice.

    Immagine 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"}
                )
    
    
    • Importazioni

      • io, json, logging, datetime, time, base64: librerie Python standard per la gestione di I/O, dati JSON, log, operazioni di data e ora e codifica base64.
      • jwt: libreria per la codifica e la decodifica dei token Web JSON (JWT).
      • requests: libreria per effettuare richieste HTTP.
      • HTTPBasicAuth: classe per la gestione dell'autenticazione di base HTTP.
      • serialization, default_backend: dalla libreria di crittografia, utilizzata per gestire le operazioni di crittografia.
      • ociVault: modulo personalizzato per l'interazione con OCI Vault.
    • Variabile globale

      • oauth_apps: Dizionario per memorizzare la configurazione dell'applicazione.
    • Funzioni

      • initContext(context): Lo scopo di questa funzione è inizializzare la configurazione dell'applicazione utilizzando i dati di contesto e i segreti del vault OCI. Riceve un oggetto dizionario di contesto che viene richiamato come prima cosa nel metodo handler principale e recupera i segreti da OCI Vault utilizzando la funzione getSecret() spiegata nel task 5.

      • getAuthContext(token, client_apps): Popola e restituisce il contesto di autenticazione per il gateway API OCI. Estrae e decodifica il token di accesso. Chiama la funzione introspectToken() per convalidare il token con ID Entra. Se il token è valido, imposta il contesto di autenticazione, chiama la funzione getBackEndAuthToken() per recuperare il token backend da IAM OCI e imposta l'ora di scadenza. Se il token non è valido, imposta l'intestazione wwwAuthenticate per indicare un errore di autenticazione.

      • introspectToken(access_token, introspection_endpoint, client_id, client_secret): Convalida il token con il valore introspection_endpoint fornito. Esegue una richiesta GET all'endpoint di introspezione con il token. Restituisce la risposta dall'endpoint di introspezione o convalida. Poiché l'ID Microsoft Entra non dispone di un endpoint API di introspezione OAuth, viene richiamato l'endpoint configurato utilizzando il token ricevuto come input.

      • getBackEndAuthToken(token_endpoint, client_id, client_secret, scope, alias, principal): Carica la chiave privata da un file PEM. Crea le richieste di rimborso JWT e le codifica in un'asserzione JWT. Prepara il payload e le intestazioni per la richiesta di token. Effettua una richiesta POST all'endpoint del token per ottenere il token backend e restituisce il token backend alla funzione getAuthContext().

      • handler(ctx, data: io.BytesIO = None): Funzione principale che gestisce l'esecuzione della funzione. Inizializza il contesto OAuth utilizzando la funzione initContext() e chiama la funzione getAuthContext() per ottenere il contesto di autenticazione. Restituisce una risposta 200 se il token è valido, altrimenti restituisce una risposta 401. Esegue il log e restituisce una risposta di 500 in caso di errori.

Task 5: creare e configurare il file ociVault.py

Creare un file ociVault.py nella stessa cartella e incollare lo snippet di codice seguente. Questa funzione di utility legge i segreti dal servizio 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: mantenere il file private_key.pem dal task 2 nella stessa cartella.

Immagine 10

Task 5: Testare la funzione

Per testare la funzione, è necessario distribuire la funzione e quindi richiamarla passando il token ID di Microsoft Entra come input.

  1. Passare alla cartella delle funzioni ed eseguire il comando fn -v deploy --app MyCustomAuthorizer seguente per distribuirla. Il comando CLI di Fn Project creerà la funzione e distribuirà la stessa nell'applicazione OCI Functions.

    Immagine 11

    Nota: prima di distribuire l'applicazione Functions, includere fdk>=0.1.74, requests, oci, pyjwt, serialization nel file requirements.txt.

  2. Generare un token di accesso dall'ID Entra Microsoft utilizzando il flusso ROPC OAuth 2.0 utilizzando il client Postman.

    Immagine 12

  3. Prendere nota del token di accesso per generare un valore payload.json che verrà passato come input per eseguire il test delle funzioni OCI. Conservare il file JSON nella stessa directory delle funzioni.

    Immagine 13

  4. Dopo aver salvato il payload, è possibile eseguire il comando seguente per simulare l'esecuzione della funzione in quanto verrà richiamata tramite il gateway API OCI, cat payload.json | fn invoke <AppName> <function name>, come mostrato nell'immagine seguente.

    Immagine 14

    Se il token ID Microsoft Entra è valido, verrà visualizzata una risposta come mostrato nella seguente immagine in cui verrà visualizzato il valore del token IAM OCI nel valore back_end_token della struttura del contesto.

Task 6: Configurare OCI API Gateway

OCI API Gateway è una piattaforma di gestione delle API completamente gestita, scalabile e cloud nativa che offre una suite di servizi, dalla rapida distribuzione delle API alla gestione del ciclo di vita e all'integrazione dei servizi backend. Utilizzeremo il gateway API per mediare l'autorizzazione per Oracle Integration utilizzando un provider di identità esterno come l'ID di Microsoft Entra.

Iniziare creando un nuovo gateway API e quindi creando una nuova distribuzione nel gateway API.

  1. Passare a Developer Services, API Management e Gateways. Immettere le informazioni seguenti e fare clic su Crea gateway.

    Immagine 15

    Immagine 16

  2. Nella pagina Dettagli gateway fare clic su Crea distribuzione e immettere le informazioni necessarie riportate di seguito per la distribuzione dell'API.

    • Nome: immettere un nome.
    • Prefisso percorso: definire il percorso.
    • Compartimento: selezionare il compartimento appropriato per la distribuzione API.

    Immagine 17

  3. Aggiungere i dettagli del criterio di autenticazione. Questa è la posizione in cui si configurano le funzioni OCI, che devono essere richiamate come responsabile autorizzazioni personalizzato. Selezionare la funzione creata nel task 4.

    Immagine 18

  4. Nella pagina Percorsi configurare l'instradamento API al servizio backend. In questa esercitazione verrà definito l'instradamento all'endpoint di Oracle Integration.

    Immagine 19

  5. Fare clic su Mostra criteri di richiesta di instradamento, dove l'utente eseguirà lo swap dei token di autenticazione dalla risposta OCI Functions all'intestazione di autenticazione della richiesta.

    Immagine 20

    Questo passo implica l'impostazione del token di autenticazione per il servizio backend in base al provider di identità backend. Nel nostro scenario, stiamo impostando il token bearer per IAM OCI come ricevuto dalle funzioni OCI del responsabile autorizzazioni personalizzato. In questo caso, viene configurata l'intestazione di autorizzazione da sostituire con il valore ${request.auth[back_end_token]}. Si noti che back_end_token fa parte del contesto nella struttura di risposta della funzione Oracle. Assicurarsi che questa espressione valuti correttamente dopo il completamento delle funzioni OCI del responsabile autorizzazioni personalizzato.

  6. Dopo aver esaminato correttamente la configurazione, fare clic su Salva modifiche per salvare la distribuzione e attendere che lo stato della distribuzione venga modificato in Attivo.

    Immagine 21

    Dopo aver attivato la distribuzione dell'API, copiare l'endpoint (URL di base) dalla sezione Informazioni sulla distribuzione. Questo URL funge da endpoint per la distribuzione, in cui il processo aziendale o l'applicazione richiameranno gli endpoint Oracle Integration utilizzando il token portatore ID Microsoft Entra. Verrà utilizzato l'URL di base nel task successivo.

    Immagine 22

Task 7: eseguire il test dell'API

In primo luogo, ottenere un token di accesso da Microsoft Entra ID utilizzando il client Postman. Il flusso ROPC verrà utilizzato per garantire che il token di accesso includa le informazioni di identità necessarie.

  1. Copiare il token di accesso poiché verrà utilizzato lo stesso, richiamando l'API dal gateway API.

    Immagine 12

  2. Crea una nuova richiesta REST che combina l'URL dell'endpoint di base copiato nel task 6 dal gateway API e dall'endpoint Oracle Integration come mostrato nell'immagine riportata di seguito. Utilizzare il token bearer nell'intestazione della richiesta.

    Immagine 23

  3. Fare clic su Invia per richiamare la richiesta API, eseguire Oracle Integration e fornire un output riuscito.

    Immagine 24

Passi successivi

Richiamo dell'interfaccia API su Oracle API Gateway riuscito utilizzando un token OAuth dall'ID di Microsoft Entra, ricezione di una risposta dal flusso trigger REST di Oracle Integration. Questa integrazione è fondamentale per i clienti che connettono servizi digitali tra diversi fornitori di servizi cloud.

conferme

Altre risorse di apprendimento

Esplora altri laboratori su docs.oracle.com/learn o accedi a più contenuti gratuiti sulla formazione su Oracle Learning YouTube channel. Inoltre, visita education.oracle.com/learning-explorer per diventare un Oracle Learning Explorer.

Per la documentazione del prodotto, visita l'Oracle Help Center.