Skip to content

E2E Identity Propagation → Object Storage (oci-service)

Folder: user_e2e_token_exchange_os_oci-service/

The token_factory target_tool="oci-service" reference. A user logs in via IDCS OAuth2, and their access token is propagated through a FastAPI backend to mcp_os, which exchanges it for an OCI TokenExchangeSigner to call the Object Storage API on behalf of the user.

Architecture

Browser ──► FastAPI backend (port 8000)
                 │  OAuth2 authorization code flow (IDCS), CSRF-protected
                 │  Proxies tool calls with BearerAuth
                 ▼
         MCP Object Storage server (port 3002)
                 │  OCITokenAuthMiddleware validates JWT (JWKS) before any tool runs
                 │  Tools read the token from the verified auth context, never a tool argument
                 │  TokenFactory.get_token(token, 'oci-service') → OCI TokenExchangeSigner
                 ▼
         OCI Object Storage

Code

from oci.identity import IdentityClient
from auth.token_factory import TokenFactory

factory = TokenFactory()
signer = factory.get_token(caller_access_token, "oci-service")

identity_client = IdentityClient(config={"region": "us-chicago-1"}, signer=signer)
regions = identity_client.list_regions().data

mcp_os's src/auth/ociclients.py runs exactly this shape:

factory = TokenFactory()
signer = factory.get_token(raw_token, "oci-service")
return oci.object_storage.ObjectStorageClient(config={"region": region}, signer=signer)

raw_token comes from the request's verified auth context (fastmcp.server.dependencies.get_access_token()), never a client-supplied tool parameter — TokenFactory decodes the credential it's given with verify_signature=False by design, trusting that OCITokenAuthMiddleware already validated it.

MCP Tools Used

Tool Parameters
get_os_namespace region
list_objects region, bucket_name
upload_object_remote region, bucket_name, object_name, object_content
delete_object region, bucket_name, object_name
whoami —

Token Flow

  1. User clicks Login → /login issues a single-use, random state value and redirects to IDCS /oauth2/v1/authorize
  2. IDCS authenticates user → redirects to /callback?code=...&state=...
  3. Backend validates state (CSRF protection), exchanges code for access_token
  4. Backend redirects to /#access_token=<token> (URL fragment); UI stores it in localStorage
  5. UI attaches Authorization: Bearer <token> to all /os/* requests
  6. Backend creates a FastMCP Client with BearerAuth(token) → calls an MCP tool on mcp_os
  7. mcp_os's OCITokenAuthMiddleware validates the JWT against the Identity Domain's JWKS → TokenFactory.get_token(token, 'oci-service') returns an OCI TokenExchangeSigner → the signer authenticates the Object Storage API call under that identity

oci-service vs. oracle-database vs. oauth

Aspect oracle-database (ADB pattern) oci-service (this pattern)
MCP server mcp_adw :8003 mcp_os :3002
Return type DB token + private key OCI TokenExchangeSigner
OCI SDK client ADW thin driver ObjectStorageClient

oci-service is specifically for native OCI SDK clients that accept a signer= parameter (Object Storage, Identity, and similar). It's not the right choice for a plain HTTPS Bearer-token call — see E2E Identity Propagation → OIC for a pattern where oauth, not oci-service, was the correct choice for exactly that reason.

When to use: Applications that read or write OCI Object Storage on behalf of a human user and need the storage operations attributed to that user's identity for audit.