MCP OAuth with OCI Identity Domains
This guide describes OAuth protection for the remote MCP server using OCI IAM
Identity Domains. OAuth is optional. With no MCP_OAUTH_* settings, the MCP
server continues to run without bearer-token protection. OAuth is enabled only
when MCP_OAUTH_ENABLED=true.
There are three different roles to keep separate:
Resource server: the MCP API. It owns the audience and scopes and validates access tokens.
Authorization server: OCI IAM Identity Domains. It authenticates users, issues tokens, and manages grants.
OAuth client: the application or MCP client that requests a token and calls the MCP API.
OCI supports configuring one confidential application as both a resource server and an OAuth client. That is a valid option for this deployment. It does not make the MCP server the authorization server: OCI IAM remains the authorization server.
The recommended resource-server scope values are:
read— required for every MCP tool call.update— required only for thereloadoperation.
Depending on the OCI IAM application configuration, the issued token may contain either the configured scope value or a fully qualified scope (FQS). The MCP server must validate the value actually present in the token. In this deployment, the issued token contains the configured values directly:
read— required for every MCP tool call.update— required only for thereloadoperation.
Set MCP_OAUTH_REQUIRED_SCOPES and MCP_OAUTH_UPDATE_SCOPE to the exact
values in the JWT scope claim.
When the authorization server requires a different scope spelling in the
authorization request, set MCP_OAUTH_AUTHORIZATION_SCOPES to that request
value. For OCI Identity Domains with primary audience
oci-policy-analysis-mcp, use MCP_OAUTH_REQUIRED_SCOPES=read to validate
the JWT and MCP_OAUTH_AUTHORIZATION_SCOPES=oci-policy-analysis-mcpread to
advertise the request scope to Codex and other MCP clients.
The token request may still require the fully qualified scope. With primary
audience oci-policy-analysis-mcp and configured scope value read, request
oci-policy-analysis-mcpread; OCI IAM then issues a token whose scope claim
is read.
If a different application configuration emits a fully qualified scope, use that exact value instead.
The MCP server validates the token signature, issuer, audience, and read scope. OAuth identifies and authorizes the caller; the MCP server still reads OCI using its configured profile, instance principal, resource principal, or session token.
OCI Identity Domain issuer/metadata-host compatibility
There are two unrelated OCI OAuth details that are easy to conflate:
Scope translation is expected for this deployment. Codex requests
oci-policy-analysis-mcpread, while OCI issues a JWT withscope=read.MCP_OAUTH_AUTHORIZATION_SCOPESandMCP_OAUTH_REQUIRED_SCOPEShandle those two values respectively.Authorization-server identity is a client compatibility concern. The tenant-specific Identity Domain discovery endpoint is hosted at
https://idcs-<id>.identity.oraclecloud.com/, but it can declare the canonical JWT issuerhttps://identity.oraclecloud.com/. A strict OAuth client can reject this metadata-host/issuer mismatch before opening the browser, even when the client ID, scopes, and redirect URI are correct.
Do not set MCP_OAUTH_ISSUER to the tenant-specific host merely to make
those strings match. MCP_OAUTH_ISSUER validates the token’s actual iss
claim and must remain the issuer OCI emits.
If Codex reports No authorization support detected after it successfully
finds the protected-resource metadata, check the Identity Domain metadata
first. Possible remediation paths are:
use an OCI Identity Domain endpoint/custom domain whose discovery URL and declared issuer match exactly, if OCI supports that for the domain;
add an MCP-side authorization-server metadata compatibility endpoint that has a stable matching issuer while retaining OCI’s actual authorize/token endpoints and JWT validation; or
use the explicit bearer-token configuration temporarily while the interactive compatibility path is being completed.
Codex OAuth with the optional OCI metadata shim
The optional shim is a static discovery document hosted by the MCP service. It
does not proxy the browser, authorization code, token exchange, user
credentials, client secret, or JWT validation. Codex still calls OCI IAM
Identity Domains directly for /authorize and /token.
Enable it only when a client rejects the tenant-specific metadata host and canonical OCI issuer as different identities. Configure these additional container variables:
# Stable local logical issuer, served by the MCP application.
export MCP_OAUTH_AUTHORIZATION_SERVER_METADATA_ISSUER="https://<mcp-public-host>/oauth/oci-idcs"
# OCI IAM endpoints; Codex calls these directly.
export MCP_OAUTH_AUTHORIZATION_ENDPOINT="https://idcs-<id>.identity.oraclecloud.com/oauth2/v1/authorize"
export MCP_OAUTH_TOKEN_ENDPOINT="https://idcs-<id>.identity.oraclecloud.com/oauth2/v1/token"
With those values, the service advertises the local logical issuer in protected resource metadata and serves its OAuth metadata at both:
/.well-known/oauth-authorization-server/oauth/oci-idcs
/oauth/oci-idcs/.well-known/oauth-authorization-server
The document has a matching local issuer, but its authorization_endpoint
and token_endpoint remain the OCI Identity Domain URLs. Keep the separate
MCP_OAUTH_ISSUER=https://identity.oraclecloud.com/ value unchanged: that is
for validating OCI’s issued JWTs, not for client discovery.
Identity Domain application checklist
The deploy script can pass environment variables and create the container, but these OAuth-client controls remain a deliberate Identity Domain administrator action:
Enable Authorization Code for the application used by Codex.
Enable PKCE with
S256and permit public-client token exchange (none), because Codex does not provide an OCI client secret.Grant the application the MCP resource scope (
read; OCI may require its fully-qualified request spellingoci-policy-analysis-mcpread).Assign the intended user/group access to the application.
Do not register the LB’s
/mcpURL as a redirect URI. It is the resource server, not Codex’s callback receiver.After the first successful
codex mcp loginbegins, copy the full derived loopback redirect URI that Codex sends inredirect_uriand add it exactly to the Integrated Application. Codex appends a server-specific suffix to the configured base callback URL.
For a local workstation, configure the Codex global callback base once:
mcp_oauth_callback_url = "http://127.0.0.1:9999/oauth/callback"
mcp_oauth_callback_port = 9999
mcp_oauth_credentials_store = "auto"
Then add or enable the Streamable HTTP MCP entry, set its OAuth client ID and resource URL, and run:
codex mcp login <server-name> --scopes oci-policy-analysis-mcpread
Deployment order
Create the OCIR repository and build/push an image with
local-build-push-mcp-ocir.sh.Configure the Identity Domain resource server/client controls above.
Configure the HTTPS LB, certificate, backend health check, and NSGs.
Run
local-deploy-mcp-container-instance.shwith the regular OAuth values plus the three shim variables when needed.Verify
/health, protected-resource metadata, and authorization-server metadata through the LB before enabling the Codex entry.Run
codex mcp login, register its derived callback URI in the Identity Domain app, and repeat login to complete the authorization-code flow.
1. App Creation: resource server, OAuth client, and scopes
Create one Confidential Application in the target OCI IAM Identity Domain. The same application may be configured in both sections below.
Resource-server configuration
Open the Identity Domain and select Integrated applications.
Select Add application → Confidential Application → Launch workflow.
Give the application a name such as
OCI Policy Analysis MCP.In OAuth configuration, enable Configure this application as a resource server now.
Set the Primary audience to a stable value such as
oci-policy-analysis-mcp. This becomesMCP_OAUTH_AUDIENCE; it is not necessarily the client ID.Add these scopes:
read update
The resource-server application defines what access to MCP means. A client must later be granted these scopes before OCI IAM will issue a token that contains them.
Granting access to a group of users
For Authorization Code access, assign the permitted Identity Domain group to the OAuth client application. Users in that group can then authenticate through the application and receive its allowed resource scope. The MCP server validates the resulting token; it does not query Identity Domain group membership during each request.
If the server must enforce a group independently of application assignment, configure an OCI IAM custom token claim containing group membership and add application-level authorization for that claim. Scope validation alone does not distinguish one user group from another.
Client configuration
If this same application will request tokens, also enable Configure this application as a client now and select only the grant types needed by the deployment:
Client Credentials for a public/service-to-service integration.
Authorization Code for a browser-based three-legged integration.
Refresh Token if long-lived user sessions need token renewal.
Resource Owner only for a tightly trusted integration that explicitly collects the user’s credentials. Authorization Code is preferred for interactive users because user credentials are not exposed to the client.
For a separate caller application, configure that application as a client and grant it the MCP resource scopes instead. The resource-server app and caller app do not have to be the same application.
For Authorization Code, register the caller’s exact HTTPS redirect URL in the client application. The redirect URL belongs to the OAuth client handling the browser flow, not to the MCP bearer-token validation endpoint.
Activate the application after saving the configuration. Record:
Identity Domain issuer URL, matching the token
issclaim exactly.Signing certificate JWKS URL, normally ending in
/admin/v1/SigningCert/jwk.Resource-server primary audience.
Authorization server URL.
Client ID and client secret, if the application is configured as a confidential client.
The two resource scopes.
See Oracle’s Register the Application in OCI IAM and Configuring OAuth documentation.
Start the MCP server
Set the OAuth resource-server settings on the container instance and start or restart the existing MCP container:
export MCP_OAUTH_ENABLED="true"
export MCP_OAUTH_ISSUER="https://idcs-<id>.identity.oraclecloud.com/"
export MCP_OAUTH_JWKS_URI="https://idcs-<id>.identity.oraclecloud.com/admin/v1/SigningCert/jwk"
export MCP_OAUTH_AUDIENCE="oci-policy-analysis-mcp"
export MCP_OAUTH_REQUIRED_SCOPES="read"
export MCP_OAUTH_UPDATE_SCOPE="update"
export MCP_OAUTH_AUTHORIZATION_SCOPES="oci-policy-analysis-mcpread"
export MCP_OAUTH_RESOURCE_SERVER_URL="https://<mcp-public-host>"
export MCP_OAUTH_AUTHORIZATION_SERVER_URL="https://idcs-<id>.identity.oraclecloud.com/"
export MCP_OAUTH_ALGORITHM="RS256"
The deployment script or container entrypoint then starts the server with the
configured transport, host, port, and OCI authentication mode. If
MCP_OAUTH_ENABLED=true and a required value is missing, startup fails before
loading tenancy data and prints a setup error.
MCP_OAUTH_REQUIRED_SCOPES is space- or comma-separated and applies to every
MCP tool. MCP_OAUTH_UPDATE_SCOPE is checked separately for reload.
2. Connecting with a Bearer Token
The MCP server is a protected resource. A caller presents an access token in the HTTP request:
curl -i \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' \
https://<mcp-public-host>/mcp
The JWT should contain:
issequal toMCP_OAUTH_ISSUER.audcontainingMCP_OAUTH_AUDIENCE.scopecontainingread.updateonly for callers allowed to reload.
Expected behavior:
No bearer token: MCP access is rejected.
Read scope: normal search and analysis tools work.
Read without update scope:
reloadis rejected.Read plus update scope:
reloadis authorized, subject to the server’s OCI authentication mode and loaded-data requirements.
Start the MCP server
Use the same environment block from App Creation, then restart the container. No callback is required for this bearer-token connection: the caller obtains the token, and the MCP server only validates it.
3. Two-legged service integration: Client Credentials
Client Credentials is the two-legged machine-to-machine flow. In this section, “public” means an externally reachable service integration; it does not mean an OAuth public client. Client Credentials requires the client to protect its credentials. There is no resource owner signing in through a browser and no callback. The OAuth client authenticates to OCI IAM with its client ID and secret and requests the MCP resource scope.
If using the same app as both resource server and client, grant that app its own MCP scopes under its client Resources configuration. A separate client application can be granted the same scopes instead.
Example token request:
export BASIC_AUTH="$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)"
curl -sS \
-H "Authorization: Basic $BASIC_AUTH" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&scope=oci-policy-analysis-mcpread" \
"https://idcs-<id>.identity.oraclecloud.com/oauth2/v1/token"
Request oci-policy-analysis-mcpupdate only for a service that is authorized
to invoke reload. Use the returned access_token as $ACCESS_TOKEN in the
Bearer Token example. The resulting JWT scope is still validated as update.
Start the MCP server
Start the MCP server with the resource-server environment block from App Creation. The server does not need the client ID or client secret; those remain with the calling service. The server validates the resulting JWT against the issuer, JWKS endpoint, audience, and scopes.
See Oracle’s Client Credentials Grant Type documentation.
Diagnostics and troubleshooting
At MCP_LOG_LEVEL=WARNING, each MCP tool invocation logs the caller and granted
scopes without logging the bearer token or client secret:
MCP OAuth invoke policy_search client_id=<caller> user_displayname=<user> ca_name=<account> scopes=<granted-scopes>
OCI IAM commonly supplies the human-readable fields as user_displayname and
ca_name; the fallback client_id is usually the token subject (sub). These
identity fields are useful for audit diagnostics but are still user data. Keep
the warning logs access-controlled and avoid enabling request-input logging at
INFO in production unless policy text and request filters are acceptable to
store in logs.
Common failures:
issmismatch: copy the exactissvalue from the JWT intoMCP_OAUTH_ISSUER.audmismatch: use the resource server’s primary audience, not the client ID unless they are intentionally identical.Missing scope: grant the client access to the MCP resource scope and request the fully qualified scope from the token endpoint. Configure
MCP_OAUTH_REQUIRED_SCOPESandMCP_OAUTH_UPDATE_SCOPEwith the exact value present in the JWTscopeclaim.JWKS failure: verify that the JWKS endpoint is reachable anonymously by the container.
Reload denied: ensure the token contains the configured
MCP_OAUTH_UPDATE_SCOPEvalue.Callback mismatch: ensure the
redirect_uriexactly matches the URL registered on the OAuth client, including scheme, host, path, and port.