Fix OCI Provider issue in 3.x version. Add OCI auth provider example … (#4116)

* Fix OCI Provider issue in 3.x version. Add OCI auth provider example and test

* Fix OCI Provider issue in 3.x version. Add OCI auth provider example and test. Fixed a couple of minor issues in README.

* Rerun CI
This commit is contained in:
Kiran Thakkar 2026-05-10 07:08:00 -07:00 committed by GitHub
commit cf59a4511f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 215 additions and 0 deletions

View file

@ -0,0 +1,51 @@
# Oracle (OCI IAM (Identity Domain)) OAuth Example
This example demonstrates how to use the OCI IAM OAuth provider with FastMCP servers.
## Setup
### 1. OCI App Registration
1. Login to OCI console (https://cloud.oracle.com for OCI commercial cloud).
2. From "Identity & Security" menu, open Domains page.
3. On the Domains list page, select the domain in which you want to create MCP server OAuth client. If you need help finding the list page for the domain, see [Listing Identity Domains.](https://docs.oracle.com/en-us/iaas/Content/Identity/domains/to-view-identity-domains.htm#view-identity-domains).
4. On the details page, select Integrated applications. A list of applications in the domain is displayed.
5. Select Add application.
6. In the Add application window, select Confidential Application.
7. Select Launch workflow.
8. In the Add application details page, Enter name and description and create the application.
9. Once the Integrated Application is created, Click on "OAuth configuration" tab.
10. Click on "Edit OAuth configuration" button.
11. Configure the application as OAuth client by selecting "Configure this application as a client now" radio button.
12. Select "Authorization code" grant type. If you are planning to use the same OAuth client application for token exchange, select "Client credentials" grant type as well. In the sample, we will use the same client.
13. For Authorization grant type, select redirect URL. In most cases, this will be the MCP server URL followed by "/auth/callback". For example http://localhost:8000/auth/callback
14. Click on "Submit" button to update OAuth configuration for the client application.
15. Make sure to Activate the client application.
16. Note down client ID and client secret for the application. You'll use these values when configuring the OCIProvider in the MCP server.
For details instructions with screenshots, please refer to [FastMCP OCI Provider Documentation](https://gofastmcp.com/integrations/oci).
### 2. Set Environment Variables
```bash
# Required
FASTMCP_SERVER_AUTH_IDCS_CLIENT_ID=your-application-client-id
FASTMCP_SERVER_AUTH_IDCS_CLIENT_SECRET=your-client-secret-value
FASTMCP_SERVER_AUTH_IDCS_DOMAIN=your-iam-domain-url # IDCS domain URL for example idcs-abscasdwdac3432rdwsda.identity.oraclecloud.com
```
### 3. Run the Example
Start the server:
```bash
python server.py
```
Test with client:
```bash
python client.py
```
When you run the client, it will open a browser on your machine to login to OCI IAM domain.

View file

@ -0,0 +1,32 @@
"""OAuth client example for connecting to FastMCP servers.
This example demonstrates how to connect to an OAuth-protected FastMCP server.
To run:
python client.py
"""
import asyncio
from fastmcp.client import Client
SERVER_URL = "http://localhost:8000/mcp"
async def main():
try:
async with Client(SERVER_URL, auth="oauth") as client:
assert await client.ping()
print("✅ Successfully authenticated!")
tools = await client.list_tools()
print(f"🔧 Available tools ({len(tools)}):")
for tool in tools:
print(f" - {tool.name}: {tool.description}")
except Exception as e:
print(f"❌ Authentication failed: {e}")
raise
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,38 @@
"""Oracle OCI IAM OAuth server example for FastMCP.
This example demonstrates how to protect a FastMCP server with Oracle OCI IAM OAuth.
Required environment variables:
- FASTMCP_SERVER_AUTH_IDCS_CLIENT_ID: Your IDCS OAuth Application clientID
- FASTMCP_SERVER_AUTH_IDCS_CLIENT_SECRET: Your IDCS client secret
- FASTMCP_SERVER_AUTH_IDCS_DOMAIN: IDCS domain URL for example idcs-abscasdwdac3432rdwsda.identity.oraclecloud.com
To run:
python server.py
"""
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.oci import OCIProvider
auth = OCIProvider(
client_id=os.getenv("FASTMCP_SERVER_AUTH_IDCS_CLIENT_ID") or "",
client_secret=os.getenv("FASTMCP_SERVER_AUTH_IDCS_CLIENT_SECRET") or "",
config_url=f"https://{os.getenv('FASTMCP_SERVER_AUTH_IDCS_DOMAIN')}/.well-known/openid-configuration"
or "",
base_url="http://localhost:8000",
# redirect_path="/auth/callback", # Default path - change if using a different callback URL
)
mcp = FastMCP("OCI OAuth Example Server", auth=auth)
@mcp.tool
def echo(message: str) -> str:
"""Echo the provided message."""
return message
if __name__ == "__main__":
mcp.run(transport="http", port=8000, host="localhost")

View file

@ -180,3 +180,10 @@ class OCIProvider(OIDCProxy):
client_id,
oci_required_scopes,
)
def _prepare_scopes_for_token_exchange(self, scopes: list[str]) -> list[str]:
"""Omit scope from the upstream auth-code token exchange."""
logger.debug(
"Omitting scope from upstream token exchange. Original scopes: %s", scopes
)
return []

View file

@ -0,0 +1,87 @@
"""Unit tests for OCI OAuth provider."""
from unittest.mock import patch
import pytest
from key_value.aio.stores.memory import MemoryStore
from fastmcp.server.auth.oidc_proxy import OIDCConfiguration
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.server.auth.providers.oci import OCIProvider
TEST_DOMAIN = "idcs-test.identity.oraclecloud.com"
TEST_CONFIG_URL = f"https://{TEST_DOMAIN}/.well-known/openid-configuration"
TEST_CLIENT_ID = "test-client-id"
TEST_CLIENT_SECRET = "test-client-secret"
TEST_AUDIENCE = "test-audience"
TEST_BASE_URL = "https://example.com:8000/"
TEST_REDIRECT_PATH = "/test/callback"
TEST_REQUIRED_SCOPES = ["openid", "profile", "email"]
@pytest.fixture
def valid_oidc_configuration_dict():
"""Create a valid OCI OIDC configuration dict for testing."""
return {
"issuer": "https://identity.oraclecloud.com/",
"authorization_endpoint": f"https://{TEST_DOMAIN}/oauth2/v1/authorize",
"token_endpoint": f"https://{TEST_DOMAIN}/oauth2/v1/token",
"jwks_uri": f"https://{TEST_DOMAIN}/admin/v1/SigningCert/jwk",
"response_types_supported": ["code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
}
class TestOCIProvider:
"""Test OCIProvider initialization."""
def test_init_with_explicit_params(self, valid_oidc_configuration_dict):
"""Test initialization with explicit parameters."""
with patch(
"fastmcp.server.auth.oidc_proxy.OIDCConfiguration.get_oidc_configuration"
) as mock_get:
oidc_config = OIDCConfiguration.model_validate(
valid_oidc_configuration_dict
)
mock_get.return_value = oidc_config
provider = OCIProvider(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
audience=TEST_AUDIENCE,
base_url=TEST_BASE_URL,
redirect_path=TEST_REDIRECT_PATH,
required_scopes=TEST_REQUIRED_SCOPES,
client_storage=MemoryStore(),
jwt_signing_key="test-secret-key",
)
mock_get.assert_called_once()
call_args = mock_get.call_args
assert str(call_args[0][0]) == TEST_CONFIG_URL
assert provider._upstream_client_id == TEST_CLIENT_ID
assert provider._upstream_client_secret is not None
assert (
provider._upstream_client_secret.get_secret_value()
== TEST_CLIENT_SECRET
)
assert (
provider._upstream_authorization_endpoint
== f"https://{TEST_DOMAIN}/oauth2/v1/authorize"
)
assert (
provider._upstream_token_endpoint
== f"https://{TEST_DOMAIN}/oauth2/v1/token"
)
assert isinstance(provider._token_validator, JWTVerifier)
assert provider._token_validator.audience == TEST_AUDIENCE
assert str(provider.base_url) == TEST_BASE_URL
assert provider._redirect_path == TEST_REDIRECT_PATH
assert provider._token_validator.required_scopes == TEST_REQUIRED_SCOPES