Repository navigation
A2A/MCP IAS M2M blocked: client_credentials grant not enabled on tutorials-identity app #2649
Description
Problem
The A2A IAS auth migration (#2593, PR #2643, merged to DEV) is code-complete and deployed, but the client-credentials (M2M) grant cannot be exercised because the IAS application tutorials-identity does not permit it.
Post-deploy smoke on DEV (2026-10-04):
| Check | Result |
|---|---|
Agent card advertises IAS scheme + non-empty tokenUrl |
✅ security: [{ias:['openid']}], tokenUrl: https://atxgsg7zi.accounts.ondemand.com/oauth2/token, no xsuaa scheme |
Anonymous /a2a → 401 / JSON-RPC -32001 |
✅ backend rpc-router enforces; approuter authenticationType: none is safe |
| IAS client-credentials token exchange | ❌ HTTP 400 unsupported_grant_type — "allowed grant_types: refresh_token, authorization_code" |
IAS-bearer /a2a → 200 |
⛔ blocked (no token can be minted) |
XSUAA-bearer /a2a → 200 (hybrid) |
⛔ not yet verified |
Root cause
tutorials-identity (DEV app id 17b636dd-fa87-4cd8-b0f2-0abd5f9a3042, clientid 0b1e8b56-5f5d-4ebf-a9e9-28aae2964236, credential-type: SECRET, tenant atxgsg7zi.accounts.ondemand.com) is a bundled, SAP-managed, read-only child application (inherited from "SAP Developers", provisioned by the service binding).
Verified against the IAS admin console (read-only navigation, nothing changed):
- OpenID Connect Configuration panel → only URIs (Redirect / Back-Channel Logout / Post Logout Redirect). No grant-types field.
- Client Authentication panel → Secrets / Certificates / JSON Web Tokens / Trust-by-Issuer/URI / Client ID Lock. No grant-types control.
- Token endpoint rejects
client_credentialsacross every request shape tried: secret-in-body, HTTP Basic, and Basic +scope=openid— all identical 400.
There is no admin-console toggle to enable M2M on this app. IAS client-credentials for a BTP app is governed by the service instance / binding provisioning parameters, not the UI.
Important: MCP shares the same IAS app
tutorials-srv-mcp's identity binding is the same application — clientid 0b1e8b56-5f5d-4ebf-a9e9-28aae2964236, same tenant. So:
- There is no already-working M2M sibling to copy.
- PR docs(api): update MCP signed-in auth to IAS native-HTTP OAuth #2592's
/mcp-authM2M surface likely has the identical unverified gap. Confirm whether MCP authenticated M2M was ever exercised with a real client-credentials token.
Proposed fix (binding-param, not UI)
- Research the exact IAS service-instance parameters that enable the technical-client /
client_credentialsgrant for the installedidentityservice plan (verify against current SAP docs — do not assume). - Add those parameters to the
tutorials-identityresource inmta.yaml+.deploy/mta.yaml(and the MCP MTAmta-mcp.yamlif MCP needs the same), with matching DEV/PROD mtaext overlays as needed. - Re-create / redeploy the IAS instance on DEV.
- Re-run the smoke (below). Then roll to PROD (same tenant
atxgsg7zi; PROD apptutorials-identity-prodlives in the same tenant).
Smoke to re-run after fix (DEV)
# 1. mint token
POST https://atxgsg7zi.accounts.ondemand.com/oauth2/token
grant_type=client_credentials (client_id/secret from tutorials-identity binding)
# expect 200 + access_token
# 2. three /a2a outcomes against the approuter
POST https://tutorial-system-dev-tutorials-approuter.cfapps.eu10-005.hana.ondemand.com/a2a (JSON-RPC)
IAS bearer -> 200
XSUAA bearer -> 200 (hybrid: cds.requires.auth = {kind:'ias', xsuaa:true})
anonymous -> 401 / -32001 (already confirmed)
# 3. agent card still advertises non-empty tokenUrl (already confirmed)
Scope note
Do not remove the XSUAA fallback until Central Joule consumers confirm cutover (hybrid mode must stay).
References
- A2A agent: migrate auth from XSUAA client-credentials to IAS #2593 (A2A IAS migration), PR feat(a2a): migrate agent auth from XSUAA client-credentials to SAP IAS (#2593) #2643 (merged to DEV, merge
2bba155a) - docs(api): update MCP signed-in auth to IAS native-HTTP OAuth #2592 (MCP IAS migration — check for same gap)
Activity
jung-thomas commentedon Oct 5, 2026
Findings & status
M2M grant (both surfaces, both envs)
The client-credentials (M2M) grant is a property of the shared
tutorials-identityIAS instance'soauth2-configuration— NOT the MTA descriptor. It was enabled additively viacf update-service tutorials-identity -c '{...}'keeping the MTA'sexisting-serviceadoption (grants:client_credentials+authorization_code+authorization_code_pkce_s256+refresh_token,public-client:true, redirect-uris). Confirmed on DEV and PROD. Runbook now indocs/developers/operations/ias-setup.md.Note: the IAS M2M token is a bare app token (claims
app_tid/aud/azp/azpacr/exp/iat/iss/jti/sap_id_type/sub) — noscopeclaim (unlike XSUAA).Smoke results
/mcp-auth(#2592)/mcp-auth(#2592)/a2a/a2a#2592's
/mcp-authM2M gap is closed on PROD.PROD
/a2a401 — root cause (code bug, not config)Two conditions must hold for
/a2aIAS auth: (1) the grant on the instance ✅, and (2) the route middleware resolving its bindings per-space. The middleware (srv/lib/a2a/ias-auth-mw.js) resolved IAS and XSUAA in onexsenv.getServices()call keyed by hardcoded instance names. PROD's XSUAA instance isxsuaa-imsprod(DEV:tutorials-xsuaa), so the name miss threw out of the combined call →/a2adegraded to a full anonymous pass-through, taking IAS-only M2M down with it. That's why DEV/a2aand PROD/mcp-authworked but PROD/a2adidn't.Fix (PR #2657 → DEV): resolve IAS alone (required); resolve XSUAA independently by service label (
xsuaa) with the dev name as fallback; if XSUAA absent, proceed IAS-only (never pass-through). XSUAA fallback stays wired when present.Scope guard respected
XSUAA fallback is retained (hybrid mode) — not removed until Central Joule consumers confirm cutover.
Remaining
tutorials-imsMTA to PROD (freshorigin, never the feature branch), then re-run the PROD/a2asmoke to confirm 401→200. PROD/a2astays 401 until that deploy.Merge pull request #2657 from sap-tutorials/fix/a2a-m2m-ias-2649
Merge pull request #2689 from sap-tutorials/fix/devtoberfest-join-pro…