SW-A2A-006: OAuth authorization code flow without PKCE required
Last updated 7 November 2026
| Fact | Value |
|---|---|
| Severity | Medium |
| Checks | A2A agent card |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI03 |
| OWASP MCP Top 10 | None mapped |
| NIST SP 800-53 | IA-5 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 1.3.0 |
What it detects
The rule applies to cards in the A2A 1.0 format only. For each OAuth 2 scheme it looks at an authorizationCode flow and reports it unless pkceRequired is the boolean true. A missing member, false and any value that is not the boolean true, such as the string "true", all count as not required. The finding points at the flow and its reason is not_required. Other flows are not judged here, and a card in an older format is left to SW-A2A-002.
Why it matters
PKCE (RFC 7636) ties an authorization code to the client that asked for it. Without it, a code that is intercepted on its way back to the client, for example in the communication between applications on the client's operating system, can be exchanged for a token by whoever holds it. A2A 1.0 gives the card a member, pkceRequired, so a client can see that the server insists on PKCE and use it. The scanner maps this rule to OWASP ASI03, Identity and Privilege Abuse, and to NIST SP 800-53 control IA-5, Authenticator Management.
Example
A complete card with an authorization code flow that does not say pkceRequired:
.well-known/agent-card.json
{
"name": "demo-agent",
"description": "Drafts release notes from merged pull requests.",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://agent.example.invalid/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {},
"securitySchemes": {
"oauth": {
"oauth2SecurityScheme": {
"flows": {
"authorizationCode": {
"authorizationUrl": "https://auth.example.invalid/authorize",
"tokenUrl": "https://auth.example.invalid/token",
"scopes": {
"notes.read": "Read release notes"
}
}
}
}
}
},
"securityRequirements": [
{
"schemes": {
"oauth": {
"list": [
"notes.read"
]
}
}
}
],
"defaultInputModes": [
"text/plain"
],
"defaultOutputModes": [
"text/plain"
],
"skills": [
{
"id": "release-notes",
"name": "Release notes",
"description": "Drafts release notes for one tag.",
"tags": [
"release"
]
}
]
}The finding points at the authorizationCode flow and says pkceRequired is not true.
How to fix
Set pkceRequired to true in the authorization code flow, and turn on the matching setting at the identity provider so a request without a code challenge is refused. Test a client that sends PKCE before you publish the card, because the member only describes the server; it does not make the provider enforce anything.
If it is a false positive
The card cannot express a rule that lives at the provider, so a provider that already refuses every authorization code request without a code challenge is still reported until the card says so. Set the member. If you cannot yet, accept the finding with a note that names the provider setting that does the work.
If the finding is not a problem in your case, accept it with a note: in the dashboard a manager or owner can accept a finding and the note is kept with it; with the scanner on its own, add it to the baseline file with a reason and an expiry date.