Skip to content

Phase 2: OAuth / JWT Authentication Setup

Authenticate MCP clients using OAuth 2.1 with an external Identity Provider (EntraID, Cognito, Okta, Keycloak). ARC-1 validates JWT Bearer tokens and extracts user identity.

When to Use

  • Enterprise environments with existing IdP
  • When you need to know which user is making requests
  • Audit trail requirements
  • When combined with Phase 3 for per-user SAP auth

Architecture

                                 ┌─────────────────────┐
                                 │  Identity Provider   │
                                 │  (EntraID / Cognito) │
                                 └──────┬──────────────┘
                                        │ OIDC tokens
┌──────────────────┐     JWT Bearer     │     ┌──────────────────┐     Basic Auth      ┌────────────┐
│  MCP Client      │ ──────────────────►├────►│  arc1 Server      │ ──────────────────► │  SAP ABAP  │
│  (IDE / Copilot) │   Authorization    │     │  validates JWT    │   service account  │  System    │
└──────────────────┘                    │     └──────────────────┘                     └────────────┘
                          ┌─────────────┘
                          │ JWKS keys
                          │ (cached 1h)

Identity Provider Setup

Microsoft Entra ID (Azure AD)

  1. Create App Registration:
  2. Azure Portal → Microsoft Entra ID → App registrations → New registration
  3. Name: ARC-1 SAP MCP Server
  4. Supported account types: Single tenant (Accounts in this organizational directory only)
  5. Redirect URI: leave blank (will be set after Copilot Studio connector creation)

  6. Expose an API:

  7. App registration → Expose an API → Set Application ID URI (accept default api://{client-id})
  8. Add a scope: access_as_user — Type: Admins and users, Display name: Access ARC-1

  9. Set Token Version to v2.0:

  10. App registration → Manifest → set "requestedAccessTokenVersion": 2
  11. Or via Azure CLI:
    # Get the object ID of the service principal's associated app
    az ad app show --id {client-id} --query id -o tsv
    # Patch the API application to use v2.0 tokens
    az rest --method PATCH \
      --url "https://graph.microsoft.com/v1.0/applications/{object-id}" \
      --body '{"api":{"requestedAccessTokenVersion":2}}'
    
  12. Why: v2.0 tokens use the raw client ID as aud claim, while v1.0 uses api://... URI. The OIDC validator needs a consistent audience value.

  13. Add Microsoft Graph User.Read permission:

  14. App registration → API permissions → Add a permission → Microsoft Graph → Delegated → User.Read
  15. Click Grant admin consent for your organization
  16. Or via Azure CLI:
    az ad app permission add --id {client-id} \
      --api 00000003-0000-0000-c000-000000000000 \
      --api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope
    az ad app permission admin-consent --id {client-id}
    
  17. Why: Power Platform requires this permission for OAuth connectors.

  18. Create a Client Secret:

  19. App registration → Certificates & secrets → New client secret
  20. Copy the secret value immediately (it won't be shown again)
  21. Or via Azure CLI:

    az ad app credential reset --id {client-id} --display-name "PowerAutomate" --years 2
    

  22. Note the values:

  23. Application (client) ID — used as both Client ID and audience
  24. Directory (tenant) ID — e.g., 9ef3a122-4319-496a-a394-a7318c2d0a7e
  25. Client secret — from step 5
  26. Issuer URL: https://login.microsoftonline.com/{tenant-id}/v2.0

AWS Cognito

  1. Create User Pool
  2. Create App Client
  3. Configure domain
  4. Issuer URL: https://cognito-idp.{region}.amazonaws.com/{pool-id}

Keycloak

  1. Create Realm
  2. Create Client (confidential)
  3. Issuer URL: https://keycloak.company.com/realms/{realm}

Server Setup

Start arc1 with OIDC Validation

arc1 --url https://sap.example.com:44300 \
    --user SAP_SERVICE_USER \
    --password 'ServicePassword123' \
    --transport http-streamable \
    --http-addr 0.0.0.0:8080 \
    --oidc-issuer 'https://login.microsoftonline.com/{tenant-id}/v2.0' \
    --oidc-audience 'api://arc1-sap-connector'

Environment Variables

export SAP_URL=https://sap.example.com:44300
export SAP_USER=SAP_SERVICE_USER
export SAP_PASSWORD=ServicePassword123
export SAP_TRANSPORT=http-streamable
export SAP_HTTP_ADDR=0.0.0.0:8080
export SAP_OIDC_ISSUER='https://login.microsoftonline.com/{tenant-id}/v2.0'
export SAP_OIDC_AUDIENCE='api://arc1-sap-connector'
export SAP_OIDC_USERNAME_CLAIM='preferred_username'  # default

Username Mapping (Optional)

If OIDC usernames don't match SAP usernames, create a mapping file:

# oidc-user-mapping.yaml
alice: ALICE_DEV
bob: BOB_ADMIN
carol@company.com: CAROL
arc1 ... --oidc-user-mapping oidc-user-mapping.yaml

Client Configuration

VS Code (with OAuth)

VS Code supports MCP OAuth natively. Configure in .vscode/mcp.json:

{
  "servers": {
    "arc1": {
      "type": "http",
      "url": "https://arc1.company.com/mcp"
    }
  }
}

VS Code will: 1. Discover the Protected Resource Metadata at /.well-known/oauth-protected-resource 2. Find the Authorization Server (your IdP) 3. Open browser for OAuth login 4. Send Bearer tokens automatically

Microsoft Copilot Studio / Power Automate

Copilot Studio uses Power Automate custom connectors to connect to MCP servers. The connector handles OAuth token acquisition automatically.

Step 1: Create Custom Connector

  1. Go to Power AutomateCustom connectorsNew custom connectorCreate from blank
  2. General tab:
  3. Connector name: ARC-1 SAP MCP
  4. Host: your-arc1-server.cfapps.us10-001.hana.ondemand.com (or your server hostname)
  5. Base URL: /

Step 2: Configure Security Tab

  1. Security tab → Authentication type: OAuth 2.0
  2. Identity Provider: Azure Active Directory
  3. Enable Dienstprinzipal-Unterstützung (Service Principal support)
  4. Client ID: {client-id} (from Entra ID app registration)
  5. Client secret: {client-secret} (from Entra ID app registration)
  6. Authorization URL: https://login.microsoftonline.com
  7. Tenant ID: {tenant-id} (your actual tenant ID — NOT common)
  8. Resource URL: {client-id} (the raw GUID, NOT api://...)
  9. Scope: api://{client-id}/access_as_user offline_access

⚠️ Critical: The Tenant ID must be your actual tenant GUID, not common. Using common fails for single-tenant apps.

⚠️ Critical: The Resource URL must be the raw client ID GUID (e.g., aa34a3d1-...), not the api:// URI. When an app requests a token for itself, Entra ID requires the GUID format.

Step 3: Create Definition

  1. Definition tab → Create an action:
  2. Summary: InvokeServer
  3. Operation ID: InvokeServer
  4. Verb: POST
  5. URL: https://your-arc1-server.example.com/mcp
  6. Click Connector aktualisieren (Update Connector)

Step 4: Add Redirect URI to Entra ID

  1. After creating the connector, copy the Umleitungs-URL (Redirect URL) shown at the bottom of the Security tab
  2. It looks like: https://global.consent.azure-apim.net/redirect/crc25-5farc-2d1-20...
  3. Go to Azure Portal → App registration → Authentication → Add platform → Web
  4. Add the redirect URI from step 6
  5. Also add the base: https://global.consent.azure-apim.net/redirect

Or via Azure CLI:

az ad app update --id {client-id} \
  --web-redirect-uris \
    "https://global.consent.azure-apim.net/redirect" \
    "https://global.consent.azure-apim.net/redirect/your-connector-specific-uri"

Step 5: Create Connection

  1. Click Verbindung erstellen (Create Connection)
  2. A Microsoft login popup will appear — sign in with your organization account
  3. Grant the requested permissions (Sign in and read user profile)

Troubleshooting Copilot Studio

Error Cause Fix
AADSTS50011 (Reply address mismatch) Redirect URI not registered Add the connector's specific redirect URI to the app registration
AADSTS90009 (Requesting token for itself) Resource URL uses api:// format Change Resource URL to raw client ID GUID
AADSTS90008 (Not consented, must require Graph) Missing User.Read permission Add Microsoft Graph User.Read and grant admin consent
AADSTS65001 (Consent not granted) App not authorized Run az ad app permission admin-consent --id {client-id}
Anmelden nicht möglich (Login not possible) Tenant ID is common or Resource URL empty Set Tenant ID to actual GUID; set Resource URL to client ID
OAuth popup opens/closes immediately Multiple issues possible Check Tenant ID, Resource URL, and redirect URI registration

Manual Token Testing

# Get a token from your IdP (example with Azure CLI)
# First, authorize Azure CLI for your app:
az ad app update --id {client-id} --set "api.preAuthorizedApplications=[{\"appId\":\"04b07795-8ddb-461a-bbee-02f9e1bf7b46\",\"delegatedPermissionIds\":[\"your-scope-id\"]}]"

# Login with the scope
az login --scope "api://{client-id}/access_as_user"

# Get a token
TOKEN=$(az account get-access-token --scope "api://{client-id}/access_as_user" --query accessToken -o tsv)

# Test against arc1
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' \
  https://your-arc1-server.example.com/mcp

How It Works

  1. MCP client sends request without token
  2. ARC-1 returns 401 with WWW-Authenticate: Bearer resource_metadata="..."
  3. Client fetches Protected Resource Metadata
  4. Client discovers IdP authorization server
  5. Client performs OAuth 2.1 Authorization Code + PKCE flow
  6. Client sends Authorization: Bearer <jwt> on every request
  7. ARC-1 validates JWT signature via JWKS (cached 1 hour)
  8. arc1 checks issuer, audience, expiry
  9. ARC-1 extracts username from configured claim
  10. Request proceeds (SAP auth still via service account)

Security Notes

  • JWT signatures are cryptographically verified via JWKS
  • JWKS keys are cached for 1 hour (auto-refresh)
  • Tokens must have correct issuer AND audience
  • ARC-1 never sees user passwords (IdP handles login)
  • SAP still uses a shared service account (for per-user SAP auth, add Phase 3)

References

Next Steps

Phase 3: Principal Propagation — Per-user SAP authentication