| Internet-Draft | AEP | July 2026 |
| Kavian | Expires 24 January 2027 | [Page] |
The Agent Enrollment Protocol (AEP) defines an HTTP-based mechanism for autonomous agents to discover service enrollment requirements, enroll an agent identity, obtain optional session credentials, revoke those credentials, and query enrollment status. AEP uses Decentralized Identifiers, client assertion JWTs, and HTTP Problem Details to provide a narrow machine-first enrollment and authentication substrate for agent-to-service interactions.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 24 January 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Autonomous agents increasingly interact with internet services without a human directly completing registration forms, email confirmations, password setup, or dashboard-based API-key provisioning. Existing HTTP authentication mechanisms can authenticate an already-provisioned client, but they do not define a machine-first enrollment flow by which an autonomous agent discovers a service's requirements, presents a cryptographic identity, and becomes recognized by that service.¶
The Agent Enrollment Protocol (AEP) defines that enrollment substrate. AEP lets an Agent discover what a Service requires, enroll a supported identity method, authenticate AEP commands with a per-request client assertion JWT, optionally obtain a session credential, revoke issued session credentials, and query enrollment status.¶
AEP is deliberately narrow. It does not define payment settlement, checkout semantics, action authorization, KYC execution, or legal policy. Those functions can compose above or beside AEP. This document defines only the minimum HTTP protocol needed for interoperable Agent enrollment and session-credential bootstrapping.¶
This document's scope is limited to the HTTP binding for Inspect, Enroll, Grant, Revoke, and Status. Update, Rotate, Decommission, non-HTTP transports, concrete session-credential formats, policy disclosures, KYA, ZK proofs, and other extensions are out of scope for this document.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
Software acting autonomously. An Agent holds or controls a cryptographic key and initiates AEP requests.¶
The HTTP server that receives AEP requests and decides whether to enroll or recognize an Agent.¶
The human or organization that owns or controls an Agent. Owner details are represented as claims when a Service requires them.¶
An optional operator that hosts Agent identity material or signing infrastructure. A Platform is not required by AEP.¶
A party that verifies claims about an Agent or Owner and may issue attestations referenced by the Agent. This document does not define attestation formats.¶
The JSON discovery document published by a Service at /.well-known/aep. It advertises AEP version, supported commands, accepted identity methods, requested claims, endpoint configuration, and extension support.¶
A JWT signed by the Agent's private key and presented on an authenticated AEP command or protected Service resource. The assertion binds the Agent identity, Service identity, operation, issuance time, expiration time, replay identifier, and, for protected resources, the resource URI.¶
A stateful credential issued by the Grant command and presented on later requests according to a separate session-credential specification.¶
A string identifier for a concrete session-credential format supported by the Service, such as an OAuth Bearer, API-key, or Basic credential specification.¶
A wire identifier advertised by a Service for authenticating protected resources. aep-jwt identifies an AEP client assertion; registered Grant Type identifiers identify session credentials.¶
The baseline AEP flow is:¶
The Agent fetches the Service's Inspect document.¶
The Agent evaluates whether it can satisfy the Service's identity-method and claim requirements.¶
The Agent constructs a client assertion JWT with aud equal to the Service DID and op equal to the command being invoked.¶
The Agent invokes Enroll.¶
The Agent calls Status when enrollment is pending or when it needs current state.¶
The Agent may call Grant for a supported session-credential type.¶
The Agent may call Revoke to invalidate issued session credentials.¶
Inspect is unauthenticated. Enroll, Grant, Revoke, and Status are authenticated with the baseline Authorization: AEP <jwt> form. Session credentials, once issued, MAY be used on commands that allow the selected credential type; Grant and Revoke themselves always use the baseline client assertion.¶
This document defines an HTTP binding using HTTP semantics [RFC9110] over HTTP/1.1 [RFC9112], HTTP/2 [RFC9113], or HTTP/3 [RFC9114]. Network use of this binding requires TLS 1.3 or later [RFC9846]. Plaintext HTTP is out of scope.¶
The binding uses only GET and POST:¶
| Command | Method | Endpoint |
|---|---|---|
| Inspect |
GET
|
/.well-known/aep
|
| Enroll |
POST
|
enroll relative to endpoint_base
|
| Status |
GET
|
status relative to endpoint_base
|
| Grant |
POST
|
grant relative to endpoint_base
|
| Revoke |
POST
|
revoke relative to endpoint_base
|
The endpoint_base value is published in the Inspect document under http.endpoint_base. If omitted, Agents MUST use /aep/. Agents construct command URLs by appending the command's relative path to endpoint_base with exactly one / separator, regardless of whether endpoint_base includes a trailing slash. For example, both /aep and /aep/ produce /aep/enroll for Enroll.¶
Requests and successful responses that carry AEP JSON payloads use application/aep+json, which uses the +json structured syntax suffix [RFC6839]. Error responses use application/problem+json.¶
Authenticated commands carry a baseline client assertion as:¶
When a session credential is used on a command that allows it, the credential presentation form is defined by the concrete session-credential document.¶
The Inspect document is available at the well-known URI path defined for AEP [RFC8615]:¶
The response body is a JSON object [RFC8259]. AEP-owned numeric protocol values are represented as JSON strings. Field names use lower_snake_case.¶
Claim names use dotted lowercase tokens:¶
claim-name = claim-token *("." claim-token)
claim-token = LCALPHA *(LCALPHA / DIGIT / "_")
LCALPHA = %x61-7A
¶
The LCALPHA rule is defined here. The DIGIT rule is defined by RFC 5234 [RFC5234].¶
The Inspect document shown here contains only the fields required for the HTTP binding, Inspect, Enroll, Grant, Revoke, Status, and an example identity method:¶
{
"aep_version": "1.0",
"authentication": {
"methods": ["aep-jwt", "oauth-bearer"]
},
"bindings": {
"supported": ["http"]
},
"claims": {
"optional": [],
"preferred": [],
"required": ["contact.email"]
},
"commands": {
"grant_types": ["oauth-bearer"],
"supported": ["enroll", "grant", "inspect", "revoke", "status"]
},
"core": {
"signing_algorithms": ["EdDSA", "ES256"]
},
"extensions": {
"supported": []
},
"http": {
"endpoint_base": "/aep/",
"openapi": {
"path_matching": {"trailing_slash": "strict"},
"url": "/openapi.json"
}
},
"identity": {
"methods": ["did:web"]
},
"service": {
"did": "did:web:api.example.com"
}
}
¶
commands.supported lists commands the Service exposes. Agents MUST NOT invoke commands absent from this list.¶
commands.grant_types lists concrete session-credential formats the Service can issue and revoke. If this array is empty or absent, the Service MUST NOT list grant or revoke in commands.supported.¶
authentication.methods lists, in preference order, the authentication methods accepted by protected resources belonging to the Service. aep-jwt identifies an AEP client assertion with op equal to authenticate. Other values MUST be registered Grant Type wire identifiers and use that grant type's credential presentation rules. The field is OPTIONAL; if it is absent, the Service advertises no protected-resource authentication method. When present, it MUST be non-empty and contain no duplicates. An absent or empty commands.grant_types array does not imply support for aep-jwt or any other method. authenticate MUST NOT appear in commands.supported because it is an assertion operation, not a Service command endpoint.¶
identity.methods lists identity method identifiers the Service accepts for authenticated AEP commands. The values are lower-case identifiers registered in the AEP Identity Methods registry. A Service that advertises Enroll, Grant, Revoke, or Status MUST advertise at least one identity method.¶
service.did identifies the Service. Agents use this value as the aud claim in client assertion JWTs.¶
Services SHOULD send HTTP cache metadata, including Cache-Control and ETag, on Inspect responses. A default freshness lifetime of 300 seconds is RECOMMENDED when the Service does not need a shorter policy window.¶
Agents MUST honor usable HTTP caching metadata on Inspect, including Cache-Control, ETag, and Last-Modified, and MUST process successful conditional revalidation through 304 Not Modified. no-cache requires revalidation before reuse. no-store prohibits persistent or transient reuse beyond the current fetch. When no shorter usable freshness policy is supplied, Agents SHOULD use 300 seconds. The cache key is the requested advertised URL and MUST be updated to the final URL after an accepted redirect so revalidation targets the representation that produced the cached document.¶
Agents MUST require the media-type essence of a successful Inspect response to be application/aep+json. Comparison is case-insensitive and ignores valid media-type parameters. A missing, malformed, or different media type MUST be rejected.¶
An Agent following an Inspect redirect MUST require each redirect target to have the same scheme, host, and effective port as the preceding request URL. Cross-origin redirects and scheme downgrades MUST be rejected. This restriction applies to the unauthenticated Inspect command and does not prevent a future extension from defining a signed Inspect response.¶
Agents MAY enforce documented bounds on the decoded response size and total completion time. An Agent that enforces such bounds MUST fail closed when a bound is exceeded and MUST NOT process a partial Inspect document.¶
http.openapi, when present, advertises an OpenAPI 3.1 document for protected Service resources. It contains required url and path_matching.trailing_slash fields. url is a URI-reference. A relative reference resolves against the final Inspect response URL. An absolute HTTPS URL MAY be cross-origin. Plaintext HTTP is prohibited except when the URL host is syntactically exactly localhost, 127.0.0.1, or [::1] for development; resolving another name to a loopback address does not qualify. User-information components and HTTPS-to-HTTP downgrade are prohibited.¶
path_matching.trailing_slash is strict or equivalent. Under strict, paths that differ by a terminal slash are distinct. Under equivalent, exactly one terminal slash is ignored except for /. The canonical behavior when processing older compatible cached data that lacks this member is strict; new documents using http.openapi MUST include it.¶
OpenAPI retrieval is anonymous. An Agent MUST NOT send resource credentials, AEP assertions, Platform credentials, cookies, caller authorization headers, or other headers copied from the triggering request. Agents MUST enforce documented redirect-count, decoded-byte, and total-completion-time bounds. Redirects MUST NOT downgrade transport or introduce user information. A cross-origin HTTPS redirect remains anonymous and is permitted within the bound.¶
Agents MUST accept OpenAPI 3.1 JSON represented as application/vnd.oai.openapi+json with a version=3.1 parameter or as application/json. Media-type comparison is case-insensitive and permits unrelated valid parameters. Agents MUST reject missing, malformed, or other media types, non-JSON bodies, documents whose openapi version is not 3.1.x, and partial or over-bound documents.¶
Agents use standard OpenAPI root and operation security inheritance and components.securitySchemes. An operation-level security replaces the root value. An empty security array makes the operation public. An empty requirement object permits anonymous access as one alternative. Multiple requirement objects are alternatives; multiple schemes within one requirement are a compound requirement and all must be satisfied.¶
The x-aep-authentication-method Security Scheme Object extension binds an arbitrary OpenAPI security-scheme name to aep-jwt or a registered AEP Grant Type wire identifier. Its value is one registered authentication method identifier. A referenced scheme without this extension is not an AEP method. An Agent MUST treat a compound requirement as unsupported when it cannot satisfy every member and MAY select another complete alternative. It MUST NOT silently reduce a compound requirement to one scheme.¶
To select an operation, Agents uppercase the concrete HTTP method and match the request path using OpenAPI path-template rules. Literal path segments take precedence over templated segments. Query parameters never select an operation. Under strict, terminal slash variants differ. Under equivalent, one terminal slash is ignored except for /. Multiple equally applicable templates, structurally equivalent templates with different variable names, or another ambiguous match are contradictions.¶
A fresh, definitive operation match MAY be used to plan authentication without first probing the protected resource. An undocumented operation, unsupported or ambiguous mapping, stale document, or contradiction between OpenAPI and a live response requires fallback to anonymous live challenge discovery and document revalidation. OpenAPI never authorizes a request and never permits credentials to bypass the redirect-safety rules.¶
Authenticated AEP commands require an Agent identity method that can bind a stable Agent identifier to verification material for the client assertion signature.¶
This document defines the identity-method substrate but does not define a concrete identity method. Concrete identity method documents define:¶
The identity method identifier used in identity.methods.¶
The Agent identifier syntax.¶
How a Service resolves or otherwise obtains verification material for the Agent identifier.¶
How the JWT kid header identifies the verification method.¶
Any caching, rotation, revocation, or trust-anchor requirements.¶
Security and privacy considerations specific to that identity method.¶
A Service enables the identity methods it accepts and advertises only those identifiers in identity.methods. A Service that advertises no identity methods MUST NOT advertise authenticated commands.¶
For example, a Service that enables the separately specified did:web identity method advertises:¶
{
"identity": {
"methods": ["did:web"]
}
}
¶
If the Agent presents an identity method not listed in the Service's identity.methods array, the Service MUST reject the request as not_recognized.¶
The AEP HTTP authentication scheme uses the AEP auth-scheme value. The rules ALPHA, DIGIT, DQUOTE, and SP are defined by RFC 5234 [RFC5234]. The rules auth-param, BWS, and OWS are defined by HTTP semantics [RFC9110].¶
AEP-credentials = "AEP" 1*SP compact-jws
compact-jws = base64url "." base64url "." base64url
base64url = 1*( ALPHA / DIGIT / "-" / "_" )
AEP-challenge = "AEP" [ 1*SP AEP-challenge-param
*( OWS "," OWS AEP-challenge-param ) ]
AEP-challenge-param = reason-param / service-param /
inspect-param / auth-param
reason-param = "reason" BWS "=" BWS DQUOTE error-code DQUOTE
service-param = "service_did" BWS "=" BWS
DQUOTE did-value DQUOTE
inspect-param = "inspect" BWS "=" BWS
DQUOTE absolute-uri DQUOTE
did-value = 1*(%x21 / %x23-5B / %x5D-7E)
absolute-uri = 1*(%x21 / %x23-5B / %x5D-7E)
error-code = lc-token *( "_" lc-token )
lc-token = LCALPHA *( LCALPHA / DIGIT )
¶
The AEP-credentials form is used in the Authorization field on command endpoints and in either Authorization or AEP-Authorization on protected resources. The AEP-challenge form is used in the WWW-Authenticate field. Parameter and field names are case-insensitive and each parameter MUST occur at most once. The reason parameter carries an AEP error code. A protected resource advertising AEP support MUST include service_did and inspect; service_did identifies the Service and inspect is the absolute HTTPS URI of that Service's Inspect document. Agents MUST require the challenged Service DID to match the fetched Inspect document. Challenges without the AEP scheme, or without both discovery parameters, do not initiate AEP discovery.¶
Enroll, Grant, Revoke, Status, and protected-resource authentication use a signed client assertion JWT. Inspect is unauthenticated and does not use a client assertion.¶
The client assertion JWT is carried as:¶
The JWT is a JWS compact serialization [RFC7515] consisting of a JOSE header, JWT claims set [RFC7519], and signature.¶
The JOSE header MUST contain:¶
{
"alg": "EdDSA",
"typ": "JWT",
"kid": "did:web:agent.example.com:agents:123#key-1"
}
¶
alg identifies the signing algorithm. Services supporting this document MUST support EdDSA [RFC8037] and ES256 [RFC7518] and advertise accepted algorithms in core.signing_algorithms. Agents MUST select an algorithm advertised by the Service. The none algorithm and symmetric JOSE algorithms MUST NOT be used for Agent identity assertions.¶
typ MUST be JWT.¶
kid identifies the Agent's DID and MAY include a fragment selecting a verification method in the resolved DID document. The DID portion of kid MUST equal the Agent DID carried in iss and sub.¶
The JWT claims set MUST contain:¶
{
"iss": "did:web:agent.example.com:agents:123",
"sub": "did:web:agent.example.com:agents:123",
"aud": "did:web:api.example.com",
"op": "enroll",
"iat": 1748428800,
"exp": 1748428860,
"jti": "9f8a4d2e-1c3b-4f5e-8b7a-000000000000"
}
¶
iss and sub MUST both equal the Agent DID.¶
aud MUST equal the Service DID advertised as service.did in the Inspect document.¶
op MUST equal the operation being invoked. The values defined by this document are enroll, grant, revoke, status, and authenticate. The four command operations are valid only at their corresponding AEP command endpoint. authenticate is valid only at a protected resource and is never valid at an AEP command endpoint.¶
An assertion with op equal to authenticate MUST also contain resource, an absolute HTTPS URI identifying the protected resource target. The value MUST equal the request target URI after URI normalization that does not change resource identity; fragments are prohibited. A protected resource MUST reject an assertion whose resource does not identify that target. Redirect targets require a newly issued assertion when the normalized resource URI changes.¶
iat and exp are NumericDate values as defined by JWT [RFC7519]: seconds since the Unix epoch represented as JSON numbers. These claims are an exception to AEP-owned JSON payload numeric-string encoding. Services MUST reject assertions where exp - iat is greater than 300 seconds. Services SHOULD allow no more than 30 seconds of local clock skew.¶
jti MUST be freshly generated for each assertion. Services MUST maintain a replay cache keyed by at least (sub, jti) for the assertion lifetime plus the accepted clock-skew window.¶
To verify a client assertion, the Service MUST:¶
Parse the JWT header, claims set, and signature.¶
Reject the assertion if alg is not advertised by the Service or is prohibited by this document.¶
Resolve the DID identified by kid.¶
Select the referenced verification method.¶
Verify the JWS signature.¶
Verify iss, sub, aud, op, resource when required, iat, exp, and jti according to this section.¶
Any verification failure MUST use the common not_recognized error defined in this document's error handling section.¶
Inspect is the unauthenticated discovery command. An Agent invokes Inspect by fetching the Service's well-known AEP document:¶
The Service returns 200 OK with an application/aep+json body containing the Inspect document described in this document. Inspect has no request body and no client assertion.¶
Agents SHOULD cache Inspect documents according to the Service's HTTP cache metadata. Agents MUST re-fetch the Inspect document before invoking a command if the cached document has expired.¶
Enroll registers an Agent DID with a Service. The request uses the baseline client assertion with op equal to enroll.¶
Endpoint:¶
Request body:¶
{
"agent_did": "did:web:agent.example.com:agents:123",
"claims": {
"contact.email": "ops@example.com"
},
"idempotency_key": "9f8a4d2e-1c3b-4f5e-8b7a-000000000000"
}
¶
agent_did MUST equal the Agent DID in the client assertion iss, sub, and kid values, ignoring any kid fragment. The DID method MUST be accepted by the Service's identity.methods advertisement.¶
claims carries claim values requested by the Service's Inspect document. This document does not define a complete claim catalog. Claim names are strings and claim values are JSON values. Services MUST ignore unknown claims unless local policy requires rejection.¶
idempotency_key is an opaque retry key. When both the HTTP Idempotency-Key header and body field are present, they MUST contain the same value.¶
When the authenticated Agent DID already has an enrollment record, the Service MUST return the current enrollment lifecycle representation. It MUST NOT treat the request as renewal or replacement, rerun enrollment policy, reset lifecycle timestamps, or replace the existing record. This requirement applies when the request uses a new idempotency key and is distinct from replaying a request with the same idempotency key.¶
Successful Enroll responses use 200 OK. A synchronous enrollment returns:¶
{
"status": "active"
}
¶
If enrollment requires asynchronous verification, the Service returns:¶
{
"owner_action_required": "true",
"status": "pending",
"verification_pending": ["contact.email"]
}
¶
The Agent polls Status to learn whether a pending enrollment has become active or rejected.¶
Enroll and Status lifecycle responses have two distinct optional dimensions. verification_pending lists submitted claim names whose asynchronous verification has not completed. requirements_pending lists requirement names the Agent still needs to satisfy. Either field can appear on either response, and the fields MUST NOT be treated as aliases. Empty arrays MUST be omitted; absence means that the corresponding set is empty. Claim values MUST NOT appear in either array.¶
owner_action_required is independent of both pending dimensions and can accompany either, both, or neither. Canonical serialization MUST omit owner_action_required unless its value is "true". Consumers MUST accept an explicit "false" for compatibility.¶
Status returns the Service's current state for the authenticated Agent identity. The request uses the baseline client assertion with op equal to status, or a session credential when a concrete session-credential document allows it.¶
Endpoint:¶
Status has no request body.¶
Successful Status responses use 200 OK:¶
{
"since": "2026-05-28T12:00:00Z",
"status": "active"
}
¶
status describes the Agent identity's state at the Service:¶
| Value | Meaning |
|---|---|
active
|
The identity is enrolled and operational. |
pending
|
Enrollment is awaiting asynchronous verification. |
unavailable
|
The identity is temporarily unavailable for Service-defined non-punitive reasons. |
suspended
|
The identity is temporarily disabled by Service action. |
terminated
|
The identity is permanently de-registered. |
rejected
|
Asynchronous verification failed. |
since is the RFC 3339 [RFC3339] timestamp of the last state transition.¶
The lifecycle fields defined for Enroll apply identically to Status. owner_action_required equal to "true" indicates that the Agent's Owner must complete an out-of-band action before the identity can become or remain active.¶
Grant exchanges a baseline client assertion for a session credential. The request uses the baseline client assertion with op equal to grant. A session credential MUST NOT be used to authenticate Grant.¶
Grant requires an existing enrollment recognized by the Service. When current enrollment is not already authoritative, an Agent SHOULD call Status before beginning an approval, signing, or credential-issuance workflow. The Service remains authoritative and MUST reject Grant for an unrecognized Agent with not_recognized; it MUST NOT implicitly enroll the Agent.¶
Endpoint:¶
Request body:¶
{
"grant_type": "oauth-bearer"
}
¶
grant_type MUST be one of the values advertised in commands.grant_types. Concrete session-credential documents MAY define additional request fields.¶
The successful response body is defined by the concrete session-credential document. This core document requires only that the response be a JSON object and that the selected document define credential presentation, expiry semantics, and revocation behavior.¶
Revoke invalidates session credentials previously issued by Grant. The request uses the baseline client assertion with op equal to revoke. A session credential MUST NOT be used to authenticate Revoke.¶
Endpoint:¶
Request body:¶
{
"grant_type": "oauth-bearer"
}
¶
grant_type MUST be one of the values advertised in commands.grant_types. By default, Revoke targets all credentials of that grant type issued to the authenticated Agent. Concrete session-credential documents MAY define additional fields for narrower credential targeting.¶
To revoke all session credentials of every grant type issued to the authenticated Agent, the request body is:¶
{
"all_grant_types": "true"
}
¶
all_grant_types is a string boolean. When all_grant_types is "true", the request MUST NOT contain grant_type or credential_id. A Service that supports Revoke MUST support all_grant_types so an Agent can invalidate all issued session credentials without discovering or iterating over every concrete grant type. A malformed Revoke request, including a request with mutually exclusive fields, fails with invalid_request.¶
Successful Revoke responses use 200 OK and an empty JSON object:¶
{}
¶
The Service MUST return success regardless of whether any matching credentials existed.¶
authenticate authenticates an Agent to an arbitrary protected Service resource; it does not identify a Service command and defines no new command endpoint. An Agent using aep-jwt sends an AEP <jwt> field value with op equal to authenticate, aud equal to the Service DID, and resource equal to the protected request target. A protected resource MUST reject assertions carrying enroll, grant, revoke, or status. Each AEP command endpoint likewise MUST reject authenticate and every non-matching command operation.¶
Protected resources accept AEP credentials in either the standard Authorization field or the dedicated AEP-Authorization field. Generic Agents default to Authorization for compatibility. A caller MAY explicitly select AEP-Authorization; Agents composing AEP with MPP or x402 SHOULD select it before the first authenticated retry and preserve that selection for the operation, including newly issued assertions after safe redirects.¶
The dedicated field preserves the complete normal field value, including its authentication scheme:¶
Services MUST accept both carriers for aep-jwt and every registered Grant Type whose normal presentation uses Authorization. Registered Grant Type specifications define that mapping. API-key credentials continue to use exactly the Service-selected header returned in the Grant response and have no second generic representation.¶
An Agent MUST use at most one AEP carrier per request. A Service inspects AEP-Authorization first and falls back to Authorization only when the dedicated field is absent. If both fields contain an AEP-recognized credential, the Service MUST reject the request as not_recognized; it MUST NOT choose one. An invalid dedicated credential fails closed without fallback to a second AEP credential in Authorization.¶
AEP-Authorization together with Authorization: Payment <credentials> or PAYMENT-SIGNATURE is valid and non-ambiguous. After dedicated AEP authentication succeeds, the AEP layer MUST NOT consume, rewrite, log, or forward an unrelated Authorization credential. Payment processing occurs only after AEP authentication succeeds; the anonymous response remains the 401 AEP challenge.¶
Field-name comparison is case-insensitive. A request MUST NOT contain multiple AEP-Authorization field lines or a combined value encoding more than one AEP credential. Services MUST treat such ambiguity as credential smuggling and return the non-disclosing not_recognized Problem Details error.¶
Both authorization fields are sensitive. Agents, Services, intermediaries, caches, and telemetry MUST redact them and MUST NOT log raw values. Neither field participates in cache keys or idempotency fingerprints. AEP-Authorization MUST NOT be copied into assertion claims or signatures, Platform context, Inspect, OpenAPI, or another protocol document.¶
A protected resource that requires authentication returns 401 Unauthorized with an AEP challenge:¶
An Agent MAY first send the exact requested method, URL, headers, and body without AEP credentials. It begins AEP discovery only when a 401 response contains a valid AEP challenge with service_did and inspect. Unrelated 401 responses MUST NOT trigger AEP authentication.¶
Before beginning discovery, approval, Grant, or authenticated retry, an Agent MUST determine whether the request body can be replayed. If it cannot reproduce the identical body, it MUST fail without starting an authentication flow. Implementations MAY apply documented cancellation, total-time, and response-size bounds and MUST fail closed rather than process partial authentication metadata.¶
An Agent MUST NOT forward an AEP assertion or session credential across an origin change. It MAY follow a same-origin redirect with the credential only when the redirected request remains authorized by the credential and, for aep-jwt, uses a newly issued assertion bound to the redirect target's resource in the same selected AEP carrier. For a cross-origin redirect, the Agent MUST remove Authorization, AEP-Authorization, PAYMENT-SIGNATURE, every AEP assertion, every AEP-issued session credential, and every payment credential, then restart at the target with an anonymous request. It MUST require a new valid AEP challenge before authenticating to the new origin. Redirect handling MUST NOT copy a Service-selected API-key header to another origin.¶
Successful authentication establishes an Agent principal and credential metadata, including the authentication method and granted scopes when applicable. It does not authorize the requested application action. The protected application separately evaluates resource policy and scopes. A valid credential with inadequate permission fails with insufficient_scope and 403 Forbidden, without being treated as an authentication failure.¶
Missing credentials use authentication_required. A method not listed in authentication.methods uses unsupported_authentication_method. Malformed or expired credentials, wrong operation, wrong audience, wrong resource, and replayed assertions use the non-disclosing not_recognized error. Protected resources MUST consume jti atomically before accepting an authenticate assertion so concurrent replays cannot both succeed.¶
POST commands are state-mutating and MUST support safe retry with the Idempotency-Key HTTP header. This requirement applies to Enroll, Grant, and Revoke in this document.¶
Services MUST cache the response associated with (agent_did, Idempotency-Key) for at least 1 hour. If a request repeats the same key with the same authenticated Agent and the same request body, the Service MUST return the cached response or an equivalent successful response.¶
If the same authenticated Agent reuses an idempotency key with a different request body, the Service MUST return 409 Conflict with code equal to idempotency_conflict.¶
The Enroll request body MAY also contain idempotency_key for bindings or application frameworks that persist idempotency metadata with the body. When both forms are present, they MUST match.¶
The HTTP binding uses RFC 9457 Problem Details [RFC9457] with an AEP code field.¶
{
"code": "not_recognized",
"status": 401,
"title": "Not recognized",
"type": "urn:aep:error:not_recognized"
}
¶
The code field is the canonical machine-readable AEP error code. type identifies the AEP error class using the form urn:aep:error:<code>. title MAY be omitted from production responses.¶
After an identity has been recognized, a verification_pending Problem Details response MAY include a non-empty verification_pending array, and a requirements_unmet response MAY include a non-empty requirements_pending array. Either response MAY include owner_action_required only when its value is "true". These fields contain names only and MUST NOT contain claim values. They describe why the attempted operation is blocked; pending Enroll and Status state continues to use successful lifecycle responses. A not_recognized response MUST NOT include any of these fields.¶
This document defines the following HTTP error codes:¶
| AEP code | HTTP status | Meaning |
|---|---|---|
enrollment_failed
|
400 | Generic enrollment failure where the Service suppresses precise detail. |
invalid_request
|
400 | The request body, parameters, or field combination is malformed or invalid. |
not_recognized
|
401 | Umbrella anti-enumeration error for failed identity, signature, audience, operation, replay, time-window, archived-identity, or unsupported-method checks. |
identity_suspended
|
403 | The recognized identity is temporarily disabled by Service action. |
identity_terminated
|
403 | The recognized identity is permanently de-registered. |
identity_unavailable
|
403 | The recognized identity is temporarily unavailable for Service-defined reasons. |
requirements_unmet
|
422 | Required claims are missing or invalid. |
verification_pending
|
403 | Enrollment or required verification has not completed. |
verification_timeout
|
422 | Required asynchronous verification did not complete in the Service's policy window. |
rate_limited
|
429 | The Agent exceeded a Service rate limit. |
unsupported_grant_type
|
400 | Grant or Revoke requested a grant_type not advertised by the Service. |
idempotency_conflict
|
409 | An idempotency key was reused with a different request body. |
authentication_required
|
401 | The protected resource requires an advertised authentication method. |
unsupported_authentication_method
|
401 | The presented authentication method is not accepted by the protected resource. |
insufficient_scope
|
403 | Authentication succeeded but the principal lacks required authorization scope. |
Services MUST use not_recognized for bad signatures, unknown Agent identities, wrong aud, wrong op, replayed jti, time-window violations, archived identities, unsupported identity methods during authenticated contact, and unknown or revoked session credentials. Services MUST NOT reveal which of these checks failed.¶
Services MUST use invalid_request for malformed JSON, missing required fields, invalid field types, unsupported field combinations, and syntactically invalid requests when returning the error would not reveal identity-recognition state.¶
Services MUST implement constant-time-shaped response behavior for not_recognized paths so that observable latency does not distinguish a known Agent from an unknown Agent or a validly formatted assertion from a bad signature.¶
When a request fails for multiple reasons, the Service MUST choose the least revealing error. For example, a request with both a bad signature and missing claims returns not_recognized, not requirements_unmet.¶
This document defines the extension points needed by the core protocol:¶
extensions.supported advertises extension identifiers implemented by the Service.¶
identity.methods advertises concrete identity methods accepted for authenticated AEP commands.¶
commands.grant_types advertises concrete session-credential formats available through Grant and Revoke.¶
commands.grant_types_config MAY carry per-grant-type configuration defined by a concrete session-credential document.¶
claims.required, claims.preferred, and claims.optional MAY contain claim names defined by other documents.¶
Additional top-level Inspect fields MAY be added by future documents.¶
Agents MUST ignore extension identifiers and additive fields they do not understand, unless local policy requires the Agent to refuse enrollment when a required capability is absent.¶
Services MUST NOT redefine the semantics of commands, fields, status values, or error codes defined by this document. Extensions are additive.¶
Concrete session-credential documents MUST define:¶
Command identifiers, binding identifiers, and grant type identifiers use lowercase hyphenated tokens:¶
wire-identifier = lc-token *( "-" lc-token )¶
Error codes use lowercase underscore-separated tokens:¶
error-code = lc-token *( "_" lc-token )¶
Identity method identifiers use either an existing DID method identifier, such as did:web, or a lowercase hyphenated token registered for AEP-specific non-DID identity methods.¶
Extension identifiers MUST be absolute URIs. AEP-owned extension identifiers SHOULD use the URN form urn:aep:ext:<authority>:<name>#v=<version>.¶
This section requests registrations and registry creation following RFC 8126 [RFC8126].¶
IANA is requested to register the following HTTP authentication scheme in the "HTTP Authentication Schemes" registry:¶
| Field | Value |
|---|---|
| Authentication Scheme Name |
AEP
|
| Reference | This document |
| Notes | Agent Enrollment Protocol client assertion authentication |
IANA is requested to register the following permanent field name in the "Hypertext Transfer Protocol (HTTP) Field Name Registry":¶
| Field Name | Status | Structured Type | Reference |
|---|---|---|---|
AEP-Authorization
|
permanent | N/A | This document |
AEP-Authorization carries one AEP-recognized protected-resource credential while preserving its registered authentication scheme and credential syntax. It is a request field and is unsafe for logging, forwarding across disallowed redirects, or cache-key construction.¶
IANA is requested to register the following URI suffix in the "Well-Known URIs" registry:¶
| Field | Value |
|---|---|
| URI Suffix |
aep
|
| Change Controller | IETF |
| Reference | This document |
| Related Information | Agent Enrollment Protocol Inspect document |
IANA is requested to register the following media type in the "Media Types" registry:¶
| Field | Value |
|---|---|
| Type name |
application
|
| Subtype name |
aep+json
|
| Required parameters | None |
| Optional parameters | None |
| Encoding considerations | Same as JSON [RFC8259] |
| Security considerations | AEP payloads can contain Agent identifiers, claims, session credentials, and other security-sensitive protocol data. Implementations need to apply the authentication, confidentiality, anti-replay, anti-enumeration, logging, and privacy requirements described in the Security Considerations and Privacy Considerations sections of this document. |
| Interoperability considerations | None |
| Published specification | This document |
| Applications that use this media type | Services and Agents implementing AEP |
| Fragment identifier considerations | Same as JSON [RFC8259] |
| Additional information | None |
| Person and email address to contact for further information | IETF iesg@ietf.org |
| Intended usage | COMMON |
| Restrictions on usage | None |
| Author | IETF |
| Change controller | IETF |
IANA is requested to create an "AEP Commands" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new command registrations define command semantics, authentication requirements, request and response shapes, idempotency behavior for state-mutating commands, and error behavior.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Command | Lowercase wire identifier. |
| Description | Short command description. |
| Reference | Stable specification reference. |
Initial entries are:¶
| Command | Description | Reference |
|---|---|---|
inspect
|
Discover Service AEP capabilities. | This document |
enroll
|
Register an Agent identity with a Service. | This document |
status
|
Query the Agent identity's current state. | This document |
grant
|
Issue a session credential. | This document |
revoke
|
Revoke session credentials. | This document |
authenticate is not registered as a command because it does not identify a command endpoint.¶
IANA is requested to create an "AEP Operations" registry. The registration policy is Specification Required. Each registration MUST define its valid target class, assertion binding, and replay behavior.¶
| Operation | Valid target | Reference |
|---|---|---|
enroll
|
Enroll command endpoint | This document |
grant
|
Grant command endpoint | This document |
revoke
|
Revoke command endpoint | This document |
status
|
Status command endpoint | This document |
authenticate
|
Protected Service resource, not AEP command | This document |
IANA is requested to create an "AEP Authentication Methods" registry. The registration policy is Specification Required. Each registration MUST define credential presentation, expiry and replay behavior, redirect handling, and authentication failure behavior. Registered AEP Grant Type identifiers are also valid authentication method identifiers when the corresponding grant specification defines protected-resource presentation.¶
| Authentication Method | Description | Reference |
|---|---|---|
aep-jwt
|
Resource-bound AEP client assertion JWT. | This document |
The initial registered Grant Type method identifiers are oauth-bearer, api-key, and basic, as defined by their respective documents.¶
IANA is requested to create an "AEP Binding Identifiers" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new binding registrations define transport semantics, endpoint discovery, authentication carriage, payload encoding, error mapping, and security considerations.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Binding Identifier | Lowercase wire identifier. |
| Description | Short binding description. |
| Reference | Stable specification reference. |
Initial entries are:¶
| Binding Identifier | Description | Reference |
|---|---|---|
http
|
HTTP binding for AEP commands. | This document |
IANA is requested to create an "AEP Extension Identifiers" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new extension registrations define the extension identifier, discovery behavior, protocol fields or commands added by the extension, error behavior, and security and privacy considerations.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Extension Identifier | Absolute URI identifying the extension. |
| Description | Short extension description. |
| Reference | Stable specification reference. |
This document creates the registry but does not register concrete extensions.¶
This document registers the following OpenAPI Specification Extension for Security Scheme Objects:¶
| Field | Value |
|---|---|
| Name |
x-aep-authentication-method
|
| Type | String |
| Description | Registered AEP authentication method identifier |
| Reference | This document |
IANA is requested to create an "AEP Error Codes" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new error codes are binding-independent, use lower_snake_case, avoid exposing identity-enumeration detail, and define default HTTP status mapping and remediation behavior.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Code | Lowercase lower_snake_case error code. |
| HTTP Status | Default HTTP status code. |
| Description | Short error description. |
| Reference | Stable specification reference. |
Initial entries are:¶
| Code | HTTP Status | Description | Reference |
|---|---|---|---|
enrollment_failed
|
400 | Generic enrollment failure. | This document |
invalid_request
|
400 | Malformed or invalid request. | This document |
not_recognized
|
401 | Anti-enumeration recognition failure. | This document |
identity_suspended
|
403 | Recognized identity is suspended. | This document |
identity_terminated
|
403 | Recognized identity is terminated. | This document |
identity_unavailable
|
403 | Recognized identity is temporarily unavailable. | This document |
requirements_unmet
|
422 | Required claims are missing or invalid. | This document |
verification_pending
|
403 | Verification has not completed. | This document |
verification_timeout
|
422 | Verification did not complete in time. | This document |
rate_limited
|
429 | Rate limit exceeded. | This document |
unsupported_grant_type
|
400 | Unsupported Grant or Revoke grant type. | This document |
idempotency_conflict
|
409 | Idempotency key reused with a different request body. | This document |
authentication_required
|
401 | Protected-resource authentication is required. | This document |
unsupported_authentication_method
|
401 | Authentication method is not accepted. | This document |
insufficient_scope
|
403 | Authenticated principal lacks sufficient scope. | This document |
IANA is requested to create an "AEP Grant Types" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new grant type registrations define the Grant request fields, Grant response shape, credential presentation syntax, expiry semantics, Revoke behavior, and security considerations for credential storage and leakage.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Grant Type | Lowercase wire identifier. |
| Description | Short credential description. |
| Reference | Stable specification reference. |
This document creates the registry but does not register concrete grant types. OAuth Bearer, API-key, and Basic session credentials are defined by separate documents.¶
IANA is requested to create an "AEP Identity Methods" registry. The registration policy is Specification Required as defined by RFC 8126. Designated experts are requested to verify that new identity method registrations define identifier syntax, verification-material resolution, JWT kid handling, trust anchors, caching behavior, key rotation behavior, and security and privacy considerations.¶
Each entry contains:¶
| Field | Description |
|---|---|
| Identity Method | Lowercase identity method name. |
| Description | Short method description. |
| Reference | Stable specification reference. |
This document creates the registry but does not register concrete identity methods. The did:web identity method is defined by a separate document.¶
Network use of the HTTP binding defined by this document requires TLS 1.3 or later. Plaintext HTTP is out of scope.¶
Client assertions are replay resistant only when Services validate the full chain: aud, op, resource when required, jti, iat, and exp. aud binds the assertion to the Service DID. op binds the assertion to one registered operation. resource binds authenticate to a protected request target. jti prevents in-window duplicate use. iat and exp bound the usable time window. Services that skip any of these checks weaken the authentication model.¶
Services SHOULD keep assertion lifetimes short. This document sets a maximum validity interval of 300 seconds. Services MAY enforce a shorter maximum.¶
Identity methods define how Services obtain verification material for Agent identities. Services MUST apply the resolution, trust-anchor, caching, and key-rotation requirements of each enabled identity method. A Service MUST NOT accept an Agent identity method that was not advertised in identity.methods.¶
The core.signing_algorithms advertisement is security relevant. Services MUST NOT advertise algorithms they do not intend to accept, and MUST NOT accept algorithms that were not advertised. Agents MUST NOT use none or symmetric JOSE algorithms for Agent identity assertions. Implementations SHOULD follow JWT best current practices [RFC8725].¶
Authentication failures are an enumeration risk. Services MUST collapse recognition failures to not_recognized and MUST shape timing so attackers cannot distinguish an unknown Agent, a bad signature, a wrong audience, a wrong operation, a replay, an expired assertion, or an archived identity.¶
Grant issues session credentials that may be bearer credentials depending on the concrete session-credential document. Services and Agents MUST treat returned credentials as secrets. Concrete session-credential documents MUST define credential lifetime, presentation, storage guidance, and revocation semantics. Revoke MUST be available for every advertised grant type.¶
If a session credential is stolen, an attacker may impersonate the Agent until the credential expires or is revoked. Agents that suspect compromise can authenticate with the baseline client assertion and invoke Revoke for the affected grant type.¶
Services SHOULD rate-limit Inspect, Enroll, Grant, Revoke, and Status to reduce probing and credential-issuance abuse. Rate limits MUST NOT create distinguishable recognition errors that defeat the anti-enumeration rules above.¶
AEP exposes Agent identity and claims to Services. Agents and Services should minimize disclosure by using the Inspect document as the negotiation surface: Services list required, preferred, and optional claims; Agents provide the minimum set needed for the intended interaction.¶
Services SHOULD keep claims.required limited to data required for enrollment or legal operation. Over-declaring required claims increases privacy risk and reduces interoperability.¶
Agents SHOULD avoid sending claims absent from claims.required, claims.preferred, or claims.optional. Services MUST ignore unknown claims unless local policy requires rejection.¶
Agent identities can become correlatable if the same identifier is reused across Services. Platforms or Agent operators that require unlinkability SHOULD use a distinct Agent identifier and signing key per Service enrollment when the selected identity method allows it.¶
Services SHOULD maintain a Service-local pairwise identifier for enrolled Agents rather than using the Agent DID as the primary internal record key across all contexts. Such identifiers SHOULD be opaque and MUST NOT be disclosed as cross-Service correlators.¶
Platform-hosted Agent identities introduce Platform-level visibility: the Platform can observe or reconstruct which Services an Agent enrolls with. This document does not prevent that visibility. Agents with stronger privacy requirements should account for the Platform trust relationship before using a Platform-hosted identity.¶
Session credentials can become correlation handles when reused outside the issuing Service or logged by intermediaries. Concrete session-credential documents MUST define presentation rules that avoid unnecessary disclosure and MUST prohibit logging raw credential values.¶
Inspect documents may disclose Service policy and capability information to unauthenticated readers. Services SHOULD avoid publishing sensitive operational details in Inspect beyond what Agents need for interoperability.¶