Observação:

Acessar com Segurança o Oracle Integration usando Tokens de Acesso do Microsoft Entra ID

Introdução

À medida que os clientes do Oracle Integration adotam estratégias multicloud, geralmente precisam conectar aplicativos e processos de negócios em diferentes provedores de nuvem. Por exemplo, uma empresa pode ter um aplicativo em execução no Microsoft Azure que precisa acessar dados de aplicativos do Oracle Cloud Infrastructure. Normalmente, você obteria um token do OCI IAM (Oracle Cloud Infrastructure Identity and Access Management) para recuperar esses dados. No entanto, usar vários provedores de nuvem significa lidar com vários tokens, o que pode ser complexo e representar riscos de segurança.

Imagine como seria conveniente usar um token OAuth para integração com aplicativos em diferentes provedores de nuvem. Este tutorial é sobre o uso de um provedor OAuth de terceiros para chamar o fluxo do Oracle Integration.

Arquitetura

Vamos visualizar o fluxo da solução:

Imagem 1

O processo começa com o usuário ou o aplicativo de negócios obtendo um token OAuth do Microsoft Entra ID. Uma vez adquirido, esse token é usado para chamar o ponto final exposto por meio do Gateway de API do OCI. O OCI API Gateway, configurado para usar um autorizador personalizado OCI Functions (anteriormente conhecido como Oracle Functions), primeiro chama essa função de autorizador para validar o token. Após a validação bem-sucedida, ele chama o ponto final de backend real, que é o fluxo do Oracle Integration.

Agora, vamos mergulhar nos detalhes da implementação desse processo. Para simplificar, vamos dividi-lo em três etapas:

Por que usar os tipos de concessão de asserção ROPC (Credenciais de Senha do Proprietário do Recurso) e JWT (JSON Web Token), respectivamente, para obter o token de acesso do Microsoft Entra ID e do OCI IAM?

O uso conjunto de concessões de asserção ROPC e JWT fornece uma abordagem simplificada e segura para lidar com autenticação e troca de token em um ambiente multicloud.

Público-alvo

Objetivos

Pré-requisitos

Tarefa 1: Registrar um Aplicativo com o Microsoft Entra ID

Para usar os recursos do IAM do Microsoft Entra ID, incluindo acesso a recursos protegidos (APIs gráficas), você deve registrar um aplicativo.

  1. Registrar uma aplicação. Para obter mais informações, consulte Registrar um aplicativo na plataforma de identidade da Microsoft.

  2. Anote o valor Application (client) ID na seção Visão Geral.

    Imagem 2

  3. Vá para Gerenciar, Certificados e Segredos e adicione um segredo do cliente. Anote o valor do segredo, pois ele será usado em uma tarefa posterior.

    Imagem 3

Tarefa 2: Etapas de Pré-requisito para Asserção de Usuário JWT no Domínio de Identidades do OCI

  1. Conclua as tarefas de Pré-requisito aqui: Pré-requisitos para Asserção do Usuário JWT.

  2. Depois que o aplicativo Oracle Integration é validado para os escopos necessários, pares de chaves autoassinados são gerados e um aplicativo confidencial é configurado. Observe o valor do escopo, private_key.pem, ID do Cliente e Segredo do Cliente.

    Observação: Ao importar a chave privada como um parceiro confiável no aplicativo confidencial, use o mesmo alias que está sendo usado durante a criação dos pares de chaves autoassinados e anote o alias para tarefas posteriores.

  3. Crie um grupo dinâmico para permitir que o tipo de recurso function de um compartimento específico possa ler segredos do serviço OCI Vault.

    Imagem 6

Tarefa 3: Criar os Segredos no OCI Vault

Use a opção geração manual de segredos do OCI Vault para armazenar os segredos coletados das Tarefas 1 e 2. Para obter mais informações, consulte Criando um Segredo em um Vault.

Imagem 4

Depois que os segredos forem criados, copie o valor do OCID da seção Informações do Segredo e armazene-o para tarefas posteriores.

Imagem 5

Tarefa 4: Criar e Configurar o Arquivo func.py

Usaremos o OCI Functions como autorizador personalizado para validar o token de acesso do Microsoft Entra ID e gerar o token de acesso do OCI IAM como back_end_token.

  1. Para começar, crie um aplicativo. No serviço OCI Functions, um aplicativo é um agrupamento lógico de funções. As propriedades especificadas para um aplicativo determinam a alocação de recursos e a configuração de todas as funções desse aplicativo. Para obter mais informações, consulte Criando Aplicativos.

  2. Depois que o aplicativo for criado, adicione a configuração ao aplicativo. Vamos buscar os seguintes itens do nosso código de função, tornando-o mais portátil e configurável sem modificar o código. Informe Chave e Valor e clique em +.

    Adicione os IDs de cliente do Microsoft Entra ID, domínios de identidade do OCI, o OCID dos segredos coletados na Tarefa 3, o alias, o escopo coletado da Tarefa 2 e um ponto final de gráfico https://graph.microsoft.com/v1.0/me no qual o token do microsoft Entra ID será validado.

    Imagem 7

  3. Para criar a função, vá para Conceitos Básicos e clique em Iniciar o OCI Cloud Shell para abrir o cloud shell interativo no estilo Linux em seu browser. Depois que o OCI Cloud Shell for carregado, você poderá criar, desenvolver e implantar a função Oracle de autorizador personalizado diretamente no OCI Cloud Shell.

  4. Para criar a função usando a CLI (Interface de Linha de Comando) do Fn Project, informe o comando a seguir para uma função Python fn init --runtime python MyCustomAuthorizer e clique em Enter.

    Imagem 8

  5. O texto padronizado da função foi criado, agora ele pode ser editado de acordo para incluir a lógica do autorizador personalizado. Altere o diretório para a pasta de funções e edite o arquivo func.py. Copie e cole o trecho de código a seguir.

    Imagem 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"}
                )
    
    
    • Importações

      • io, json, logging, datetime, time, base64: Bibliotecas Python padrão para tratamento de E/S, dados JSON, registro em log, operações de data e hora e codificação base64.
      • jwt: Biblioteca para codificação e decodificação de JWT (JSON Web Tokens).
      • requests: Biblioteca para fazer solicitações HTTP.
      • HTTPBasicAuth: Classe para tratar a autenticação básica HTTP.
      • serialization, default_backend: Na biblioteca de criptografia, usada para tratar operações criptográficas.
      • ociVault: Um módulo personalizado para interagir com o OCI Vault.
    • Variável Global

      • oauth_apps: Dicionário para armazenar a configuração do aplicativo.
    • Serviço Functions

      • initContext(context): A finalidade dessa função é inicializar a configuração do aplicativo usando os dados de contexto e os segredos do OCI Vault. Ele recebe o objeto de dicionário de contexto que é chamado como a primeira coisa no método do handler principal e recupera os segredos do OCI Vault usando a função getSecret() explicada na Tarefa 5.

      • getAuthContext(token, client_apps): Preenche e retorna o contexto de autenticação do Gateway de API do OCI. Extrai e decodifica o token de acesso. Chama a função introspectToken() para validar o token com o ID Entra. Se o token for válido, definirá o contexto de autenticação, chamará a função getBackEndAuthToken() para recuperar o token de backend do OCI IAM e definirá o tempo de expiração. Se o token não for válido, defina o cabeçalho wwwAuthenticate para indicar um erro de autenticação.

      • introspectToken(access_token, introspection_endpoint, client_id, client_secret): Valida o token com o introspection_endpoint fornecido. Faz uma solicitação GET para o ponto final de introspecção com o token. Retorna a resposta do ponto final de introspecção ou validação. Como o Microsoft Entra ID não tem o ponto final da API de introspecção OAuth, estamos chamando o ponto final configurado usando o token recebido como entrada.

      • getBackEndAuthToken(token_endpoint, client_id, client_secret, scope, alias, principal): Carrega a chave privada de um arquivo PEM. Cria reivindicações JWT e as codifica em uma asserção JWT. Prepara o payload e os cabeçalhos para a solicitação de token. Faz uma solicitação POST para o ponto final do token para obter o token de backend e retorna o token de backend para a função getAuthContext().

      • handler(ctx, data: io.BytesIO = None): Função principal que trata a execução da função. Inicializa o contexto OAuth usando a função initContext() e chama a função getAuthContext() para obter o contexto de autenticação. Retorna uma resposta 200 se o token for válido; caso contrário, retorna uma resposta 401. Registra e retorna uma resposta 500 em caso de erros.

Tarefa 5: Criar e Configurar o Arquivo ociVault.py

Crie um arquivo ociVault.py na mesma pasta e cole o trecho de código a seguir. Essa função do utilitário lê os segredos do serviço 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

Observação: Mantenha o arquivo private_key.pem da Tarefa 2 na mesma pasta.

Imagem 10

Tarefa 5: Testar a Função

Para testar a função, precisamos implantar a função e chamá-la passando o token do Microsoft Entra ID como entrada.

  1. Navegue até a pasta de funções e execute o comando a seguir fn -v deploy --app MyCustomAuthorizer para implantá-la. O comando da CLI do Fn Project estará criando a função e implantando-a no aplicativo OCI Functions.

    Imagem 11

    Observação: inclua fdk>=0.1.74, requests, oci, pyjwt, serialization no arquivo requirements.txt antes de implantar o aplicativo de funções.

  2. Gere um token de acesso com base no ID do Microsoft Entra usando o fluxo ROPC OAuth 2.0 usando o cliente Postman.

    Imagem 12

  3. Anote o token de acesso para gerar um payload.json, que será passado como entrada para testar as Funções do OCI. Mantenha o arquivo JSON no mesmo diretório de funções.

    Imagem 13

  4. Depois de salvar o payload, você poderá executar o comando a seguir para imitar a execução da função, pois ela será chamada por meio do Gateway de API do OCI, cat payload.json | fn invoke <AppName> <function name>, conforme mostrado na imagem a seguir.

    Imagem 14

    Se o token do Microsoft Entra ID for válido, você verá uma resposta, conforme mostrado na imagem a seguir, na qual verá o valor do token do OCI IAM no valor back_end_token da estrutura de contexto.

Tarefa 6: Configurar o Gateway de API do OCI

O OCI API Gateway é uma plataforma de gerenciamento de API nativa da nuvem totalmente gerenciada e escalável que oferece um conjunto de serviços, desde a implantação rápida de API até o gerenciamento do ciclo de vida e a integração do serviço de backend. Aproveitaremos o gateway de API para mediar a autorização do Oracle Integration usando um provedor de identidades externo como o Microsoft Entra ID.

Comece criando um novo gateway de API e, em seguida, criando uma nova implantação no gateway de API.

  1. Navegue até Developer Services, API Management e Gateways. Especifique as seguintes informações e clique em Create Gateway.

    Imagem 15

    Imagem 16

  2. Na página Detalhes do Gateway, clique em Criar Implantação e especifique as informações necessárias a seguir para a sua implantação de API.

    • Nome: Digite um nome.
    • Prefixo do caminho: Defina o caminho.
    • Compartimento: Selecione o compartimento apropriado para sua implantação de API.

    Imagem 17

  3. Adicionar os detalhes da política de autenticação. É aqui que você configura o OCI Functions, que deve ser chamado como um autorizador personalizado. Selecione a função criada na Tarefa 4.

    Imagem 18

  4. Na página Rotas, configure o roteamento da API para o serviço de backend. Neste tutorial, definiremos o roteamento para o ponto final do Oracle Integration.

    Imagem 19

  5. Clique em Mostrar Políticas de Solicitação de Rota. É aqui que o usuário executaria a troca de tokens de autenticação da resposta do OCI Functions para o cabeçalho de autenticação da solicitação.

    Imagem 20

    Esta etapa envolve a definição do token de autenticação para o serviço de backend com base no provedor de identidades de backend. Em nosso cenário, estamos definindo o token do portador para o OCI IAM conforme recebido do autorizador personalizado OCI Functions. Aqui, configuramos o cabeçalho da autorização para ser substituído pelo valor ${request.auth[back_end_token]}. Observe que back_end_token faz parte do contexto na estrutura de resposta da função Oracle. Certifique-se de que essa expressão seja avaliada com sucesso após a conclusão do OCI Functions do autorizador personalizado.

  6. Após revisar com sucesso a configuração, clique em Salvar alterações para salvar a implantação e aguardar até que o estado da implantação seja alterado para Ativo.

    Imagem 21

    Após ativar a implantação de API, copie o Ponto Final (URL base) na seção Informações de implantação. Esse URL serve como ponto final para sua implantação, em que seu processo de negócios ou aplicativo chamará os pontos finais do Oracle Integration usando o token portador do Microsoft Entra ID. Usaremos o URL base na próxima tarefa.

    Imagem 22

Tarefa 7: Testar a API

Primeiro, obtenha um token de acesso do Microsoft Entra ID usando o cliente Postman. Usaremos o fluxo ROPC para garantir que o token de acesso inclua as informações de identidade necessárias.

  1. Copie o token de acesso, pois estaremos usando o mesmo ao chamar a API do gateway de API.

    Imagem 12

  2. Crie uma nova solicitação REST combinando o URL do ponto final base copiado na Tarefa 6 do gateway de API e do ponto final do Oracle Integration, conforme mostrado na imagem a seguir. Use o token bearer no cabeçalho da solicitação.

    Imagem 23

  3. Clique em Enviar para chamar a solicitação de API; ela executará o Oracle Integration e deverá fornecer uma saída bem-sucedida.

    Imagem 24

Próximas Etapas

Chamamos com sucesso a API no Oracle API Gateway usando um token OAuth do Microsoft Entra ID, recebendo uma resposta do fluxo de trigger REST do Oracle Integration. Essa integração é crucial para os clientes que conectam serviços digitais em diferentes fornecedores de nuvem.

Reconhecimentos

Mais Recursos de Aprendizagem

Explore outros laboratórios em docs.oracle.com/learn ou acesse mais conteúdo de aprendizado gratuito no canal Oracle Learning YouTube. Além disso, visite education.oracle.com/learning-explorer para se tornar um Oracle Learning Explorer.

Para obter a documentação do produto, visite o Oracle Help Center.