
SAP OAuth 2.0 client credentials for an AI agent | Agentic ERP Quickstart
Move the agent's SAP connection off a stored password and onto SAP-issued OAuth 2.0 client credentials. Covers the cognito-m2m profile, SOAUTH2 setup in SAP, why SAP scopes are OData service names, and how to validate the result in your own environment.
Series: Building Production-Grade ERP Agents on AWS (18 articles)
- …
- 5SAP OAuth 2.0 client credentials for an AI agent | Agentic ERP Quickstart This article
SAP OAuth 2.0 client credentials for an AI agent | Agentic ERP Quickstart
A stored SAP password is a credential that never expires until somebody remembers to rotate it, and nobody remembers. OAuth 2.0 client credentials replace it with a short-lived token SAP itself issues and expires, so the agent never holds a password at all. One configuration line switches the credential. The work is on the SAP side, and two specific mistakes cause most of the failures. Treat what follows as a configuration guide, not a certified route.
Series: Building governed ERP agents on AWS · Series guide →
This post: How to configure SAP client credentials for the agent's SAP connection, and how to validate that configuration in your own environment.
Prereq: A running deployment from the deploy walkthrough , thecognito-basicsetup , a deployed AWS for SAP MCP Server, and an SAP administrator who can create an OAuth 2.0 client.
What you get from this post
By the end, the agent reaches SAP with a short-lived OAuth 2.0 bearer token that SAP itself issues, instead of a stored username and password. You also get:
- A one-line configuration change that switches the SAP credential, and a clear list of what it does not change.
- The SAP-side setup steps to request from your SAP administrator.
- The two configuration mistakes that cause most failures on this path, and what their error codes look like.
- A validation table you can run in your own environment, plus what each check does and does not prove.
Time: about 30 minutes of configuration, plus SAP administrator time and one redeploy.
You will need: an AWS account with the Quickstart deployed, SAP administrator access, and a deployed AWS for SAP MCP Server.
You will need: an AWS account with the Quickstart deployed, SAP administrator access, and a deployed AWS for SAP MCP Server.
New to this series? Start here
The Agentic ERP Automation Quickstart is an open source reference implementation that deploys an AI agent for work your team does by hand from a written procedure, on Amazon Bedrock AgentCore. Accounts payable is the worked example, where a case looks like an invoice that does not match its purchase order. A new process type is a skill directory and your own SOPs. Four pieces matter for this post:
- The frontend and runtime. People sign in to a web application, and the agent runs behind it.
- The scheduled OData poller. A Lambda function that reads SAP on a timer to find exceptions. OData is the REST-style API protocol that SAP exposes for business data.
- The AWS for SAP MCP Server. A separate AWS-provided runtime that gives the agent its SAP tools. MCP is the Model Context Protocol, the standard the agent uses to call tools. You deploy this stack yourself, and the Quickstart does not deploy it for you.
- AgentCore Gateway. The component that connects the agent to that external MCP runtime and applies policy at the boundary.
One setting in
cdk/config.yaml, called auth_profile, selects a matching set of identity choices across all of those pieces. This post covers the cognito-m2m profile. The auth profile overview explains the full profile model if you want the background first.When to choose cognito-m2m
Many SAP organizations will not issue a password for an integration user. Others have already standardized on OAuth 2.0 client credentials for machine access, configured in the SAP transaction
SOAUTH2. If either is true for you, cognito-m2m meets that requirement without changing how people sign in to the application.The operational gain is real. On the SAP-facing connection you get a credential that expires on its own and that your SAP team can revoke centrally, instead of a password someone has to rotate by hand.
The limit is also worth stating up front. This profile does not change who SAP thinks is calling. Requests still arrive as one fixed technical user, so SAP audit records show that account rather than the person who asked the agent for something. If you need SAP to see the signed-in person, that is the
entra-obo path .What changes when you switch
Each auth profile in
auth-profiles.yaml sets four things: how people sign in, how the runtime authorizes incoming calls, which operating modes are enabled, and which credential reaches SAP. Between cognito-basic and cognito-m2m, only the last one moves:1
2
cognito-basic: { frontend: cognito, inbound: cognito, mode: [autonomous, live], outbound: basic }
cognito-m2m: { frontend: cognito, inbound: cognito, mode: [autonomous, live], outbound: m2m-sap }That
outbound value governs exactly one connection: the credential the external MCP runtime presents to SAP.cognito-basic | cognito-m2m | |
|---|---|---|
| Credential arriving at SAP | Username and password | An OAuth 2.0 bearer token |
| Who issues that credential | Nobody. It is a static secret. | SAP itself, through SOAUTH2 |
| SAP identity | The configured technical user | The technical user bound to the OAuth client |
| Rotation and revocation | Rotate the password by hand | Tokens expire, and revoking the client cuts access |
| SAP-side prerequisite | A password for the technical user | An OAuth client, a bound user, and service scopes |
Everything else stays exactly as it was:
- People still sign in with Amazon Cognito. The login experience does not change.
- The runtime still trusts Cognito for incoming authorization.
- The Gateway still reaches the external MCP runtime with OAuth 2.0 client credentials, through a Gateway Service target. Both profiles use a Service target once you enable SAP MCP.
- The scheduled OData poller still uses Basic credentials. It is the only component that calls SAP directly, and it does not read
auth_profileat all. Its shared session helper always builds a Basic authorization header. Switching profiles does not move the poller. - There is still no per-user SAP identity. Both profiles reach SAP as one technical account.
NOTE
The name is easy to misread. "M2M" here describes only the SAP-facing connection. Machine-to-machine tokens are already used elsewhere in the deployment on every profile, includingcognito-basic. This profile adds SAP-issued tokens. It does not add machine authentication to a deployment that lacked it.
How a SAP request flows
With this profile selected, an agent request to SAP travels like this:
1
2
3
4
5
6
1. Agent asks for SAP data
2. AgentCore Gateway Service target, reached with a Cognito machine token
3. AWS for SAP MCP Server the external runtime that owns the SAP connection
4. AgentCore Identity exchanges client credentials for a token <-- new in cognito-m2m
5. SAP SOAUTH2 token endpoint issues the bearer token
6. SAP OData service runs the call as the bound technical userSteps 4 and 5 are the only additions. Under
cognito-basic, step 3 presents a username and password instead.
This profile always goes through the Gateway. It never uses the direct-to-MCP path, which belongs to
entra-obo and its per-user token exchange.Step 1: Set the profile and redeploy
Set
auth_profile at the top level of cdk/config.yaml and keep the SAP endpoint under sap:1
2
3
4
auth_profile: cognito-m2m
sap:
base_url: https://your-sap-host:443Then redeploy:
1
python3 launch.py deployAfter the deploy finishes, confirm that the external MCP and M2M adapter prerequisites resolved. Note that Terraform cannot deploy this profile, because the outbound SAP MCP adapter exists only in the CDK path.
TIP
For unattended backlog processing, selectcognito-m2m-batchinstead of addingbatchto this profile by hand. That profile declaresautonomous,live, andbatch, and only thebatchvalue provisions the sweeper.
Step 2: Set up the OAuth client in SAP
This work belongs to your SAP administrator. You can reuse the technical user from the
cognito-basic setup if it already has the right OData permissions. Ask for the following:- Register a
SOAUTH2client for the client-credentials grant. - Bind that client to the technical SAP user.
- Store the client ID and secret in your approved secret store, ready for the external MCP configuration.
- Activate the OData services the workload needs.
- Authorize the bound technical user for only those services and only the business scope you intend.
Screen labels and the scope model vary between SAP releases. Confirm them in the transaction on your own system rather than following a screenshot from a different environment.
Before you connect anything on the AWS side, have your SAP administrator run an isolated test against one activated OData service. This tells you whether the SAP half works on its own. Record whether any failure is an authentication failure, an authorization failure, or a service that was never activated. Do not capture client secrets or real bearer tokens in a terminal recording.
Scopes are OData service names, not URIs
This trips up almost everyone coming from a general OAuth background. The
MCP_SERVER_SAP_OAUTH_SCOPES setting takes SAP OData service names, such as API_PURCHASEORDER_PROCESS_SRV. It does not take generic OAuth or OpenID Connect scope URIs. A wrong value here typically surfaces as an HTTP 403 from SAP.Step 3: Configure the external MCP server
The AWS for SAP MCP Server is a separate stack that this repository does not deploy. It owns its own runtime, the SAP OAuth provider configuration, the client credentials, the list of requested SAP services, and its own write-enable flags. Configure it for the M2M flow, and leave its write tools disabled until your authorization and change-control requirements are met.
Two details matter for the SAP credential provider:
- Use
oauthDiscovery.authorizationServerMetadataand supply the authorization endpoint, token endpoint, and issuer explicitly. - Do not put an SAP token URL in
discoveryUrl. That field expects a.well-knowndiscovery document, and an SAPSOAUTH2token endpoint is not one.
On the repository side, the adapter creates the Gateway Service target and a Gateway OAuth 2.0 credential provider for this path. That provider must match the inbound credentials and issuer the external MCP stack expects. A mismatch shows up as a 401 from the external runtime, usually because the issuer (
iss) claim does not match.
Step 4: Validate the path
Run these checks in order. Each one narrows the problem, and each has a limit worth knowing.
| Check | What success shows | What it does not show |
|---|---|---|
| SAP-side isolation test | A client-credentials token can reach one activated, authorized OData service | Anything about your Gateway or MCP configuration |
| Agent SAP read through the external MCP runtime | The full Gateway Service-target path reaches SAP | Any human attribution, because no human identity is in this flow |
SAP audit review (SM20 or the RSAU_* transactions) | Activity recorded under the bound technical user | Proof of a mapped human user. This is a smoke test, not attribution |
When something fails, match the symptom before you change anything:
| Symptom | Most likely cause |
|---|---|
| 401 from the external MCP runtime | The inbound issuer does not match |
| Credential-provider validation error | discoveryUrl was used where SAP needs authorizationServerMetadata |
| 403 from SAP | Wrong service-name scope, or the bound user lacks the SAP role permission |
| 401 from SAP | Wrong SOAUTH2 client, secret, or token endpoint |
The M2M operator runbook covers each of these in more depth.
What actually controls SAP writes
Write protection on this path comes from three independent layers, and none of them is on by default in a way you should assume.
- Cedar policy at the Gateway. Because this profile uses the Gateway, the repository's Cedar policy can apply to its Service target. The sample roles gate create, update, and function-import actions, and forbid
odata_delete. This has blocking effect only when the policy engine is deployed and you setcedar_enforcement_mode: ENFORCE. The default isLOG_ONLY, which records decisions without blocking. The policy also does not cover the direct-to-MCP path used byentra-obo. - The external MCP runtime. It decides which write tools it exposes at all, through
MCP_SERVER_WRITE_ENABLEDand per-operation flags. - SAP authorizations. The permissions on the bound technical user remain the final word, and the only layer SAP enforces itself.
There is no runtime write switch in the agent. If a write must be impossible, restrict it in SAP.
Choosing between cognito-basic and cognito-m2m
Pick based on the authentication contract your SAP operators require, not on a general sense of which is more secure. Both profiles reach SAP as one technical account, and neither tells SAP who the person behind the request was.
Choose
cognito-basic when you can get a password for a technical user and you want the path with a recorded live-SAP verification run. Choose cognito-m2m when your SAP organization requires OAuth 2.0 client credentials for machine access, and you are prepared to validate the chain yourself.If the real requirement is that SAP records the signed-in person, neither profile answers it. Go to the
entra-obo setup.Where to go next
entra-obo: per-user SAP identity for the path that propagates the signed-in user's identity to a mapped SAP user.- The M2M operator runbook , which you should read in full before attempting a production SAP integration.
AUTH_PROFILE_SELECTION.mdfor the complete profile reference.
Series: Building Production-Grade ERP Agents on AWS (18 articles)
- …
- 5SAP OAuth 2.0 client credentials for an AI agent | Agentic ERP Quickstart This article
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article