Token Factory¶
Location: inception_core/libs/src/auth/token_factory/
Package: part of inception_core/libs — installed as a local editable package
TokenFactory is the central credential translation library for the platform. It converts an incoming caller credential (an IDCS access token, OCI signer dict, or OCI CLI profile name) into exactly the credential format a downstream target needs — Oracle DB scoped token, OCI SDK signer, or OAuth access token — so every consumer gets a consistent, tested entry point without implementing JWT assertion signing, UPST handling, Vault secret retrieval, or token exchange individually.
Why it exists¶
Different downstream targets in this platform consume different credential types:
| Target | Required credential |
|---|---|
| Oracle Autonomous Database | Scoped DB token + session private key |
| OCI SDK clients (Object Storage, etc.) | TokenExchangeSigner backed by a UPST |
| Downstream OAuth APIs | Regular OAuth Bearer token |
| Identity resolution | user_id extracted from a token claim |
Without TokenFactory, every client would duplicate the same JWT assertion construction, Identity Propagation Trust setup, Vault secret reads, and OCI signer wiring. TokenFactory centralises that logic once, behind a two-call API.
Quick start¶
from token_factory import TokenFactory
factory = TokenFactory.from_env()
result = factory.get_token(caller_access_token, "oracle-database", scope="urn:oracle:db::id::*")
db_token = result["token"]
session_private_key_pem = result["private_key_pem"]
API¶
TokenFactory.from_env()¶
Loads all configuration from environment variables once. Returns a configured TokenFactory instance ready for get_token() calls.
factory.get_token(credential, target_tool, scope=None)¶
Performs the credential translation.
| Parameter | Required | Description |
|---|---|---|
credential |
yes | Caller credential — access token string, OCI signer dict, or OCI CLI profile name |
target_tool |
yes | Requested output type: oracle-database, oci-service, oauth, or userid |
scope |
conditional | Required for oracle-database. Optional for oauth and userid. Not used for oci-service. |
Supported flows¶
oracle-database¶
Exchanges a caller access token for a scoped Oracle Database token plus the matching session private key material. Used by MCP ADW server and any agent that opens a DB connection under the end user's identity.
result = factory.get_token(caller_token, "oracle-database", scope="urn:oracle:db::id::*")
# result["token"] → DB access token
# result["private_key_pem"] → session private key (PEM)
# result["expires_in"] → seconds until expiry
Dependencies: JWT assertion signing, IdentityPropagationTrust, TokenExchangeSigner, OCI Identity Data Plane generate_scoped_access_token.
oci-service¶
Returns an OCI TokenExchangeSigner backed by a UPST. Pass it directly to any OCI SDK client as signer=. Used by mcp_os (ociclients.py) to sign Object Storage API calls on behalf of the authenticated user.
from oci.object_storage import ObjectStorageClient
signer = factory.get_token(caller_token, "oci-service")
client = ObjectStorageClient(config={"region": "us-chicago-1"}, signer=signer)
Dependencies: JWT assertion signing, IdentityPropagationTrust, TokenExchangeSigner.
oauth¶
Exchanges a caller credential for a regular OAuth access token from the Identity Domain. Accepts three credential shapes:
# From an access token string (JWT assertion flow)
result = factory.get_token(caller_token, "oauth", scope="urn:opc:idm:__myscopes__")
# From an OCI signer dict
signer_dict = {"tenancy": "...", "user": "...", "fingerprint": "...",
"region": "...", "private_key_content": "<pem>"}
result = factory.get_token(signer_dict, "oauth")
# From an OCI CLI profile name
result = factory.get_token("DEFAULT", "oauth")
# result["token"] → OAuth access token
# result["token_type"] → "Bearer"
# result["expires_in"] → seconds until expiry
userid¶
Resolves the user_id for the caller without handing the full access token to the consumer. Useful for audit logging and session attribution.
result = factory.get_token(caller_token, "userid")
# result["user_id"] → resolved subject identifier
Accepted credential shapes¶
| Shape | Supported flows | Notes |
|---|---|---|
| Access token string | all flows | Required for oracle-database and oci-service |
| OCI signer dict | oauth, userid |
Needs tenancy, user, fingerprint, region, and one of private_key_file/private_key_content |
| OCI CLI profile name | oauth, userid |
Resolved from ~/.oci/config |
Return types¶
target_tool |
Return type | Fields |
|---|---|---|
oracle-database |
dict |
token, private_key_pem, expires_in, token_type, issued_token_type |
oci-service |
TokenExchangeSigner |
OCI SDK signer object — not a dict |
oauth |
dict |
token, expires_in, token_type, issued_token_type |
userid |
dict |
user_id, issued_token_type |
Configuration¶
TokenFactory.from_env() reads the following environment variables:
Required¶
| Variable | Purpose |
|---|---|
DOMAIN_URL |
Identity Domain base URL — https://<domain>.identity.oraclecloud.com:443 |
DOMAIN_OCI_REGION |
OCI region for TokenExchangeSigner and Identity Data Plane calls |
TOKEN_EXCHANGE_CREDS_OCID |
OCI Vault secret OCID containing client_id:client_secret for the token-factory confidential app |
TOKEN_FACTORY_ISSUER |
JWT iss claim used when building assertions — must match the configured IdentityPropagationTrust issuer |
TOKEN_FACTORY_PRIVATE_KEY_PEM or TOKEN_FACTORY_PRIVATE_KEY_SECRET_OCID or TOKEN_FACTORY_PRIVATE_KEY_PATH |
RSA private key for JWT assertion signing (one source required) |
TOKEN_FACTORY_CERT_PEM or TOKEN_FACTORY_CERT_SECRET_OCID or TOKEN_FACTORY_CERT_PATH |
X.509 certificate matching the private key (one source required) |
Optional¶
| Variable | Default | Purpose |
|---|---|---|
TOKEN_FACTORY_KID |
SIGNING_KEY |
JWT kid header for oauth/userid assertions — set to the Identity Domain certificate store alias |
TOKEN_FACTORY_PRIVATE_KEY_PASSPHRASE |
— | Passphrase for encrypted private keys |
TOKEN_FACTORY_OCI_REGION |
derived from OCID | Region override for Vault secret reads |
TOKEN_FACTORY_OCI_AUTH_MODE |
instance-principal |
OCI auth mode: instance-principal or oci-profile |
TOKEN_FACTORY_OCI_AUTH_PROFILE |
DEFAULT |
OCI CLI profile when oci-profile mode is set |
TOKEN_FACTORY_PROPAGATION_TRUST_CLAIM |
— | Additional JWT claim injected for oracle-database and oci-service — format claim_name:claim_value |
TOKEN_FACTORY_UPST_PUBLIC_KEY_FORMAT |
single-line |
Ephemeral public key format during UPST exchange: single-line or pem |
TOKEN_EXCHANGE_TIMEOUT_SECONDS |
30 |
HTTP timeout for upstream token exchange calls |
TOKEN_FACTORY_LOG_FULL_ASSERTION |
— | Log full compact JWT (debug only — assertions are replay-sensitive) |
Minimal environment example¶
DOMAIN_URL="https://<domain>.identity.oraclecloud.com:443"
DOMAIN_OCI_REGION="us-chicago-1"
TOKEN_EXCHANGE_CREDS_OCID="ocid1.vaultsecret.oc1..<app-secret>"
TOKEN_FACTORY_ISSUER="https://upst.tokenfactory/"
TOKEN_FACTORY_PRIVATE_KEY_SECRET_OCID="ocid1.vaultsecret.oc1..<private-key>"
TOKEN_FACTORY_CERT_SECRET_OCID="ocid1.vaultsecret.oc1..<certificate>"
TOKEN_FACTORY_KID="token-factory-cert"
TOKEN_FACTORY_PROPAGATION_TRUST_CLAIM="upstclient:tokenexchange"
OCI signer fallback order¶
Factory-owned OCI SDK clients (for Vault reads, Identity Data Plane calls) resolve credentials in this order:
- Resource Principals — automatic inside OCI Functions or Container Instances
- Instance Principals — attempted with a 5-second timeout; skipped automatically when the metadata endpoint (
169.254.169.254) is unreachable - API Key (
~/.oci/config) — falls back to theDEFAULTprofile on developer laptops
No extra configuration is needed for local development as long as ~/.oci/config is present and valid.
OCI prerequisites¶
1. Confidential OAuth app (token-factory client)¶
Create a confidential app in the Identity Domain used by DOMAIN_URL:
- Enable
client_credentials - Enable
jwt-assertion - Store
client_id:client_secretin the Vault secret referenced byTOKEN_EXCHANGE_CREDS_OCID
2. IdentityPropagationTrust (for oracle-database and oci-service)¶
curl -X POST \
-H 'Authorization: Bearer <admin-token>' \
-H 'Content-Type: application/json' \
-d '{
"active": true,
"allowImpersonation": false,
"issuer": "https://upst.tokenfactory/",
"name": "Token Trust JWT to UPST",
"oauthClients": ["<token-factory-client-id>"],
"publicCertificate": "<base64-encoded-leaf-cert>",
"subjectClaimName": "sub",
"subjectMappingAttribute": "userName",
"subjectType": "User",
"type": "JWT",
"clientClaimName": "upstclient",
"clientClaimValues": ["tokenexchange"],
"schemas": ["urn:ietf:params:scim:schemas:oracle:idcs:IdentityPropagationTrust"]
}' \
'https://<domain>.identity.oraclecloud.com:443/admin/v1/IdentityPropagationTrusts'
3. Identity Domain certificate store (TOKEN_FACTORY_KID)¶
For oauth and userid flows:
- Load the token-factory public key certificate into the Identity Domain certificate store
- Set the certificate alias to match TOKEN_FACTORY_KID
- Configure the same certificate locally via TOKEN_FACTORY_CERT_*
- Configure the matching private key via TOKEN_FACTORY_PRIVATE_KEY_*
OCI Vault secret format¶
| Secret | Contents |
|---|---|
TOKEN_EXCHANGE_CREDS_OCID |
Plain text: client_id:client_secret |
TOKEN_FACTORY_PRIVATE_KEY_SECRET_OCID |
PEM-encoded RSA private key |
TOKEN_FACTORY_CERT_SECRET_OCID |
PEM-encoded X.509 certificate |
The runtime principal must have IAM read permission on every referenced secret.
Error types¶
| Exception | Cause |
|---|---|
ValueError |
Unsupported target_tool, missing scope for oracle-database, or non-token credential passed to oracle-database/oci-service |
ConfigurationError |
Missing or invalid environment variable at config-load time |
TokenExchangeError |
Upstream token exchange or JWT decode failure |
Where it is used¶
| Consumer | Flow | File |
|---|---|---|
MCP SQLcl server (mcp_sqlcl) |
DB token for caller identity propagation | src/auth/ociclients.py → get_db_token() |
MCP Object Storage server (mcp_os) |
OCI signer for Object Storage calls | src/auth/ociclients.py → get_oci_signer() |
| E2E ADB pattern | DB token for VPD row-level security | user_e2e_token_exchange_adb backend |
| E2E Object Storage pattern | OCI signer for Object Storage operations | user_e2e_token_exchange_os backend |
Related¶
- Solution Patterns —
user_e2e_token_exchange_adbanduser_e2e_token_exchange_osshow full end-to-end usage - MCP SQLcl Server — uses
get_db_token()fromociclients.py - MCP Object Storage Server — uses
get_oci_signer()fromociclients.py inception_core/libs/src/auth/token_factory/_factory_support.py— internal implementation (JWT assertion building, UPST exchange, Vault client)