Skip to content

Service Token Acquisition (oauth)

Folder: service_get_token_oauth/

The reference for token_factory's target_tool="oauth" flow, covering both credential shapes it accepts:

  1. CLI example (examples/api_key_to_iam_token.py + src/agent_get_token/) — a non-interactive service identity (OCI API key signer credential) exchanged for an oauth token. No browser, no user.
  2. NL2SQL chat app (src/backend/app.py + ui.html) — an interactive user's IDCS login token exchanged for a fresh oauth token on every request, used to call mcp_sqlcl.

Its counterpart is E2E Identity Propagation → ADB, the oracle-database reference.


1. CLI example — OCI signer credential

Service (OCI API key) ──► IDCS token endpoint (signed POST) ──► Bearer token ──► MCP server

src/agent_get_token/client.py exposes two helpers that delegate the actual exchange to token_factory.TokenFactory:

from agent_get_token import build_signer_credential, build_signer_only_factory

credential = build_signer_credential(
    tenancy_ocid=OCI_IDENTITY_TENANCY_OCID,
    user_ocid=OCI_IDENTITY_USER_OCID,
    fingerprint=OCI_IDENTITY_FINGERPRINT,
    region=OCI_IDENTITY_REGION,
    private_key_file="~/.oci/oci_api_key.pem",
)
factory = build_signer_only_factory(
    domain_url="https://<domain>.identity.oraclecloud.com",
    domain_region=OCI_IDENTITY_REGION,
)

result = factory.get_token(credential, "oauth", scope="urn:opc:idm:__myscopes__")
token = result["token"]                    # Bearer token string
header = f"{result['token_type']} {token}" # "Bearer eyJ..."

build_signer_only_factory() constructs a TokenFactoryConfig directly instead of going through TokenFactory()'s environment loader, because that loader unconditionally requires JWT-bearer-assertion settings (TOKEN_FACTORY_ISSUER, TOKEN_FACTORY_CREDS, TOKEN_FACTORY_PRIVATE_KEY_*, TOKEN_FACTORY_CERT_*) that a pure OCI signer credential exchange never uses.

Required .env keys:

OCI_IDENTITY_TOKEN_URL=https://<domain>.identity.oraclecloud.com/oauth2/v1/token
OCI_IDENTITY_TENANCY_OCID=ocid1.tenancy.oc1..
OCI_IDENTITY_USER_OCID=ocid1.user.oc1..
OCI_IDENTITY_FINGERPRINT=xx:xx:xx:..
OCI_IDENTITY_REGION=us-chicago-1
OCI_IDENTITY_PRIVATE_KEY_FILE=~/.oci/oci_api_key.pem

When to use: CLI agents, background jobs, or any non-interactive service that is an OCI principal and needs a short-lived Bearer token without storing a client secret.


2. NL2SQL chat app — user access token

A user signs in via IDCS OAuth2 (authorization-code flow), asks a question in plain English, and the backend translates it to SQL and runs it via mcp_sqlcl. Every call authenticates to mcp_sqlcl with a freshly oauth-exchanged token, not the user's raw login token — a direct, literal instance of token_factory's documented oauth example, inline, per request:

from auth.token_factory import TokenFactory

factory = TokenFactory()
result = factory.get_token(caller_access_token, "oauth", scope=oauth_scope)
oauth_access_token = result["token"]

The exchange happens fresh on every call rather than once at login, since the exchanged token's lifetime (a few minutes) is much shorter than the original IDCS login token's — nothing about it is worth caching client-side.

Architecture

Browser ──► FastAPI backend (port 8000)
                 │  OAuth2 authorization code flow (IDCS), CSRF-protected
                 │  Stores the caller's original IDCS login token client-side
                 ▼
         /nl2sql: TokenFactory.get_token(caller_token, "oauth", scope=...)
                 │  fresh downstream oauth token, per request
                 ▼
         MCP SQLcl server (port 3001)
                 │  OCITokenAuthMiddleware validates the exchanged JWT (JWKS)
                 │  schema_information → OCI GenAI (NL2SQL) → execute_sql
                 ▼
         OCI Autonomous Database

Prerequisites

  • IDCS confidential application with authorization_code and JWT Assertion grants enabled, and the TOKEN_FACTORY_KID certificate loaded into the Identity Domain certificate store
  • mcp_sqlcl running on port 3001
  • OCI GenAI model access (compartment + model id)

When to use: Any interactive application that needs to call a Bearer-token-secured MCP server or REST API on behalf of a logged-in user, where the downstream system doesn't need OCI-SDK-signer-level access.