| Internet-Draft | OAuth Authorization Evidence | November 2026 |
| Liu, et al. | Expires 6 May 2027 | [Page] |
This specification defines an authorization details type for carrying authorization evidence in OAuth 2.0 access tokens, using the enriched response mechanism of Rich Authorization Requests. When an Authorization Server records an approval decision, it enriches the authorization details with cryptographic proof of the decision and of the structured disclosure on which the decision was based. Evidence is recorded for approved, narrowed, and declined decisions, supporting accountability, compliance, and dispute resolution in scenarios where autonomous agents act on behalf of users. An approval record that cannot be ended is not an accountable record, so this specification defines the withdrawal mechanism alongside the evidence itself. Withdrawal is one of the four ways a governing authorization ends, alongside completion, termination, and expiry.¶
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 6 May 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.¶
In OAuth 2.0 [RFC6749] flows, the Authorization Server (AS) records the approval decision internally, but the decision is not conveyed to Resource Servers or included in access tokens. For many use cases this is sufficient. In scenarios where an AI agent acts autonomously on behalf of a user, with the approval often given asynchronously and well before the work happens, the Resource Server, the auditor, and the user all need to know what was approved, what the approver was told at the time, and whether the authorization is still in force.¶
Rich Authorization Requests (RAR) [RFC9396] provides the carrier but not the content. Section 7.1 of [RFC9396] states: "The authorization details attached to the access token MAY differ from what the client requests. In addition to the user authorizing less than what the client requested, there are some use cases where the AS enriches the data in an authorization details object. Whether enrichment is allowed and specifics of how it works are necessarily part of the definition of the respective authorization details type." Two gaps remain for approval decisions:¶
The evidence gap. No authorization details type records the approval decision itself: the outcome, the approver, the structured disclosure the approver was shown, the action that confirmed it, the bounds the decision approved, and a signature that makes the record tamper-evident to a party that does not hold the AS's key.¶
The ending gap. An approval establishes a governing authorization, and that authorization ends. Nothing in the framework lets the approver end it, or lets a Resource Server learn that it ended.¶
This specification defines such an authorization details type.
It defines how the AS enriches an authorization_evidence
object at approval time, how the resulting record is signed and
projected into credentials, and how the approver withdraws the
authorization that the record establishes. The mechanism
combines:¶
A structured disclosure object that records what
the approver was told, so that the evidence can be examined
without reconstructing the consent interface.¶
A detached JWS [RFC7515] over the approval record, canonicalized with JCS [RFC8785], giving AS-signed proof of the decision; a digest of the signed input that detects corruption and binds a reference to the record it names; and a digest of the approved bounds that binds the decision to the authority it approved.¶
Two projections of the record into a credential, embedded and by reference, so that deployments can trade token size and disclosure exposure against retrieval cost.¶
An authorization record on the AS, reached through a withdrawal handle, by which the approver ends the governing authorization.¶
Evidence is recorded for approved, narrowed, and declined decisions. Only evidence for approved or narrowed decisions is bound to issued credentials, since a declined decision establishes no authorization.¶
One invariant governs the whole mechanism: evidence does not establish current permission. A verified evidence record proves that an approval happened and what it covered. Whether the authorization is still in force is determined by the lifecycle of the governing authorization and the validity of the credential, not by the presence of the record.¶
Unless otherwise noted, all data types and serialization rules follow the JSON data interchange format as defined in [RFC8259].¶
OAuth 2.0 Bounded Work Authorization [I-D.liu-mcguinness-oauth-bounded-work] sets out a framework for work a user has approved. It describes three objects: a bounded work proposal, the governing authorization that an approval decision establishes for it, and approval evidence. It states the invariants that relate them and lists eight requirements, R1 through R8, that a realization of the framework has to meet. It specifies no wire formats.¶
This specification is a realization of the framework's third object. It defines the evidence record itself, how that record is projected into credentials, and the mechanism through which the approver can end the authorization the record describes. Its terminology (Section 2) uses "governing authorization", "approval record", "approver", and "subject" with the meanings the framework gives them.¶
Against the framework's requirements, this specification is the realization of R2, of R5, and of the part of R7 that runs from a credential to the approval behind it. It also carries the distinctness that R1 asks of the identifier: the disclosure object names the governing authorization by an identifier that is not the client identifier, not a token issued under the authorization, and not a refresh token family (Table 3), and the Authorization Server assigns one where the authorization has none of its own.¶
The remaining requirements belong to other work. Comparing each requested operation against the approved bounds, and refusing where the comparison cannot decide (R3), is the work of whatever framework supplies the bounds, for example domain-specific authorization schemas ([I-D.liu-oauth-authorization-schemas]). Refusing every issuance path once the authorization has ended (R4), and bounding the time within which already issued credentials stop being accepted (R6), are properties of the token layer; Section 6 states what an ending requires of the Authorization Server, but it neither covers every issuance path nor sets an acceptance window. Answering a query about the authorization from a party that holds no credential (R8) is not defined here either: Section 8 conveys lifecycle state as part of a response about a token.¶
Domain-specific authorization schemas
[I-D.liu-oauth-authorization-schemas] define the
behavioral boundaries of a governing authorization and their runtime
enforcement: a schema instance requested in
authorization_details, once approved, is bound to the issued
access token, verified by Resource Servers against each behavior, and
given a lifecycle (active/ended, with ending reasons completed,
withdrawn, terminated, and expired).¶
The two compose in a single access token. The schema
instance states what is permitted, the bounds. The
authorization evidence states why it is permitted, the
approval decision and the disclosure it was based on. The
authority_hash member of
Table 1 binds the second to
the first, so that a party holding the evidence record
apart from the token can tell whether the schema instance
it later obtains is the one the approval was witnessed
against (Section 9.3).¶
This specification uses the terms "authorization server" (AS),
"client", "resource server" (RS), and "access token" defined in
[RFC6749], "authorization details" defined in
[RFC9396], and the NumericDate type
defined in [RFC7519].¶
In addition, this specification defines the following terms:¶
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.¶
This section walks through the scenario the mechanism is built
for, so that the objects and checks defined in the rest of this
document can be seen together in one place. The scenario is a
shopping assistant acting for a user, user_12345, and
it is the same scenario that the complete token of
Appendix A carries. It exercises both
gaps of Section 1: an approval that produces
a signed evidence record, and a withdrawal that ends the
authorization and is enforced against later operations.¶
Step 1 (approval). The user asks her agent to add items to her
cart up to $50 in total. The agent submits this as requested
authority, and the AS discloses it to the user: what will be
done, the $50 bound, and how the authority ends. The user
approves. The AS creates a governing authorization, records the
approval as an evidence object, and signs it
(Section 5.3). The signed record
carries a content_hash of itself and an
authority_hash of the approved bounds
(Section 9.3). The AS also generates a
withdrawal handle and presents it to the user
(Section 6.1), then issues an access
token whose authorization_details carry both the
evidence object and the bounds object.
Appendix A shows that token in full.¶
Step 2 (use within bounds). The agent calls the shop's API to
add a $30 item. The Resource Server obtains the evidence, from
the token or by introspection
(Section 8), verifies the
as_signature under the AS's key
(Section 7), and checks the
content_hash and authority_hash against the
record and the bounds it holds. It confirms that the governing
authorization is still active and that this operation is within
the approved bounds. The request is allowed.¶
Step 3 (withdrawal). The user changes her mind and follows the
withdrawal handle. The AS authenticates her, confirms that she
is the approver recorded on the authorization, and marks the
governing authorization ended with reason withdrawn
(Section 6.1, Paragraph 8). It revokes the issued
token and reflects the ending in its status mechanism
(Section 6).¶
Step 4 (denial). The agent, unaware of the withdrawal, calls the API again to add another item. The token may still be cryptographically valid, but the Resource Server learns from introspection or the status mechanism that the governing authorization has ended, and denies the operation. This is what the ending gap of Section 1 is about: a valid credential on ended work authorizes nothing.¶
Step 5 (accountability afterwards). Some time later an auditor retrieves the evidence record. The authorization has ended and the token is gone, but the record still verifies under the AS's key and still states what the user approved, what she was shown, and the bounds she agreed to. The record outlives both the credential and the authorization, which is what makes it useful for accountability. It does not say, and never said, whether an operation is permitted now; Step 4 answered that through the lifecycle of the governing authorization, not through the record (Section 1).¶
The authorization_evidence authorization details type contains
a record of the approval decision made during the authorization
process. It follows the enriched response pattern of
Section 7.1 of [RFC9396]: the client requests the type,
and the AS populates it with the evidence record derived from the
approval interaction that actually took place.¶
Carrying evidence as an authorization details type rather than as a standalone token claim is what makes that round trip possible. The client has a defined way to ask for a record, and the AS has a defined place to put the one it built. A standalone claim leaves the AS to decide unilaterally whether evidence is attached and gives the client no means of requesting it, so the record a relying party later inspects would not correspond to anything the client asked for.¶
A client requests authorization evidence by including an
authorization_evidence authorization details object in its
authorization request. The client typically includes minimal fields,
indicating that evidence is requested:¶
{
"authorization_details": [
{
"type": "authorization_evidence"
}
]
}
The AS has final authority over the evidence content, which is derived from the actual approval interaction.¶
After the approver completes the approval interaction, the AS enriches the
authorization_evidence object with the complete evidence
record. The enriched authorization details is included in the token
response:¶
{
"authorization_details": [
{
"type": "authorization_evidence",
"evidence": {
"id": "urn:uuid:e73517f6-d8eb-4017-9602-2e251c7ffc7c",
"approval": {
"decision": "approved",
"approver": "user_12345",
"disclosure": {
"summary": "Add items under $50 to cart",
"authority_ref": "urn:uuid:d4affed6-3a05-4fa8-8d0f-936d5fdadc59",
"notices": [
"Items will be added to your saved cart at shop.example"
],
"risks": [
"The agent may add multiple items up to the $50 limit without further confirmation"
],
"locale": "en-US"
},
"action": "button_click",
"timestamp": 1731320595
},
"authority_hash": "yPT2jqmoJHFzOqrZRRfuOU8WNqkqDGhUmP_E09tgLKo",
"content_hash": "LTfvHZqdAF4BzvfUZkUccL2mudBr3Re81CLkIEFIZjg",
"as_signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im...",
"proposal_ref": "urn:uuid:d9172091-0653-41e2-8b76-9f960950c7bd",
"withdrawal_handle": "https://as.example.com/revoke/urn:uuid:181c3a47-7ae7-4109-8290-d69cd3ca6896"
}
}
]
}
This is the same record Appendix A
carries, so it has the same content_hash and the
same authority_hash. The
as_signature is abbreviated here for readability;
Appendix B gives the complete value
and the intermediate steps that produce it. The
authorization details object that authority_hash
digests is not shown here;
Appendix A carries it beside the
evidence record.¶
| Field | Type | Requirement | Description |
|---|---|---|---|
| id | string | REQUIRED | Unique identifier for this evidence record. The value MUST be a URI that is unique across the records the AS issues. A UUID URN is RECOMMENDED. The identifier is not a secret and is not required to be unguessable; the unguessability requirement falls on the withdrawal handle instead (see Section 6.1, Paragraph 5). |
| approval | object | REQUIRED | The approval decision: its outcome, the approver, the structured disclosure, the confirming action, and when it happened. See Table 2. |
| authority_hash | string | CONDITIONAL | The base64url-encoded SHA-256 digest of the
JCS-canonicalized form ([RFC8785]) of
the authorization details object that carries the
approved bounds and whose identifier member carries
the value of
approval.disclosure.authority_ref. The
member commits the signed evidence record to those
bounds, so that a party holding the record on its
own can tell whether a bounds object it obtains
later is the one the AS witnessed the approval of.
The member is REQUIRED where such an object is
present in the token and is omitted only where the
approved authority is carried by no authorization
details object. Where more than one object carries
that identifier, the AS MUST NOT include the member,
because this specification defines no digest over a
set of objects. Section 9.3
states what the digest does and does not
establish. |
| content_hash | string | REQUIRED | The base64url-encoded SHA-256 digest computed
over the same JCS-canonicalized JSON object used
as the as_signature input, as described in
Section 4.5. The digest is
not a substitute for that signature. A party that
holds both the record and the digest can recompute
one from the other and learns nothing, so the
member detects corruption and truncation rather
than adversarial modification. Its uses are to
detect damage to a record in storage or in transit,
to bind a reference that reached a verifier by one
path to the record that reached it by another, and
to let two parties confirm that they hold the same
record without disclosing it. |
| as_signature | string | REQUIRED | Detached JWS over the signed field set defined in Section 4.5. |
| evidence_ref | object | OPTIONAL | Reference to a related evidence record, for
example the original approval in a delegation
chain. The object carries the id and
content_hash of the referenced record and
no other member, the same shape used by the
reference projection of
Section 4.6, so that the reference
is bound to the content it names. |
| proposal_ref | string | OPTIONAL | A URI referencing the original authorization proposal, the client's initial request describing the intended operation before the AS applied policy evaluation, scope reduction, or narrowing at the approval decision. The value is an opaque URI assigned by the AS for internal correlation; no protocol for retrieving the proposal content via this URI is defined by this specification. It enables post-hoc comparison between what was requested and what was authorized. |
| withdrawal_handle | string | OPTIONAL | The withdrawal handle, a URI, for the governing authorization established by this approval, as defined in Section 6.2. |
| Field | Type | Requirement | Description |
|---|---|---|---|
| decision | string | REQUIRED | The outcome of the approval interaction:
approved, narrowed, or
declined. Evidence is recorded for all
three outcomes; only evidence for approved or
narrowed decisions is bound to issued credentials,
since a declined decision establishes no
authorization. A narrowed decision approves
less than was requested (for example, a lower
spending limit); the approved bounds are those of
the governing authorization actually established,
which may be reflected in the corresponding
authorization details objects. |
| approver | string | OPTIONAL | An identifier of the approver, opaque or a URI, stable within the AS's domain. The value MAY be a pseudonymous identifier, one that the AS can resolve to a party but that carries no meaning to a holder of the evidence record. When absent, the approver is the resource owner to whom the disclosure was presented. Identifiers of other parties (subject, client) are carried by the surrounding token or authorization details context and are not duplicated in the evidence object. |
| disclosure | object | REQUIRED | The structured disclosure presented to the approver, as defined in Section 4.4.2. |
| action | string | REQUIRED | How the approver confirmed the operation. This
member is directed at auditors and at the AS's own
dispute resolution rather than at Resource Server
policy evaluation, which has no general basis for
ranking one confirmation mechanism against another.
The value is a free-form string, but implementations
SHOULD use values from the following set for
interoperability: button_click,
biometric_confirmation,
pin_entry, voice_confirmation,
hardware_key, implicit_consent.
Custom values MAY be used for deployment-specific
confirmation mechanisms. |
| timestamp | NumericDate | REQUIRED | When the decision occurred. |
The value implicit_consent indicates that the
authorization was issued under a pre-approved standing
consent arrangement rather than a direct interaction at
authorization time. In that case disclosure
records what the approver was presented with when the
standing arrangement was established, and
timestamp is the time of that establishment
rather than the time of the token request. An AS that
cannot supply both MUST NOT use implicit_consent,
and MUST NOT issue an evidence record for an authorization
that no approver interaction supports.¶
For a narrowed decision, the disclosure
records what the approver actually approved, not what was
originally requested. The request that was narrowed can be
correlated through proposal_ref
(Table 1).¶
The disclosure object captures, in structured
form, the description of the proposed authorization that
was presented to the approver. Its purpose is evidential:
a party examining the evidence can determine what the
approver was told, without reconstructing it from the
request parameters the client sent.¶
| Field | Type | Requirement | Description |
|---|---|---|---|
| summary | string | REQUIRED | A short human-readable summary of the proposed authorization, corresponding to the primary text shown to the approver (for example, "Add items under $50 to cart"). |
| authority_ref | string | REQUIRED | The identifier of the governing authorization
that this approval established. The value MUST be
distinct from the client identifier, from any token
issued under the authorization, and from any refresh
token family, so that a second party can name the
work without having to infer it. Where the approved
authorization is described by other authorization
details objects in the same request (for example, a
domain-specific schema instance), this member MUST
carry the identifier those objects share (such as
identifier, work_id, or an
AS-assigned instance identifier). Where the
authorization carries no identifier of its own, the
AS MUST assign one before it signs the evidence
record. |
| notices | array | OPTIONAL | Material information presented to the approver in addition to the summary (for example, "Items will be added to your saved cart at shop.example"). |
| risks | array | OPTIONAL | Material risks of the proposed authorization that were presented to the approver (for example, "The agent may add multiple items up to the $50 limit without further confirmation"). |
| locale | string | OPTIONAL | The language of the presented disclosure, as a language tag [RFC5646] (for example, "en-US"). |
The disclosure records what the approver was shown, and
that includes how the authority ends. Where the approved
authority carries termination conditions and no
authorization details object in the request records them,
the AS SHOULD state them in notices, so that the
evidence shows what the approver agreed about the end of
the authority and not only about its extent. Where such
an object does record them, authority_hash
commits the evidence record to it
(Section 9.3).¶
Rendering itself, including layout, presentation, and
user interface, is an implementation matter and is not
standardized by this specification. The
disclosure object records what was communicated,
not how it was displayed. It is covered by the AS
signature of Section 4.5, which
commits the AS to the content it presented. That
signature is not an attestation by the approver's device,
and this specification defines no mechanism through which
a device proves what it rendered (see
Section 9.5).¶
The as_signature member is a signature by the
Authorization Server over the approval record. It proves
that the AS witnessed the decision, it makes the record
tamper-evident, and it is what a relying party examines in a
dispute. Its guarantee is bounded by trust in the AS, as
discussed in Section 9.4.¶
The signature covers the evidence object in full,
with two exceptions. The as_signature member is
excluded because a signature cannot cover itself. The
content_hash member is excluded because it is the
digest of the signing input and is therefore derived from
it. Every other member is covered, including
id, approval together with the
disclosure object inside it,
authority_hash, evidence_ref,
proposal_ref, and the OPTIONAL
withdrawal_handle member defined in
Section 6.2.¶
The signing input is a JSON object containing exactly the
covered members, canonicalized with JCS
[RFC8785]. Because
withdrawal_handle is covered, an AS that includes
the member MUST have associated the handle with the
authorization record before it signs, and cannot add or
change the member afterwards without invalidating the
signature. The fields that carry the lifecycle state of the
authorization are maintained on the AS and are not members
of the evidence object, so signing the object does
not fix any value that later changes.¶
Members of the evidence object other than those
defined by this specification MAY be present, for example an
attestation of the rendering step of the kind discussed in
Section 9.5. Any such member is
part of the signed field set and is therefore covered by the
as_signature without further provision. A verifier
that does not understand an extension member MUST still
include it when reconstructing the signing input, and MUST
NOT reject the record solely because a member is present and
unrecognized.¶
The signature format MUST be a detached JWS
[RFC7515] in Compact Serialization, using the
AS's signing key. In detached Compact Serialization (see
Appendix F of [RFC7515]), the payload portion is
omitted, resulting in the form
header..signature.¶
The JWS header MUST contain a typ header parameter
with the value evidence+jws, and a verifier MUST
reject a JWS whose typ is absent or carries any
other value. The parameter separates this JWS from the
others the AS produces under the same key, in particular
from the signature over an access token. Without it, an
as_signature could be presented where a token
signature is expected, and a signature made for another
purpose could be accepted as one, because the two would be
indistinguishable byte strings over the same key. The value
is not registered in the IANA "Media Types" registry;
[RFC7515] permits an application to define
its own.¶
Implementations MUST support ES256 [RFC7518]
for both signing and verification, so that a conformant AS
and a conformant verifier interoperate without prior
arrangement. Other asymmetric algorithms registered in the
IANA "JSON Web Signature and Encryption Algorithms" registry
MAY be used where both parties support them. The steps that
produce the JCS input, the content_hash, and the
as_signature are specified as a single algorithm in
Section 5.3.¶
To verify an as_signature, the verifier
reconstructs the signing input from the received
evidence object by removing the
as_signature and content_hash members and
canonicalizing the remainder with JCS, then verifies the
detached JWS using the AS's public key. The
content_hash can be checked in the same pass: the
verifier recomputes the digest over the reconstructed JCS
output and compares it with the received
content_hash. That comparison requires no key
material and detects corruption or truncation of the record.
It does not detect adversarial modification, because an
attacker who alters a record can recompute its digest, and
it is therefore not a substitute for signature verification.
Section 9.2 states what the
digest does provide.¶
A verifier MUST NOT select the verification algorithm from
the alg header parameter of the JWS it is
verifying. It MUST have an explicit allowlist of acceptable
asymmetric algorithms, MUST reject a JWS whose
alg value is not in that allowlist, and MUST reject
a JWS whose alg value is none. It MUST
reject a JWS whose alg value does not match the type
of the key used to verify it. When the JWS header contains a
crit member, the verifier MUST reject the JWS if any
header parameter named in crit is absent from the
header, or is present but not one the verifier understands
and has processed. These header parameters are defined in
[RFC7515].¶
In the examples in this document the as_signature
value is abbreviated for readability. Actual values use the
detached JWS Compact Serialization form described above.
Appendix B gives a complete worked
example, including the canonical signing input, the digest,
and a signature that verifies.¶
The key used to sign the evidence record MAY be the same key
the AS uses to sign access tokens, provided the
typ requirement above is met, or it MAY be a
separate dedicated key. Whichever key is used,
implementations MUST ensure that it is associated with the
AS through a verifiable mechanism, for example by publishing
it in the AS's JWKS endpoint as defined in
[RFC7517]. A dedicated evidence signing key
allows key rotation on a schedule independent of token
validation.¶
The evidence object defined in the preceding
sections can be conveyed to Resource Servers in two forms:¶
evidence object is carried in the
authorization_details of the access token, as
shown in the preceding examples. This is the default
form.¶
evidence object contains
exactly two members, id and
content_hash, and no others. It contains no
as_signature, because there is nothing in the
token for such a signature to cover. The Resource Server
retrieves the complete evidence object, including its
as_signature, via token introspection
(Section 8).¶
A non-normative example of the authorization details object as it appears in a token using the reference projection:¶
{
"authorization_details": [
{
"type": "authorization_evidence",
"evidence": {
"id": "urn:uuid:e73517f6-d8eb-4017-9602-2e251c7ffc7c",
"content_hash": "LTfvHZqdAF4BzvfUZkUccL2mudBr3Re81CLkIEFIZjg"
}
}
]
}
This two-member pointer is the same shape the OPTIONAL
evidence_ref member of
Table 1 uses to name a related
record. A deployment that needs a pointer to an evidence
record uses this shape whichever way it is being carried.¶
The requirement levels in Table 1 and Table 2 describe the complete evidence record, as the AS produces it and as the embedded projection carries it. The reference projection is a pointer to that record, not a fragment of it that is expected to satisfy the same requirements, and the introspection response returns the complete record.¶
The AS selects the projection per deployment. The
reference projection reduces token size and limits the
exposure of the disclosure object in signed tokens
that are visible to multiple Resource Servers (see the
privacy discussion in Section 10).¶
When a Resource Server receives a reference projection, it
MUST retrieve the complete evidence object via token
introspection, MUST verify its as_signature as
described in Section 7, and MUST verify
that the id and content_hash of the
retrieved record match the reference carried in the token.
On mismatch, or when the introspection endpoint is
unreachable, the Resource Server MUST treat the evidence as
unavailable and apply its policy for a missing evidence
record rather than relying on the unsigned reference alone.¶
The evidence object records the outcome of an
approval interaction. Before the AS can generate a signed
evidence record, it first presents a consent interface to the
approver and captures the decision. Regardless of the grant
type, that interaction follows a common pattern:¶
The AS receives an authorization request from a client, which may be acting on behalf of an AI agent.¶
The AS determines that an approval decision is required
and presents a consent interface to the approver. The
consent interface renders the structured disclosure of
the requested authorization (the disclosure
object, Section 4.4.2).¶
The approver reviews the disclosure and performs a decision action, e.g., clicking an "Allow" or "Deny" button, providing biometric input, or entering a PIN.¶
The AS captures the interaction details (the disclosure,
the decision, the action the approver took, and when) and
constructs the evidence object as defined in
Section 4.¶
For an approved or narrowed decision, the AS signs the evidence record and includes it in the issued access token. For a declined decision, no credential is issued and no authorization record is created, because the decision establishes no governing authorization. The AS MAY retain the evidence record for audit and dispute resolution, but a record retained this way is bound to no credential and is not reachable through token introspection (Section 8).¶
Client/Agent Authorization Server Approver
| | |
|-- authorization req --->| |
| | |
| |--- consent UI ---------->|
| | (disclosure) |
| | |
| |<-- decision action ------|
| | (button_click, etc.) |
| | |
| | [capture evidence] |
| | [sign with as_signature]|
| | |
|<-- access token --------| |
| (with evidence) | |
| | |
The pattern above applies to any OAuth flow in which an
approver is presented with a disclosure. In the Authorization
Code Grant the AS presents a consent screen during the
/authorize redirect. In CIBA the AS delivers a
consent request to the approver's authentication device, e.g.,
a push notification, and the evidence captures the approver's
out-of-band decision action. Flows that capture approval
decisions asynchronously follow the same pattern, for example
the JWT authorization grant with interaction response
([I-D.parecki-oauth-jwt-grant-interaction-response])
and the deferred token response
([I-D.ietf-oauth-deferred-token-response]).¶
Flows that involve no approver interaction, e.g., the Client
Credentials Grant without user context, cannot produce
evidence records. There is no approval decision to
record, and this specification defines no substitute.¶
When issuing an access token carrying evidence, the AS executes the following algorithm. These steps answer the question that Section 7.1 of [RFC9396] leaves to the definition of the authorization details type: what the AS puts in the enriched object, and how that content is protected.¶
Record the structured disclosure presented to the
approver, including its summary, the identifier of the
governing authorization the approval establishes, and
any notices or material risks, as the
disclosure object.¶
Capture the decision, the confirming action, and the
timestamp of the decision in the approval
object.¶
Generate the id of the evidence record, a URI
unique across the records the AS issues.¶
Where the token carries the approved bounds in an
authorization details object identified by
disclosure.authority_ref, canonicalize that
object with JCS [RFC8785], compute its
SHA-256 digest, and store the base64url encoding in
authority_hash. The digest is taken over the
object as the AS will place it in the token response,
so that a verifier recomputing it from the object it
receives obtains the same bytes.¶
Where the deployment conveys a withdrawal handle in the
token response, associate the handle with the
authorization record and populate
withdrawal_handle, and populate
evidence_ref and proposal_ref where
they apply. Every member of the evidence
object other than content_hash and
as_signature MUST be final before signing,
because every one of them is covered by the signature.¶
Create a JSON object containing every member of the
evidence object except content_hash
and as_signature.¶
Apply JCS [RFC8785] to the object from the previous step, producing a deterministic byte string.¶
Compute the SHA-256 digest of the JCS output, encode it in
base64url, and store the result in the
content_hash member of the evidence
object.¶
Compute a detached JWS in Compact Serialization over the
JCS output from step 7, with a JWS header carrying the
typ value required by
Section 4.5, using the AS's
private signing key, and store the resulting
header..signature value in the
as_signature member of the evidence
object.¶
Include the authorization_evidence authorization
details object in the token response and bind it to the
issued access token, in the embedded or the reference
projection (Section 4.6).¶
Token refresh is a renewal operation, not a new approval.
When the AS issues a new access token from a refresh token
while the governing authorization is still active, it
re-projects the same evidence record, with unchanged
id, content_hash, and
as_signature, into the new token. A refresh
request MUST NOT mint new evidence for the authorization.
The condition under which the AS may issue a token carrying
evidence at all is stated in
Section 6.2.¶
A Resource Server that relies on the evidence verifies it as follows:¶
Reconstruct the JCS signing input from the received
evidence object by removing the
as_signature and content_hash members,
as described in Section 4.5.¶
Verify the detached JWS carried in as_signature
using the AS's public key, applying the header parameter
checks of Section 4.5, including the
required typ value.¶
Recompute the SHA-256 digest of the JCS input and compare it
with content_hash. This check requires no key
material and detects corruption or truncation of the record.
It is not a security check on its own and does not replace
the previous step.¶
Where the record carries authority_hash, locate
the authorization details object in the token whose
identifier member carries the value of
approval.disclosure.authority_ref, canonicalize
that object with JCS [RFC8785], compute its
SHA-256 digest, and compare the base64url encoding with
authority_hash. Reject the record on mismatch:
the bounds presented are not the bounds the AS witnessed
the approval of. Where the token carries no such object,
obtain it from the introspection response
([RFC7662]) before comparing.¶
Confirm that approval.timestamp is not later than
the token's iat claim, that is, that the decision
occurred at or before issuance. For an opaque token, use
the iat member of the introspection response
([RFC7662]).¶
The disclosure object records what was presented to the
approver. The Resource Server was not party to that interaction
and cannot verify the content independently; it relies on the AS
signature as proof that the AS witnessed the approver's decision
on the authorization described.¶
Resource Servers SHOULD log the evidence id, the
decision timestamp, and the outcome of the operation. That log
is what closes the audit trail between the approval and the
action taken under it.¶
When opaque (reference) tokens are used, or when the evidence is conveyed as a reference projection (Section 4.6), Resource Servers obtain the authorization details, including the full evidence object, via token introspection ([RFC7662]) as described in Section 9 of [RFC9396].¶
The introspection response MUST return the
authorization_details bound to the token at issuance
without alteration. For an embedded projection, the returned
evidence object is identical to the one carried in the token.
For a reference projection, the AS returns the full evidence
object; the Resource Server MUST compare its id and
content_hash with the reference carried in the token
and MUST reject the record on mismatch.¶
If the governing authorization underlying the evidence has
ended, the AS SHOULD reflect this in the introspection
response, for example by reporting the token as inactive or
by conveying the current authorization state in a separate
member. Reflecting an ending does not license altering the
evidence object. The requirement above applies to the record
as signed, and lifecycle state is conveyed beside it rather
than inside it, because a change to any signed member would
invalidate the as_signature.¶
The as_signature is what establishes that the
retrieved record is one the AS produced. The
content_hash comparison is what establishes that it is
the record this token pointed at. For a reference projection
neither check is optional.¶
This section discusses security considerations specific to authorization evidence in OAuth 2.0. General OAuth 2.0 security considerations, including token threats and countermeasures, are described in [RFC6819].¶
The as_signature is the only integrity protection the
evidence record has once it leaves the token, so the AS
signing key is the root of the mechanism. Implementations
MUST use an algorithm of current security strength from the
set Section 4.5 defines, MUST protect
the signing key against disclosure, and MUST rotate it under a
documented key management process. Key rotation is observable
to verifiers through the kid of the JWS header
(Section 9.8).¶
The algorithm and critical header parameter requirements of
Section 4.5 exist because a verifier
that takes its algorithm from the JWS it is verifying lets
the party that produced the JWS choose the strength of the
check. Where the evidence signing key is published in a
JWKS alongside keys used for other purposes, the AS SHOULD
assign it a kid that is not shared with other keys,
and a verifier SHOULD confirm through the
key_ops member of [RFC7517] that the
key it selected is intended for signature verification. A
key published for encryption MUST NOT be used to verify an
as_signature.¶
The content_hash is a digest of data its holder
already has, and it is worth being precise about what it
establishes. A party holding a record can recompute the
digest, so the member detects corruption and truncation in
storage or in transit. It does not detect an adversary:
anyone able to modify a record can recompute its digest, and
a verifier comparing the two learns only that they are
consistent with each other. Integrity against modification
comes from the as_signature and never from the
digest.¶
The digest becomes adversarially meaningful only when it
reaches a verifier by a different path from the record it
describes. That is the case in the reference projection of
Section 4.6, where the id and
content_hash travel inside a signed token while the
full record arrives from an introspection endpoint.
Comparing the two establishes that the record the AS returned
is the record the token pointed at. The same reasoning is
why evidence_ref in
Table 1 carries a digest of the
record it names.¶
Because identical records yield identical digests, the
content_hash also allows deduplication in audit
stores and lets two parties confirm that they hold the same
record without disclosing it. Since the digest is
deterministic, a record whose content can be enumerated
yields a digest that can be computed in advance and used as
a correlation handle. Records in this specification carry a
unique id and a decision timestamp, which makes
enumeration impractical, but implementations publishing
digests to shared or public stores SHOULD assess whether
correlation of digests across stores raises privacy concerns
for their deployment.¶
The as_signature covers the evidence object,
but the enforceable bounds are not members of that object. A
separate authorization details object in the same token carries
them, and the evidence names it through
approval.disclosure.authority_ref. The signature
therefore commits the AS to the decision and to what the
approver was shown, but not to the bounds. A party holding the
record apart from the token cannot tell whether a bounds object
it obtains later is the one the AS approved.¶
The authority_hash member closes that gap. It commits
the signed record to a digest of the bounds object, in the same
way content_hash commits a reference to the record it
names. It detects a bounds object presented alongside a genuine
record that is not the one the record approves, whether the
substitution happens in an audit store, in a retrieval from the
AS, or in a Resource Server's own assembly of the two. This
matters most in the reference projection
(Section 4.6) and after the credential expires,
where the record and the bounds travel separately.¶
Two limits are worth stating. Where the access token is itself
a signed JWT, its signature already covers the bounds object, so
authority_hash adds nothing against an outside
attacker; its value is in the uses of the record that leave the
token behind. And it does not remove the trust in the AS
described in Section 9.4: the AS holds the signing
key and can sign a digest of bounds it never intended to
enforce. Against the AS the digest buys consistency, not
honesty. An AS that commits to one set of bounds at approval
and enforces another is detectable by any party holding both.¶
Where no authorization details object carries the bounds, for
example in a deployment that expresses authority only through
scope, the member is absent and no such binding exists.
This is a property of the deployment, not a defect in the record.
A relying party that requires the binding MUST treat the absence
of authority_hash as a reason not to rely on the record
for bounds it cannot otherwise establish.¶
The trust boundary of the mechanism is narrow. The
as_signature provides cryptographic proof that the AS
recorded an approval decision. It does not
independently prove that the approver actually
approved. The AS controls both the approval interaction and
the signing key, so a compromised or malicious AS could
fabricate evidence records. Trust in the evidence record
therefore depends on trust in the AS and its operational
security. Deployments requiring stronger non-repudiation
guarantees SHOULD supplement this mechanism with approver-side
signatures or independent approval auditing.¶
The as_signature is an assertion by the AS about
what the AS presented and recorded. It is not an assertion
by the approver's device about what that device displayed.
Where the AS serves the consent interface directly to the
approver's user agent, the two coincide closely enough that
the distinction rarely matters. Where a party other than
the AS renders the disclosure, for example an agent or
client that presents the structured disclosure
object in its own interface, they do not: the AS records
what it supplied for presentation and has no visibility
into what was actually shown, in what form, or for how
long.¶
This specification provides no attestation of the rendering step. Attestation backed by device hardware, such as a secure element or a trusted execution environment, can establish that a genuine application on a genuine device presented a given data structure and that a genuine approver gesture followed. On a general-purpose operating system it cannot establish that what reached the display matched that data structure, because the rendering path lies outside the attested boundary. A Resource Server MUST NOT read the absence of such an attestation from an evidence record as a defect in that record, since this specification neither defines nor requires one.¶
A deployment that does use an attestation of the rendering
step MUST carry it inside the evidence object, so
that it is covered by the as_signature of
Section 4.5, and MUST bind it to
the same id. An attestation carried outside the
signed record, or bound only to a device or a session,
proves that some consent interaction once occurred on that
device. It proves nothing about the approval this evidence
record describes, and it can be replayed across approvals.
The format of such an attestation, the trust anchors
against which it is verified, and its privacy consequences
are outside the scope of this specification.¶
A verified evidence record does not become invalid when the
authorization it describes ends. The as_signature
carries no expiry and the record is deliberately immutable,
so a genuine record taken from an expired or revoked token
continues to verify indefinitely. That the same record
reappears in successive tokens issued for one governing
authorization is intended behavior and not an attack; see
Section 5.3.¶
The exposure is a party presenting such a record as though the authorization were still in force. That cannot be answered cryptographically, because nothing about the record changes when the authorization ends. It is answered by the credential and by authorization state: the record is usable only through a credential the AS issued to carry it, and a Resource Server that needs to know whether the governing authorization is still active consults the mechanisms of Section 9.9 rather than the evidence record. Presenting a genuine record in a context the AS did not issue it for is addressed by Section 9.7.¶
When the evidence object is embedded in a signed
JWT access token ([RFC9068]), the token
signature and the as_signature cover the same
bytes. The value of having both is not that they protect
different fields but that they answer different questions
and have different reach. The token signature binds the
evidence to this particular token, with its subject,
audience, and expiry. The as_signature binds the
evidence to the approval record the AS created, and it
continues to verify after the evidence has left the token
entirely. Together they mean:¶
as_signature is invalidated either way;¶
The third point is what makes the reference projection of
Section 4.6 workable. A Resource Server
holding only an id and a content_hash can
retrieve the record and establish that the AS produced it,
with no reliance on the token that pointed at it. It is
also what makes the record usable as audit evidence after
the credential has expired.¶
Implementations MUST NOT copy an evidence object from
one access token into another without re-validating the
as_signature and confirming that
approval.timestamp is consistent with the new
token's issuance time.¶
With an opaque (reference) token there is no token signature
over the evidence at all. The evidence object is not
embedded in the token and MUST be retrieved by the Resource
Server via token introspection ([RFC7662]) or a
dedicated evidence retrieval endpoint, so the
as_signature is the only integrity protection the
record has. Implementations MUST protect the transport
between the Resource Server and that endpoint with TLS.¶
When the RS is in a different trust domain from the AS, the RS
still has to obtain the AS's public key to verify the
as_signature. Implementations SHOULD:¶
kid (Key ID) in the JWS header of
the as_signature to enable key selection;¶
When the AS and RS belong to different administrative domains, trust establishment for the evidence signing key MAY be facilitated through a trust framework, federation agreement, or explicit key distribution mechanism.¶
A second cross-domain case is not resolved here. Where the
same work reaches more than one Authorization Server, as
when an agent obtains a token for that work at a second
Authorization Server through an identity assertion grant
([I-D.ietf-oauth-identity-assertion-authz-grant]),
each Authorization Server decides under its own policy and
signs its own evidence record. This specification does not
say whether the second verifies and relies on the record the
first signed, whether it produces a record of its own that
refers to the first, or how an auditor is meant to join the
two. The authority_ref of the second record MAY
carry the identifier the first assigned, which is what would
join them, but nothing here requires that or defines what a
verifier concludes when it is present. How the records of
two Authorization Servers for one piece of work relate is
left for further work.¶
Withdrawal under this specification is enforced by the AS and by downstream token revocation and status mechanisms; it is not purely cryptographic. A token issued before the governing authorization ended remains cryptographically valid until it expires, is revoked via the token revocation mechanisms of the host protocol (e.g., [RFC7009]), or is rejected based on status information such as token status lists ([I-D.ietf-oauth-status-list]) or shared signals ([I-D.ietf-secevent-caep]). Deployments with short-lived tokens reduce the window during which an ended authorization is not yet enforced; deployments requiring timely propagation of endings SHOULD combine token revocation with status lists or event-based propagation.¶
This specification does not require resource servers to consult authorization state. Whether an RS enforces the ending of a governing authorization directly, or relies on the AS to revoke the associated tokens, is a deployment decision.¶
An evidence record is a record of a person's decision. The
approval.approver identifier, the
approval.timestamp, and the content of the
disclosure are personal data in most jurisdictions, and
the authorization record on the AS
(Section 6.1) holds the same
information together with the ending state of the
authorization. Both are subject to applicable data protection
requirements, including purpose limitation, retention limits,
and the approver's rights of access and erasure.¶
The disclosure object is shown to one approver during
the approval flow but, in an embedded projection, becomes
readable by every Resource Server that accepts the token. Its
summary, notices, and risks can reveal what the approver was
doing, with whom, and up to what limit. Deployments where this
matters SHOULD use the reference projection
(Section 4.6), which keeps the disclosure at
the AS and exposes only the id and
content_hash to Resource Servers.¶
The content_hash is stable across re-projections of
the same evidence record, for example when a refresh token
yields a new access token
(Section 5.3). Two tokens carrying
the same content_hash descend from the same approval.
Implementations publishing evidence digests to shared or public
stores SHOULD assess whether that linkage is acceptable in
their deployment; Section 9.2
discusses the entropy considerations.¶
Both members of a reference projection are properties of the
record, so a deployment that wants to limit correlation
across audiences cannot vary the pointer on its own. Giving
each audience a pointer of its own means producing a distinct
evidence record for each, with its own id,
content_hash, and as_signature. That
limits what one Resource Server learns about the others, and
it also removes the single record that joins them, which is
what an auditor or an incident responder asks for. This
specification takes no position between the two. A deployment
decides according to whether the parties receiving evidence
are trusted with the association.¶
Authorization records outlive the tokens that carried their evidence, because withdrawal has to remain possible after a credential expires. Retention of authorization records after the governing authorization ends SHOULD follow the same data protection policies that apply to the evidence records themselves and SHOULD be bounded rather than indefinite. The authentication and entitlement checks that Section 6.1, Paragraph 8 requires of a withdrawal request are what keep the authorization history of one approver from being inspected or acted on by another.¶
This specification registers the following authorization details type in the "OAuth Authorization Details Types" registry established by [RFC9396]:¶
This specification defines three sets of enumerated values and creates no registry for any of them. The reason differs in each case.¶
The decision values approved,
narrowed, and declined form a closed set.
They carry protocol meaning, a verifier has to interpret all
three, and this specification assigns no semantics to a
fourth. Extending the set would change what a conformant
verifier is required to understand, so extension is left to a
revision of this document rather than to a registry.¶
The action values are informational. They tell an
auditor how the approver confirmed the decision and are not
an input to any determination a Resource Server is required
to make (see Table 2). A
deployment whose confirmation mechanism falls outside the
listed set records its own value, and nothing in the protocol
depends on a verifier recognizing it, so assigning the values
centrally would buy no interoperability.¶
The ending_reason values of
Section 6.1 never leave the
Authorization Server. They are carried neither in access
tokens nor in evidence records, so they do not cross an
interoperability boundary and there is nothing to coordinate.¶
The typ header parameter value
evidence+jws required by
Section 4.5 is not registered in the
IANA "Media Types" registry either. [RFC7515]
permits an application to use a type value of its own, and
this value identifies a JWS that only implementations of this
specification produce or accept.¶
The following example shows a complete access token carrying
both an authorization_evidence object and a
transaction_authority object, a domain-specific
authorization schema instance
([I-D.liu-oauth-authorization-schemas]).¶
The scenario is a shopping assistant acting for
user_12345. The approver was shown the summary "Add
items under $50 to cart", together with a notice about where the
items would end up and a risk stating that the agent could add
several items without asking again. The approver clicked a
button at 1731320595. The AS recorded that interaction, signed
it, and issued the token 105 seconds later.¶
The schema instance states what is permitted:
a $50 total, two operations, one location. The evidence states
why it is permitted: the approval decision and
the disclosure it was based on. The authority_ref in
the disclosure and the transaction_id of the schema
instance share a value, which is what correlates the two, and
authority_hash commits the signed evidence record to
the schema instance as issued, so that the correlation still
holds when the two are retrieved separately. A
Resource Server can then enforce the boundary deterministically
while retaining an audit trail from each authorized action back
to the decision.¶
The act.sub value uses the wit:// URI scheme
to identify the acting agent by its workload identity, as
defined in the Identity Assertion Authorization Grant
([I-D.ietf-oauth-identity-assertion-authz-grant]).
The hash suffix provides a collision-resistant binding between
the URI and the agent's attestation evidence.¶
{
"iss": "https://as.example.com",
"sub": "user_12345",
"aud": "https://api.shop.example",
"exp": 1731369540,
"iat": 1731320700,
"jti": "urn:uuid:4d9aca66-58a9-46f2-b2c1-06c48e99980d",
"act": {
"sub": "wit://myassistant.example/sha256.abc123..."
},
"authorization_details": [
{
"type": "authorization_evidence",
"evidence": {
"id": "urn:uuid:e73517f6-d8eb-4017-9602-2e251c7ffc7c",
"approval": {
"decision": "approved",
"approver": "user_12345",
"disclosure": {
"summary": "Add items under $50 to cart",
"authority_ref": "urn:uuid:d4affed6-3a05-4fa8-8d0f-936d5fdadc59",
"notices": [
"Items will be added to your saved cart at shop.example"
],
"risks": [
"The agent may add multiple items up to the $50 limit without further confirmation"
],
"locale": "en-US"
},
"action": "button_click",
"timestamp": 1731320595
},
"authority_hash": "yPT2jqmoJHFzOqrZRRfuOU8WNqkqDGhUmP_E09tgLKo",
"content_hash": "LTfvHZqdAF4BzvfUZkUccL2mudBr3Re81CLkIEFIZjg",
"as_signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im...",
"proposal_ref": "urn:uuid:d9172091-0653-41e2-8b76-9f960950c7bd",
"withdrawal_handle": "https://as.example.com/revoke/urn:uuid:181c3a47-7ae7-4109-8290-d69cd3ca6896"
}
},
{
"type": "transaction_authority",
"transaction_id": "urn:uuid:d4affed6-3a05-4fa8-8d0f-936d5fdadc59",
"domain": "ecommerce",
"boundary": {
"total_amount_limit": { "value": 50, "currency": "USD" },
"operations": ["ecommerce.search", "ecommerce.cart.add"]
},
"actions": ["search", "cart.add"],
"locations": ["https://api.shop.example"]
}
]
}
This appendix gives the intermediate values that the algorithm
in Section 5.3 produces for the
evidence object of Appendix A,
so that an implementation can check its JCS, digest, and
detached JWS handling against a known answer. The signing key
is a test key generated for this example and MUST NOT be used
in any deployment.¶
Step 4 of Section 5.3 digests the
authorization details object that carries the approved bounds.
For this token that is the transaction_authority
object of Appendix A. Its JCS
canonical form is 310 bytes, encoded in base64url below on
the same terms as the other encoded values in this appendix.¶
eyJhY3Rpb25zIjpbInNlYXJjaCIsImNhcnQuYWRkIl0sImJvdW5kYXJ5Ijp7Im9wZXJh dGlvbnMiOlsiZWNvbW1lcmNlLnNlYXJjaCIsImVjb21tZXJjZS5jYXJ0LmFkZCJdLCJ0 b3RhbF9hbW91bnRfbGltaXQiOnsiY3VycmVuY3kiOiJVU0QiLCJ2YWx1ZSI6NTB9fSwi ZG9tYWluIjoiZWNvbW1lcmNlIiwibG9jYXRpb25zIjpbImh0dHBzOi8vYXBpLnNob3Au ZXhhbXBsZSJdLCJ0cmFuc2FjdGlvbl9pZCI6InVybjp1dWlkOmQ0YWZmZWQ2LTNhMDUt NGZhOC04ZDBmLTkzNmQ1ZmRhZGM1OSIsInR5cGUiOiJ0cmFuc2FjdGlvbl9hdXRob3Jp dHkifQ
The SHA-256 digest of those bytes, base64url-encoded, is the
authority_hash member the evidence record carries:¶
yPT2jqmoJHFzOqrZRRfuOU8WNqkqDGhUmP_E09tgLKo
Step 6 of Section 5.3 collects every
member of the evidence object except
content_hash and as_signature. For this
record those members are id, approval,
authority_hash, proposal_ref, and
withdrawal_handle.
Applying JCS
[RFC8785] to them produces a canonical form of
683 bytes, encoded in base64url below. The line breaks are
not part of the value; concatenating the lines yields the
encoding of those 683 bytes.¶
eyJhcHByb3ZhbCI6eyJhY3Rpb24iOiJidXR0b25fY2xpY2siLCJhcHByb3ZlciI6InVz ZXJfMTIzNDUiLCJkZWNpc2lvbiI6ImFwcHJvdmVkIiwiZGlzY2xvc3VyZSI6eyJhdXRo b3JpdHlfcmVmIjoidXJuOnV1aWQ6ZDRhZmZlZDYtM2EwNS00ZmE4LThkMGYtOTM2ZDVm ZGFkYzU5IiwibG9jYWxlIjoiZW4tVVMiLCJub3RpY2VzIjpbIkl0ZW1zIHdpbGwgYmUg YWRkZWQgdG8geW91ciBzYXZlZCBjYXJ0IGF0IHNob3AuZXhhbXBsZSJdLCJyaXNrcyI6 WyJUaGUgYWdlbnQgbWF5IGFkZCBtdWx0aXBsZSBpdGVtcyB1cCB0byB0aGUgJDUwIGxp bWl0IHdpdGhvdXQgZnVydGhlciBjb25maXJtYXRpb24iXSwic3VtbWFyeSI6IkFkZCBp dGVtcyB1bmRlciAkNTAgdG8gY2FydCJ9LCJ0aW1lc3RhbXAiOjE3MzEzMjA1OTV9LCJh dXRob3JpdHlfaGFzaCI6InlQVDJqcW1vSkhGek9xclpSUmZ1T1U4V05xa3FER2hVbVBf RTA5dGdMS28iLCJpZCI6InVybjp1dWlkOmU3MzUxN2Y2LWQ4ZWItNDAxNy05NjAyLTJl MjUxYzdmZmM3YyIsInByb3Bvc2FsX3JlZiI6InVybjp1dWlkOmQ5MTcyMDkxLTA2NTMt NDFlMi04Yjc2LTlmOTYwOTUwYzdiZCIsIndpdGhkcmF3YWxfaGFuZGxlIjoiaHR0cHM6 Ly9hcy5leGFtcGxlLmNvbS9yZXZva2UvdXJuOnV1aWQ6MTgxYzNhNDctN2FlNy00MTA5 LTgyOTAtZDY5Y2QzY2E2ODk2In0
The content_hash member is the base64url encoding of
the SHA-256 digest of those bytes:¶
LTfvHZqdAF4BzvfUZkUccL2mudBr3Re81CLkIEFIZjg
This is the value an implementation compares its own digest against. Matching it confirms that the canonical form was reconstructed byte for byte, which is the part of the algorithm most easily implemented differently.¶
The JWS header is the following JSON object, whose base64url encoding is 90 characters, shown wrapped:¶
{"alg":"ES256","kid":"evidence-signing-key-1","typ":"evidence+jws"}
eyJhbGciOiJFUzI1NiIsImtpZCI6ImV2aWRlbmNlLXNpZ25pbmcta2V5LTEiLCJ0eXAi
OiJldmlkZW5jZStqd3MifQ
Because the JWS is detached
(Appendix F of [RFC7515]), the payload is omitted
from the serialization but is still part of the signature
input. The signature input is the ASCII encoding of the
base64url header, a period, and the base64url of the 683
canonical bytes, for a total of 1002 bytes. Signing it with
ES256 under the test key gives the following
as_signature value, wrapped for page width:¶
eyJhbGciOiJFUzI1NiIsImtpZCI6ImV2aWRlbmNlLXNpZ25pbmcta2V5LTEiLCJ0eXAi OiJldmlkZW5jZStqd3MifQ..22UCJlAjbHNkN3SnUfSlyw6kclM8DwpyI5KIe4IAAZBs jaH5wTZ4soXF0l4W6CutWsXiyqsdq-adUCuQVRvyJw
ECDSA signatures are randomized, so an implementation that signs the same input under the same key produces a different signature value. Verification, not reproduction, is the test: the value above verifies against the signature input under the public key given below.¶
{
"kty": "EC",
"crv": "P-256",
"kid": "evidence-signing-key-1",
"x": "d0xHxTVj1-wZckpUgGic1hcnXCweVUUUCAkAoPGjOVc",
"y": "oXz5fZ_W84y1JYu_Q9NZZPD2ipEuzs0Sa5q87jnr6Uo"
}
The canonical form covers the withdrawal_handle
member as well as approval. An implementation that
omits withdrawal_handle from the signing input, or
that includes content_hash in it, obtains a different
digest and a signature that does not verify.¶
The authors would like to thank Brian Campbell for his feedback during the development of this specification.¶