RFC 9770

Notification of Revoked Access Tokens in the Authentication and Authorization for Constrained Environments (ACE) Framework

๐Ÿ“… published June 2025, from ietf

RFC 9770 ๐ŸŽ€ Notification of Revoked Access Tokens in the Authentication and Authorization for Constrained Environments (ACE) Framework

Okay bestie, here's the lowdown on this one ๐ŸŒธ. It's RFC 9770, published by the IETF in June 2025 as a Standards Track document, and it's ISSN 2070-1721. The authors are M. Tiloca (RISE AB), F. Palombini (Ericsson AB), S. Echeverria and G. Lewis (both CMU SEI) ๐Ÿ’œ. It doesn't list anything it obsoletes or updates, so it's a fresh start, no drama ๐Ÿงš.

Abstract ๐Ÿ’–

Okay so this doc specifies a method for the ACE framework that lets an authorization server tell clients and resource servers (aka registered devices) about revoked access tokens ๐Ÿฆ‹. The method lets clients and RSs (resource servers) get at a Token Revocation List (TRL) on the authorization server using the Constrained Application Protocol (CoAP), and they can optionally use resource observation too โœจ. These unsolicited notifications of revoked tokens are a glow-up next to other approaches like token introspection, and the best part is that clients and RSs don't need any extra endpoints ๐Ÿซถ.

Status of This Memo ๐Ÿ“œ

This is an Internet Standards Track document. ๐ŸŒˆ

It's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 7841 ๐Ÿ’….

For the current status of this document, any errata, and how to send feedback, head to https://www.rfc-editor.org/info/rfc9770 ๐Ÿ”—.

Copyright (c) 2025 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) as they're in effect on the date this document was published. Please read these carefully, because they spell out your rights and restrictions for this document ๐Ÿ™ˆ. Code Components pulled out of this document must include the Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Revised BSD License โญ.

Table of Contents ๐Ÿ“

1.  Introduction
  1.1.  Terminology
2.  Protocol Overview
3.  Issuing of Access Tokens at the AS
4.  Token Hash
  4.1.  Motivation for the Used Construction
    4.1.1.  Issuing of the Access Token to the Client
    4.1.2.  Provisioning of Access Tokens to the RS
    4.1.3.  Design Rationale
  4.2.  Hash Input on the Client and the AS
    4.2.1.  AS-to-Client Response Encoded in CBOR
    4.2.2.  AS-to-Client Response Encoded in JSON
  4.3.  HASH_INPUT on the RS
    4.3.1.  Access Tokens as CWTs
    4.3.2.  Access Tokens as JWTs
  4.4.  Computing the Token Hash
5.  Token Revocation List (TRL)
  5.1.  Update of the TRL
6.  The TRL Endpoint
  6.1.  Error Responses with Problem Details
  6.2.  Supporting Diff Queries
    6.2.1.  Supporting the "Cursor" Extension
  6.3.  Query Parameters
7.  Full Query of the TRL
8.  Diff Query of the TRL
9.  Response Messages when Using the "Cursor" Extension
  9.1.  Response to Full Query
  9.2.  Response to Diff Query
    9.2.1.  Empty Update Collection
    9.2.2.  Cursor Not Included in the Diff Query Request
    9.2.3.  Cursor Included in the Diff Query Request
10. Registration at the Authorization Server
11. Notification of Revoked Access Tokens
  11.1.  Handling of Revoked Access Tokens and Token Hashes
12. ACE Token Revocation List Parameters
13. ACE Token Revocation List Error Identifiers
14. Security Considerations
  14.1.  Content Retrieval from the TRL
  14.2.  Size of the TRL
  14.3.  Communication Patterns
  14.4.  Request of New Access Tokens
  14.5.  Vulnerable Time Window at the RS
  14.6.  Preventing Unnoticed Manipulation of Access Tokens
  14.7.  Two Token Hashes at the RS Using JWTs
  14.8.  Additional Security Measures
15. IANA Considerations
  15.1.  Media Type Registrations
  15.2.  CoAP Content-Formats Registry
  15.3.  Custom Problem Detail Keys Registry
  15.4.  ACE Token Revocation List Parameters Registry
  15.5.  ACE Token Revocation List Errors
  15.6.  Expert Review Instructions
16. References
  16.1.  Normative References
  16.2.  Informative References
Appendix A.  On Using the Series Transfer Pattern
Appendix B.  Local Supportive Parameters of the TRL Endpoint
Appendix C.  Interaction Examples
  C.1.  Full Query with Observe
  C.2.  Diff Query with Observe
  C.3.  Full Query with Observe and Diff Query
  C.4.  Diff Query with Observe and "Cursor" Extension
  C.5.  Full Query with Observe and Diff Query with "Cursor"
        Extension
Acknowledgments
Authors' Addresses

1. Introduction โœจ

Okay, so the Authentication and Authorization for Constrained Environments (ACE) framework [RFC9200] is what enforces access control on Internet of Things (IoT) devices that act as resource servers (RSs) ๐Ÿงš. To use ACE, both clients and RSs have to register with an authorization server (AS) and become registered devices. Once they're registered, a client can ask the AS for an access token for an RS. Then, to get into the RS, the client has to present that token, and the RS validates it before storing it (see Section 5.10.1.1 of [RFC9200]) ๐Ÿ’….

Even though access tokens have expiration times, sometimes a token needs to be revoked before it expires ๐Ÿ˜ญ. That can happen when:

  1. a registered device has been compromised or is suspected of being compromised;
  2. a registered device is decommissioned;
  3. there has been a change in the ACE profile for a registered device;
  4. there has been a change in access policies for a registered device; or
  5. there has been a change in the outcome of policy evaluation for a registered device (e.g., if policy assessment depends on dynamic conditions in the execution environment, the user context, or the resource utilization).

As discussed in Section 6.1 of [RFC9200], right now only client-initiated revocation is specified [RFC7009] for OAuth 2.0 [RFC6749]. That's because it assumes access tokens in OAuth have a relatively short lifetime ๐Ÿ™ˆ. But that's not really the case for constrained, intermittently connected devices, which need access tokens with relatively long lifetimes ๐Ÿฅบ.

This document specifies a method that lets registered devices access and possibly subscribe to a Token Revocation List (TRL) on the AS ๐Ÿฆ‹. That way they can get updated info about related access tokens that were revoked before they expired, and they can find those tokens efficiently by their corresponding hash values. Here, registered devices talk to the AS and to each other using CoAP [RFC7252], and they can subscribe to the TRL on the AS with resource observation for CoAP [RFC7641]. Other underlying protocols aren't banned from being supported in the future, as long as they're defined for use in the ACE framework ๐ŸŒˆ.

Unlike token introspection (see Section 5.9 of [RFC9200]), a registered device doesn't hand an access token it owns over to the AS to ask whether it's still valid. Instead, registered devices just grab updated info about revoked tokens, identified by hash values ๐Ÿซถ.

The benefits here are that this complements token introspection, and registered devices don't need to support any extra endpoints (see Section 1.1) ๐Ÿ’–. The only extra things registered devices have to do are a request/response exchange with the AS to access and possibly subscribe to the TRL (see Section 2), plus the lightweight computation of hash values to use as access token identifiers (see Section 4) โœจ.

The process that declares access tokens revoked is out of scope for this document, and so is the method the AS uses to figure out, or get told about, revoked access tokens. Once the AS knows about them, it updates the TRL as specified here, but how it found out isn't covered ๐Ÿ“.

1.1. Terminology ๐Ÿ’…

okay bestie, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this doc mean what BCP 14 [RFC2119] [RFC8174] says they mean, but ONLY when they're in all capitals like this. ๐ŸŽ€ lowercase "must" is just vibes, not a rule ๐Ÿ™ˆ

you also need to be familiar with the ACE framework [RFC9200] ๐Ÿฆ‹, plus CBOR Web Tokens (CWTs) [RFC8392] and JSON Web Tokens (JWTs) [RFC7519]. no skipping, it's giving homework ๐Ÿ“š

the entity names in this architecture come from OAuth 2.0 [RFC6749] โœจ. so, client, RS, and authorization server (AS) all come from there ๐Ÿ‘‘

you should also know the stuff about the Concise Data Definition Language (CDDL) [RFC8610], Concise Binary Object Representation (CBOR) [RFC8949], JSON [RFC8259], CBOR Object Signing and Encryption (COSE) [RFC9052], CoAP [RFC7252], CoAP Observe [RFC7641], and using hash functions to name objects like in [RFC6920] ๐Ÿงš it's a lot, but it's all connected, iykyk ๐Ÿ’œ

quick note on "endpoint": this doc uses the OAuth meaning [RFC6749] ๐ŸŒธ. it's for things like /token and /introspect at the AS, and /authz-info at the RS. the CoAP definition, "[a]n entity participating in the CoAP protocol" [RFC7252], is NOT what we mean here ๐Ÿ™ˆ not me mixing those up lol

this spec also uses these terms:

  • Token hash: an identifier for an access token, in binary format encoding. It has no relation to other access token identifiers that might be used, like the 'cti' (CWT ID) claim of CBOR Web Tokens (CWTs) [RFC8392]. different thing, don't mix them up ๐ŸŽ€
  • Token Revocation List (TRL): a collection of token hashes where the matching access tokens have been revoked but aren't expired yet ๐Ÿฅ€
  • TRL endpoint: an endpoint at the AS that has a TRL as its representation. the default name in a url-path is /revoke/trl. implementations don't have to use that name and can make up their own instead ๐Ÿ“
  • Registered device: a device registered at the AS, meaning a client, an RS, or both. a registered device acts as a requester toward the TRL endpoint ๐Ÿซถ
  • Administrator: an entity authorized to get FULL access to the TRL at the AS, and it acts as a requester toward the TRL endpoint. an administrator isn't necessarily a registered device like we defined above (so, a client asking for access tokens or an RS using them) ๐Ÿ‘‘
an administrator might also get authorized to do more admin stuff at the AS, like through a dedicated admin interface that's OUT of scope for this doc ๐Ÿ™…โ€โ™€๏ธ. once they pull token hashes from the TRL and combine them with other info from the AS, they can figure out extra stuff, like the fact that accesses were revoked for specific registered devices. big brain energy ๐Ÿง โœจ
  • Pertaining access token:
    • for an administrator: an access token the AS issued ๐Ÿฆ‹
    • for a registered device: an access token that's meant to be owned by that device. an access token pertains to a client if the AS issued it for that client after its request. an access token pertains to an RS if the AS issued it to be consumed by that RS ๐ŸŒˆ
  • Token hash pertaining to a requester: a token hash that matches an access token pertaining to that requester, aka an administrator or a registered device ๐Ÿ’œ
  • TRL update pertaining to a requester: an update to the TRL where token hashes pertaining to that requester got added to or removed from the TRL ๐Ÿ”ฅ
  • Full query: a kind of TRL query where the AS returns the token hashes of the revoked access tokens that are currently in the TRL and pertain to the requester. more details in Sections 6 and 7 ๐Ÿ“‹
  • Diff query: a kind of TRL query where the AS returns a list of diff entries. each entry is about one update that happened to the TRL and holds a set of token hashes pertaining to the requester. more details in Sections 6 and 8 โญ

see Section 4 for more terminology used throughout that section ๐Ÿงš

examples in this doc are written in CBOR diagnostic notation, as defined in Section 8 of [RFC8949] and Appendix G of [RFC8610] ๐ŸŽ€. the diagnostic notation comments often give you a text version of the numeric parameter names and values, so you're not left guessing ๐Ÿ™ˆ


2. Protocol Overview โœจ

okay so this protocol is all about how a CoAP-based AS tells clients and RSs (aka registered devices) about revoked access tokens that belong to them ๐Ÿฆ‹. how the relationship between a registered device and the AS gets set up? NOT this spec's problem, it's out of scope ๐Ÿ™…โ€โ™€๏ธ

big picture, the steps go like this:

  1. startup: the AS creates ONE single TRL that's accessible through the TRL endpoint. at any moment, the TRL represents the list of all revoked access tokens issued by the AS that aren't expired yet ๐ŸŒธ
  2. registration: when a device registers at the AS, it also gets the url-path to the TRL endpoint ๐Ÿ“

at any point after registration is finished, the registered device can send a GET request to the TRL endpoint at the AS. when it does, it can ask for either the current list of pertaining revoked access tokens (see Section 7), or the most recent updates to that list (see Section 8) ๐Ÿ’…

the registered device can rely on Observation for CoAP [RFC7641] too ๐Ÿ‘€. in that case, the GET request to the TRL endpoint includes the CoAP Observe Option set to 0 (register), so it's an Observation Request. by doing this, the registered device basically subscribes to the TRL, because it wants notifications about TRL updates ๐Ÿ””. when the AS gets the Observation Request, it adds the registered device to the list of observers of the TRL endpoint ๐Ÿซถ

  1. revocation: when an access token gets revoked, the AS adds its token hash to the TRL. and when a revoked access token eventually expires, the AS removes its token hash from the TRL ๐Ÿฅ€

either way, after updating the TRL, the AS sends Observe notifications as per [RFC7641]. so an Observe notification goes to each registered device that's subscribed to the TRL AND that the access token pertains to ๐Ÿ”ฅ

depending on the specific subscription made through the Observation Request, the notification gives either the current updated list of revoked access tokens in the part of the TRL that pertains to that device (see Section 7), or the most recent TRL updates over that list of pertaining revoked access tokens (see Section 8) โœจ

more Observe notifications may get sent, consistent with any ongoing extra observations of the TRL endpoint ๐ŸŒˆ

  1. admins: an administrator can access and subscribe to the TRL just like a registered device, while getting the content of the WHOLE TRL (see Section 7) or the most recent updates to the whole TRL (see Section 8) ๐Ÿ‘‘

Figure 1 shows the big-picture service this protocol gives you ๐Ÿงš. to keep it simple, the figure looks at the simultaneous revocation of three access tokens, t1, t2, and t3, whose token hashes are th1, th2, and th3 ๐ŸŽ€. so the AS adds all three token hashes to the TRL at once and sends Observe notifications to ONE administrator and FOUR registered devices ๐Ÿ’–. each dotted line between a pair of registered devices shows the access token they BOTH own ๐Ÿฆ‹

                       +----------------------+
                       | Authorization server |
                       +-----------o----------+
                     /revoke/trl   |   TRL: (th1,th2,th3)
                                   |
    +-----------------+------------+------------+------------+
    |                 |            |            |            |
    | th1,th2,th3     | th1,th2    | th1        | th3        | th2,th3
    v                 v            v            v            v
   +---------------+ +----------+ +----------+ +----------+ +----------+
   | Administrator | | Client 1 | | Resource | | Client 2 | | Resource |
   |               | |          | | server 1 | |          | | server 2 |
   +---------------+ +----------+ +----------+ +----------+ +----------+
                        :    :        :           :            :    :
                        :    :   t1   :           :     t3     :    :
                        :    :........:           :............:    :
                        :                   t2                      :
                        :...........................................:

                        Figure 1: Protocol Overview

Appendix C has examples of the protocol flow and message exchanges between the AS and a registered device ๐Ÿ“–๐Ÿ’œ

3. Issuing of Access Tokens at the AS โœจ

okay bestie, this part is for the AS (authorization server) when it's minting access tokens ๐ŸŽ€ if the AS supports the method in this document, it MUST follow these rules when issuing an access token. no exceptions, periodt ๐Ÿ’…

  • All the intended header parameters in the access token MUST be specified within integrity-protected fields. ๐Ÿ”’
  • If the access token is a CWT, the following applies:
    • Any "unprotected" field MUST be empty, i.e., its value MUST be encoded as the empty CBOR map (0xa0). This applies to the top-level "unprotected" field of the COSE object used for the CWT, the "unprotected" field of each element of the "signatures" array, and the "unprotected" field of each element of any "recipients" array (see Sections 2, 3, 4, 5, and 6 of [RFC9052]).
    • Consistent with the specific COSE object used for the CWT, the corresponding tagged structure in the set COSE_Tagged_Message MUST be used (see Section 2 of [RFC9052]). So the CBOR array that encodes the CWT MUST be tagged with the COSE CBOR tag matching the COSE object you used. Table 1 in Section 2 of [RFC9052] lists the tag numbers in question ๐Ÿท๏ธ
    • Then the resulting tagged data item MUST be tagged with the CWT CBOR tag, tag number 61 (see Section 6 of [RFC8392]). After that, the resulting data item MUST NOT be further tagged. no double-tagging drama ๐Ÿ™…โ€โ™€๏ธ
    • Encoding of the tag numbers MUST be done using definite lengths, and the length of the encoded tag numbers MUST be the minimum possible length. This means that tag number 16 is encoded as 0xd0 and not as 0xd810. very particular, very important ๐Ÿง
    • The example in Figure 2 shows a CWT that uses the COSE object COSE_Encrypt0 (see Section 5.2 of [RFC9052]). ๐Ÿฆ‹
  • If, like for JWTs [RFC7519], the access token relies on a JSON object for encoding its claims, the following applies:

Consistent with the ACE framework [RFC9200], this document specifically considers JWTs, which are always represented using the JSON Web Signature (JWS) Compact Serialization from [RFC7515] or the JSON Web Encryption (JWE) Compact Serialization from [RFC7516]. Consequently, all the header parameters are specified within integrity-protected fields. ๐ŸŒธ

In case alternative access tokens were used, the following applies:

  • If the access token uses the JWS JSON Serialization from [RFC7515], it MUST NOT include the JWS Unprotected Header.
  • If the access token uses the JWE JSON Serialization from [RFC7516], it MUST NOT include the JWE Shared Unprotected Header and it MUST NOT include the "header" member in any of the elements of the "recipients" array.
   / CWT CBOR tag / 61(
     / COSE_Encrypt0 CBOR tag / 16(
       / COSE_Encrypt0 object / [
         / protected /   h'a3010a044c53796d6d65747269633132
                           38054d99a0d7846e762c49ffe8a63e0b',
         / unprotected / {},
         / ciphertext /  h'b918a11fd81e438b7f973d9e2e119bcb
                           22424ba0f38a80f27562f400ee1d0d6c
                           0fdb559c02421fd384fc2ebe22d70713
                           78b0ea7428fff157444d45f7e6afcda1
                           aae5f6495830c58627087fc5b4974f31
                           9a8707a635dd643b'
       ]
     )
   )

Figure 2: Example of CWT Using COSE_Encrypt0 ๐Ÿ“ธ

Section 14.6 discusses how adhering to the rules above neutralizes an attack against the RS where an active adversary can induce the RS to compute a token hash different from the correct one. this one is seriously important, bestie ๐Ÿ˜ญ๐Ÿ”ฅ


4. Token Hash โœจ

this section is where we spell out how token hashes get computed ๐Ÿงฎ

First, Section 4.1 provides the motivation for the used construction. ๐Ÿ’ญ

Building on that, the value used as input to compute a token hash is defined in Section 4.2 for the client and the AS and in Section 4.3 for the RS. Finally, Section 4.4 defines how such an input is used for computing the token hash. ๐Ÿช„

The process outlined below refers to the base64url encoding and decoding without padding (see Section 5 of [RFC4648]) and denotes as "binary representation" of a text string the corresponding UTF-8 encoding [RFC3629], which is the implied charset used in JSON (see Section 8.1 of [RFC8259]). ๐Ÿ“

Consistent with Section 3.4 of [RFC8949], the term "tag" is used for the entire CBOR data item consisting of both a tag number and the tag content: the tag content is the CBOR data item that is being tagged. ๐Ÿท๏ธ

Also, "tagged access token" is used to denote nested CBOR tags (possibly a single one), with the innermost tag content being a CWT. ๐Ÿง

4.1. Motivation for the Used Construction

An access token can have one among different formats. The most expected formats are CWT [RFC8392] and JWT [RFC7519], with the former being the default format to use in the ACE framework (see Section 3 of [RFC9200]). While access tokens are opaque to clients, an RS is aware of whether access tokens that are issued for it to consume are either CWTs or JWTs. ๐Ÿ‘€

4.1.1. Issuing of the Access Token to the Client

There are two possible encodings that the AS can use for the AS-to-Client response (see Section 5.8.2 of [RFC9200]) where the issued access token is included and provided to the requester client. The RS may not be aware of which encoding is used for that response to that particular requester client. ๐Ÿคทโ€โ™€๏ธ

  • One method of encoding relies on CBOR, which is required if CoAP is used (see Section 5 of [RFC9200]) and is recommended otherwise (see Section 3 of [RFC9200]). That is, the AS-to-Client response has media-type "application/ace+cbor".

This implies that, within the CBOR map specified as message payload, the 'access_token' parameter is a CBOR data item of type CBOR byte string and with a value of BYTES. In particular:

  • If the access token is a CWT, then BYTES is the binary representation of the CWT (i.e., of the CBOR array that encodes the untagged CWT) or of a tagged access token with the CWT as the innermost tag content.
  • If the access token is a JWT, then BYTES is the binary representation of the JWT (i.e., of the text string that encodes the JWT).
  • An alternative method of encoding relies on JSON. That is, the AS-to-Client response has media-type "application/ace+json".

This implies that, within the JSON object specified as message payload, the 'access_token' parameter has as a value a text string TEXT. In particular:

  • If the access token is a JWT, then TEXT is the text string that encodes the JWT.
  • If the access token is a CWT, then TEXT is the base64url-encoded text string of BYTES, which is the binary representation of the CWT (i.e., of the CBOR array that encodes the untagged CWT) or of a tagged access token with the CWT as the innermost tag content.

4.1.2. Provisioning of Access Tokens to the RS

In accordance with the used transport profile of ACE (e.g., [RFC9202], [RFC9203], [RFC9431]), the RS receives a piece of token-related information hereafter denoted as TOKEN_INFO. ๐Ÿ“ฆ

In particular:

  • If the AS-to-Client response was encoded in CBOR, then TOKEN_INFO is the value of the CBOR byte string conveyed by the 'access_token' parameter of that response. That is, TOKEN_INFO is the binary representation of the access token.
  • If the AS-to-Client response was encoded in JSON and the access token is a JWT, then TOKEN_INFO is the binary representation of the text string conveyed by the 'access_token' parameter of that response. That is, TOKEN_INFO is the binary representation of the access token.
  • If the AS-to-Client response was encoded in JSON and the access token is a CWT, then TOKEN_INFO is the binary representation of the base64url-encoded text string that encodes the binary representation of the access token. That is, TOKEN_INFO is the binary representation of the base64url-encoded text string conveyed by the 'access_token' parameter.

The following overviews how the above specifically applies to the existing transport profiles of ACE: ๐Ÿ—บ๏ธ

  • The access token can be uploaded to the RS by means of a POST request to the /authz-info endpoint (see Section 5.10.1 of [RFC9200]), using a CoAP Content-Format or HTTP media-type that reflects the format of the access token, if available (e.g., "application/cwt" for CWTs), or "application/octet-stream" otherwise. When doing so (e.g., like in [RFC9202]), TOKEN_INFO is the payload of the POST request.
  • The access token can be uploaded to the RS by means of a POST request to the /authz-info endpoint, using the media-type "application/ace+cbor". When doing so (e.g., like in [RFC9203]), TOKEN_INFO is the value of the CBOR byte string conveyed by the 'access_token' parameter, within the CBOR map specified as payload of the POST request.
  • The access token can be uploaded to the RS during a DTLS session establishment, e.g., like it is defined in Section 3.3.2 of [RFC9202]. When doing so, TOKEN_INFO is the value of the 'psk_identity' field of the ClientKeyExchange message (when using DTLS 1.2 [RFC6347]) or of the 'identity' field of a PSKIdentity, within the PreSharedKeyExtension of a ClientHello message (when using DTLS 1.3 [RFC9147]).
  • The access token can be uploaded to the RS within the MQTT CONNECT packet, e.g., like it is defined in Section 2.2.4.1 of [RFC9431]. When doing so, TOKEN_INFO is specified within the 'Authentication Data' field of the MQTT CONNECT packet, following the property identifier 22 (0x16) and the token length.

Note that, if the access token is a CWT, it is specifically tagged as defined in Section 3. ๐ŸŽ€

4.1.3. Design Rationale

Considering the possible variants discussed above, it must always be ensured that the same HASH_INPUT value is used as input for generating the token hash of a given access token, by the AS that has issued the access token and by the registered devices to which the access token pertains (both client and RS). ๐Ÿง 

This is achieved by building HASH_INPUT according to the content of the 'access_token' parameter in the AS-to-Client responses because that is what the AS, the client, and the RS are all able to see. the whole thing hinges on everyone seeing the same bytes ๐Ÿ‘‘

4.2. Hash Input on the Client and the AS

The client and the AS consider the content of the 'access_token' parameter in the AS-to-Client response, in which the access token is included and provided to the requester client. Note that, if the access token is a CWT, it is specifically tagged as defined in Section 3. โœจ

The following defines how the client and the AS determine the HASH_INPUT value to use as input for computing the token hash of the conveyed access token, depending on the AS-to-Client response being encoded in CBOR (see Section 4.2.1) or in JSON (see Section 4.2.2). ๐Ÿ’œ

Once HASH_INPUT is determined, the client and the AS use it to compute the token hash of the conveyed access token as defined in Section 4.4. ๐Ÿฆ‹

4.2.1. AS-to-Client Response Encoded in CBOR โœจ

Okay so if the AS-to-Client response is encoded in CBOR, then HASH_INPUT gets defined like this ๐Ÿ’…

  • BYTES is the value of the CBOR byte string conveyed in the 'access_token' parameter. Looking at the example in Figure 3, BYTES is the bytes {0xd8, 0x3d, 0xd0, ..., 0x64, 0x3b}.
    • Heads up: BYTES is the binary representation of the tagged access token if this is a CWT (as per Section 3), or of the access token if this is a JWT. ๐Ÿซถ
  • HASH_INPUT_TEXT is the base64url-encoded text string that encodes BYTES.
  • HASH_INPUT is the binary representation of HASH_INPUT_TEXT.
   Header: Created (Code=2.01)
   Content-Format: 19 (application/ace+cbor)
   Max-Age: 85800
   Payload:
   {
      / access_token / 1 : h'd83dd0835820a3010a044c53796d6d
                             6574726963313238054d99a0d7846e
                             762c49ffe8a63e0ba05858b918a11f
                             d81e438b7f973d9e2e119bcb22424b
                             a0f38a80f27562f400ee1d0d6c0fdb
                             559c02421fd384fc2ebe22d7071378
                             b0ea7428fff157444d45f7e6afcda1
                             aae5f6495830c58627087fc5b4974f
                             319a8707a635dd643b',
      / token_type /  34 : 2 / PoP /,
      / expires_in /   2 : 86400,
      / ace_profile / 38 : 1 / coap_dtls /
      / (remainder of the response omitted for brevity) /
   }

Figure 3: Example of AS-to-Client CoAP Response Using CBOR ๐ŸŽ€

4.2.2. AS-to-Client Response Encoded in JSON ๐ŸŒธ

If the AS-to-Client response is encoded in JSON, then HASH_INPUT is the binary representation of the text string conveyed by the 'access_token' parameter.

With reference to the example in Figure 4, HASH_INPUT is the binary representation of "eyJh...YFiA". Figure 4 uses line breaks when showing the access token, but those are just for display purposes, okay? ๐Ÿ™ˆ

Note that:

  • If the access token is a JWT, then HASH_INPUT is the binary representation of the JWT.
  • If the access token is a CWT, then HASH_INPUT is the binary representation of a base64url-encoded text string, which encodes the binary representation of a tagged access token with the CWT as the innermost tag content (as per Section 3). ๐Ÿฆ‹
   HTTP/1.1 200 OK
   Content-Type: application/ace+json
   Cache-Control: no-store
   Pragma: no-cache
   Payload:
   {
      "access_token" : "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJB
                        MTI4Q0JDLUhTMjU2In0.
                        QR1Owv2ug2WyPBnbQrRARTeEk9kDO2w8
                        qDcjiHnSJflSdv1iNqhWXaKH4MqAkQtM
                        oNfABIPJaZm0HaA415sv3aeuBWnD8J-U
                        i7Ah6cWafs3ZwwFKDFUUsWHSK-IPKxLG
                        TkND09XyjORj_CHAgOPJ-Sd8ONQRnJvW
                        n_hXV1BNMHzUjPyYwEsRhDhzjAD26ima
                        sOTsgruobpYGoQcXUwFDn7moXPRfDE8-
                        NoQX7N7ZYMmpUDkR-Cx9obNGwJQ3nM52
                        YCitxoQVPzjbl7WBuB7AohdBoZOdZ24W
                        lN1lVIeh8v1K4krB8xgKvRU8kgFrEn_a
                        1rZgN5TiysnmzTROF869lQ.
                        AxY8DCtDaGlsbGljb3RoZQ.
                        MKOle7UQrG6nSxTLX6Mqwt0orbHvAKeW
                        nDYvpIAeZ72deHxz3roJDXQyhxx0wKaM
                        HDjUEOKIwrtkHthpqEanSBNYHZgmNOV7
                        sln1Eu9g3J8.
                        fiK51VwhsxJ-siBMR-YFiA",
      "token_type"   : "PoP",
      "expires_in"   : 86400,
      "ace_profile"  : "coap_dtls"
   }

Figure 4: Example of AS-to-Client HTTP Response Using JSON

4.3. HASH_INPUT on the RS ๐Ÿ‘‘

The following explains how the RS figures out the HASH_INPUT value to use when computing the token hash of an access token. It depends on whether the RS is using CWTs (see Section 4.3.1) or JWTs (see Section 4.3.2). โœจ

4.3.1. Access Tokens as CWTs ๐Ÿงš

If the RS expects access tokens to be CWTs, then the RS runs through these steps:

  1. The RS receives the token-related information TOKEN_INFO, following whatever the used profile of ACE specifies (see Section 4.1.2).
  2. The RS assumes that the client got the access token in an AS-to-Client response encoded in CBOR (see Section 4.2.1). So the RS treats TOKEN_INFO as the binary representation of the tagged access token, with the CWT as the innermost tag content (as per Section 3).
  3. The RS verifies the access token as per Section 5.10.1.1 of [RFC9200]. If the verification fails, then the RS does not discard the access token yet. Instead, it moves on to Step 4.

Otherwise, the RS stores the access token and computes the matching token hash as defined in Section 4.4. Specifically, the RS considers HASH_INPUT_TEXT to be the base64url-encoded text string that encodes TOKEN_INFO. Then HASH_INPUT is the binary representation of HASH_INPUT_TEXT.

After that, the RS stores the computed token hash as associated with the access token, and then it wraps up this algorithm. ๐Ÿ’–

  1. The RS assumes that the client got the access token in an AS-to-Client response encoded in JSON (see Section 4.2.2). So the RS treats TOKEN_INFO as the binary representation of HASH_INPUT_TEXT. In turn, HASH_INPUT_TEXT is the base64url-encoded text string that encodes the binary representation of the tagged access token, with the CWT as the innermost tag content (as per Section 3).
  2. The RS does the base64url decoding of HASH_INPUT_TEXT and treats the result as the binary representation of the tagged access token.
  3. The RS verifies the access token as per Section 5.10.1.1 of [RFC9200]. If the verification fails, then the RS wraps up this algorithm.

Otherwise, the RS stores the access token and computes the matching token hash as defined in Section 4.4. Specifically, HASH_INPUT is TOKEN_INFO.

After that, the RS stores the computed token hash as associated with the access token. ๐Ÿ”ฅ

4.3.2. Access Tokens as JWTs ๐ŸŒˆ

If the RS expects access tokens to be JWTs, then the RS runs through these steps:

  1. The RS receives the token-related information TOKEN_INFO, following whatever the used profile of ACE specifies (see Section 4.1.2).
  2. The RS verifies the access token as per Section 5.10.1.1 of [RFC9200]. If the verification fails, then the RS wraps up this algorithm. Otherwise, the RS stores the access token.
  3. The RS computes a first token hash associated with the access token as defined in Section 4.4.

Specifically, the RS assumes that the client got the access token in an AS-to-Client response encoded in JSON (see Section 4.2.2). So HASH_INPUT is TOKEN_INFO.

After that, the RS stores the computed token hash as associated with the access token. ๐Ÿ’œ

  1. The RS computes a second token hash associated with the access token as defined in Section 4.4.

Specifically, the RS assumes that the client got the access token in an AS-to-Client response encoded in CBOR (see Section 4.2.1). So HASH_INPUT is the binary representation of HASH_INPUT_TEXT, which in turn is the base64url-encoded text string that encodes TOKEN_INFO.

After that, the RS stores the computed token hash as associated with the access token.

The RS skips Step 3 only if it is certain that all its pertaining access tokens are provided to any client by means of AS-to-Client responses encoded as CBOR messages. Otherwise, the RS MUST perform Step 3. ๐Ÿ™ƒ

The RS skips Step 4 only if it is certain that all its pertaining access tokens are provided to any client by means of AS-to-Client responses encoded as JSON messages. Otherwise, the RS MUST perform Step 4. ๐Ÿ™ƒ

If the RS performs both Steps 3 and 4 above, then the RS MUST store, maintain, and rely on both token hashes as associated with the access token, consistent with what Section 11.1 specifies. ๐Ÿ”’

Section 14.7 explains how computing and storing both token hashes shuts down an attack against the RS, where a dishonest client can trick the RS into computing a token hash that's different from the correct one. It's giving main character defense energy ๐Ÿ›ก๏ธ๐Ÿ’€

4.4. Computing the Token Hash ๐Ÿ“

Once HASH_INPUT is determined as defined in Sections 4.2 and 4.3, a hash value of HASH_INPUT is generated as per Section 6 of [RFC6920]. The output in binary format is used as the token hash. Note that the binary format embeds the identifier of the hash function used in the first byte of the computed token hash. ๐Ÿง 

The specific hash function used MUST be collision resistant on byte strings and MUST be selected from the "Named Information Hash Algorithm Registry" [IANA.Hash.Algorithms]. Consistent with the compliance requirements in Section 2 of [RFC6920], the hash function sha-256 as specified in [SHA-256] is mandatory to implement. โœ…

The AS specifies the used hash function to registered devices during their registration procedure (see Section 10). ๐Ÿ“‹

5. Token Revocation List (TRL) ๐Ÿ—‘๏ธ

Upon startup, the AS creates a single Token Revocation List (TRL) encoded as a CBOR array. ๐Ÿš€

Each element of the array is a CBOR byte string, whose value is the token hash of an access token. The CBOR array MUST be treated as a set, i.e., the order of its elements has no meaning. ๐Ÿซง

The TRL is initialized as empty, i.e., its initial content MUST be the empty CBOR array. The TRL is accessible through the TRL endpoint at the AS. ๐ŸŒŸ

5.1. Update of the TRL ๐Ÿ”„

The AS updates the TRL in the following two cases:

  • When a non-expired access token is revoked, the token hash of the access token is added to the TRL. That is, a CBOR byte string with the token hash as its value is added to the CBOR array encoding the TRL. โž•
  • When a revoked access token expires, the token hash of the access token is removed from the TRL. That is, the CBOR byte string with the token hash as its value is removed from the CBOR array encoding the TRL. โž–

The AS MAY perform a single update to the TRL such that one or more token hashes are added or removed at once. For example, this can be the case if multiple access tokens are revoked or expire at the same time or within an acceptably narrow time frame. โฐ

6. The TRL Endpoint โœจ

Okay so, bestie, this part is all about the TRL endpoint at the AS ๐Ÿ’– Consistent with Section 6.5 of [RFC9200], every single conversation between the AS and a requester at that endpoint MUST be encrypted, and it MUST also be integrity and replay protected ๐Ÿ” Responses from the AS to the requester MUST be bound to the matching requests too. No loose ends, periodt ๐Ÿ’…

After a request hits the TRL endpoint, a successful response from the AS uses Content-Format application/ace-trl+cbor ๐ŸŽ€ The payload is a CBOR map, and the CBOR values that shorten the parameters inside it are defined in Section 12 ๐Ÿงš

The AS MUST put measures in place so that only registered devices and authorized administrators can get into the TRL endpoint (see Section 10). Strangers stay out, no cap ๐Ÿšซ

The TRL endpoint only supports the GET method, and it lets you run two kinds of queries on the TRL ๐Ÿฆ‹

  1. Full query: the AS hands back the token hashes of the revoked access tokens that are currently in the TRL and belong to the requester ๐Ÿ‘‘

The AS MUST support this type of query. How a full query gets processed and what its response looks like are defined in Section 7.

  1. Diff query: the AS hands back a list of diff entries. Each diff entry is tied to one update that happened to the TRL, and it holds a set of token hashes that belong to the requester. Every one of those hashes was either added to the TRL or removed from it during that specific update ๐ŸŒˆ

The AS MAY support this type of query. If it does, the AS keeps a history of TRL updates as defined in Section 6.2. How a diff query gets processed and what its response looks like are defined in Section 8 โญ

If the AS supports diff queries, it MAY also support the related "Cursor" extension, and that extension has two perks ๐Ÿ“

  1. The AS can dodge super long messages when a bunch of diff entries need to go out. It can send several diff query responses instead, each one carrying a neat adjacent subset of diff entries ๐Ÿ“ฆ
  2. A requester can grab diff entries for TRL updates that aren't the newest ones, as long as those updates happened after the update tied to the diff entry it named as a reference point ๐Ÿ”

If the AS supports the "Cursor" extension, it stores extra info while it keeps the history of TRL updates (see Section 6.2.1). On top of that, processing full query and diff query requests, along with their response formats, gets extended as defined in Section 9 โœจ

Appendix B has an overview of all the local helper parameters the AS uses at its TRL endpoint when it supports diff queries and the "Cursor" extension ๐Ÿซถ

6.1. Error Responses with Problem Details

Some error responses from the TRL endpoint at the AS can carry error-specific info using the problem-details format defined in [RFC9290] ๐Ÿ™ˆ These error responses MUST have Content-Format set to application/concise-problem-details+cbor. The payload of these error responses MUST be a CBOR map that specifies a Concise Problem Details data item (see Section 2 of [RFC9290]). The CBOR map is shaped like this:

  • It MUST include the Custom Problem Detail entry 'ace-trl-error', which is registered in Section 15.3 of this document ๐Ÿ’œ This entry is a CBOR map with these fields:
    • The 'error-id' field MUST be present. Its map key is the CBOR unsigned integer 0. Its value is a CBOR integer that tells you which error happened at the AS. That value comes from the 'Value' column of the "ACE Token Revocation List Errors" registry defined in Section 15.5 of this document ๐Ÿ”ฅ
    • The 'cursor' field MAY be present. Its map key is the CBOR unsigned integer 1. Its value is either a CBOR unsigned integer or the CBOR simple value null (0xf6). How this field gets used is defined in Section 6.3 ๐ŸŒธ

The CDDL notation [RFC8610] for the 'ace-trl-error' entry looks like this:

ace-trl-error = {
    0: int,        ; error-id
  ? 1: uint / null ; cursor
}
  • It MAY include further Standard Problem Detail entries or Custom Problem Detail entries (see [RFC9290]) ๐Ÿงš

The Standard Problem Detail entry 'detail' (map key -2) is a good one to include. Its value is a CBOR text string with a human-readable diagnostic description of the error that happened at the AS ๐Ÿ’ฌ That text is meant for software engineers, plus device and network operators, so they can debug stuff and get context for any intervention they might need. The diagnostic message SHOULD be logged by the AS. Heads up though: the 'detail' entry probably won't matter much in an unattended setup where nobody is expected to step in ๐Ÿค–

Here's an example of an error response using the problem-details format, shown in Figure 5 ๐ŸŽ€

Header: Bad Request (Code=4.00)
Content-Format: 257 (application/concise-problem-details+cbor)
Payload:
{
  / title /         -1: "Invalid parameter value",
  / detail /        -2: "Invalid value for 'cursor': -53",
  / ace-trl-error /  1: {
    / error-id / 0: 0 / "Invalid parameter value" /,
    / cursor /   1: 42
  }
}

Figure 5: Example of Error Response with Problem Details

The problem-details format in general, and the Custom Problem Detail entry 'ace-trl-error' in particular, are OPTIONAL to support for registered devices ๐ŸŒˆ A registered device that supports the 'ace-trl-error' entry, and that can understand the error it's given, can use that info to figure out what to do next โœจ

6.2. Supporting Diff Queries

If the AS supports diff queries, it can send over a list of diff entries, and each one is tied to one update that happened to the TRL (see Section 6) ๐Ÿฆ‹ So when the AS answers a diff query for a requester, it gives back the diff entries for the most recent TRL updates that belong to that requester.

Here's how the AS builds and keeps an ordered list of diff entries for each registered device and administrator. From here on, those are called "requesters" ๐Ÿซถ A requester's diff entry for a TRL update holds a set of token hashes that belong to that requester, and each one was added to the TRL or removed from it during that update.

The AS defines the single constant positive integer MAX_N >= 1 ๐Ÿ’… For each requester, the AS keeps an update collection of at most MAX_N series items, and each of those series items is a diff entry. For each requester, the AS MUST keep track of the MAX_N most recent TRL updates that belong to that requester. If the AS supports diff queries, it MUST give requesters the value of MAX_N when they register (see Section 10) ๐Ÿ‘‘

The series items in the update collection MUST be strictly chronologically ordered ๐Ÿ•ฐ๏ธ At any moment, the first series item is the one that was added to the update collection the longest ago and is still being kept by the AS. The last series item is the one that was added most recently. How the AS pulls this off is up to the implementation ๐ŸŒธ

Every time the TRL changes, the AS does these steps for each requester ๐Ÿ”

  1. The AS looks at the subset of the TRL that belongs to that requester. If this TRL update doesn't touch that subset, the AS stops processing for that requester. Otherwise, it moves on to Step 2.
  2. The AS creates two trl_patch sets of token hashes, one called the 'removed' set and one called the 'added' set, both tied to this TRL update ๐Ÿ’–
  3. The AS fills those two sets with the token hashes of the removed access tokens and the added access tokens, respectively. It pulls them from the TRL subset it looked at in Step 1 โœจ
  4. The AS creates a new series item that holds the two sets from Step 3 ๐Ÿงš
  5. If the update collection for that requester already has MAX_N series items, the AS MUST delete the oldest series item in it ๐Ÿ—‘๏ธ
  6. The AS adds the series item to that requester's update collection as the last (most recent) series item ๐ŸŽ€

6.2.1. Supporting the "Cursor" Extension

If the AS supports the "Cursor" extension for diff queries, it also does the following ๐Ÿ“

The AS defines the single constant unsigned integer MAX_INDEX, where MAX_INDEX <= ((2^64) - 1). The value of MAX_INDEX is REQUIRED to be at least (MAX_N - 1) and is RECOMMENDED to be at least ((2^32) - 1). MAX_INDEX SHOULD be orders of magnitude greater than MAX_N ๐Ÿ”ข

All of this applies separately to each requester's update collection ๐Ÿ’œ

  • Every series item X in the update collection is also tied to an unsigned integer 'index', whose minimum value is 0 and whose maximum value is MAX_INDEX. The first series item ever added to the update collection MUST have an 'index' with a value of 0 ๐Ÿฅ‡

If i_X is the 'index' value for series item X, then the series item Y that comes right after it gets i_Y = (i_X + 1) % (MAX_INDEX + 1). So once a series item with 'index' MAX_INDEX has been added, the next one wraps around and gets an 'index' of 0 ๐Ÿ”„

Here's an example with MAX_N = 3, showing how the 'index' values in the update collection change as new series items get added and old ones get deleted ๐Ÿงธ

...
(i_A = MAX_INDEX - 2, i_B = MAX_INDEX - 1, i_C = MAX_INDEX)
(i_B = MAX_INDEX - 1, i_C = MAX_INDEX, i_D = 0)
(i_C = MAX_INDEX, i_D = 0, i_E = 1)
(i_D = 0, i_E = 1, i_F = 2)
...
  • The unsigned integer 'last_index' is also defined, with a minimum value of 0 and a maximum value of MAX_INDEX ๐Ÿ“

If the update collection is empty (meaning no series items have been added yet), the value of 'last_index' is not defined. If the update collection isn't empty, 'last_index' has the same value as the 'index' currently tied to the last series item in the collection ๐Ÿ’…

So after V series items have been added to the update collection, the last and most recently added series item has an 'index' of 'last_index' = (V - 1) % (MAX_INDEX + 1). As long as the 'index' hasn't wrapped around yet, 'last_index' is just the total number of series items ever added to that update collection, minus 1 โœจ

When a diff query is processed with the "Cursor" extension, the 'index' values get used as cursor information, as defined in Section 9.2 ๐Ÿฆ‹

For each requester's update collection, the AS also defines a constant positive integer MAX_DIFF_BATCH, where MAX_DIFF_BATCH <= MAX_N. This value is the maximum number of diff entries that can go into a single diff query response ๐ŸŒˆ The specific value MAY depend on the particular registered device or administrator that the update collection belongs to. If the AS supports the "Cursor" extension, it MUST give registered devices and administrators their corresponding MAX_DIFF_BATCH value when they register (see Section 10) ๐Ÿซถ

6.3. Query Parameters

okay so the TRL endpoint can take some query parameters on a GET request, and bestie, the rule here is that the AS MUST silently ignore any unknown query parameters ๐Ÿซถ no drama, no error, just vibes ๐Ÿ’…

  • diff ๐Ÿฆ‹: if you include it, you're asking the AS to do a diff query of the TRL (see Section 8). Its value MUST be either:
    • the integer 0, which means the (notification) response should include as many diff entries as the AS can fit in the response ๐ŸŒธ; or
    • a positive integer strictly greater than 0, which means that's the maximum number of diff entries the (notification) response should include ๐Ÿ“

if the AS doesn't support diff queries at all, it ignores the diff parameter even when it's there and just does a full query like normal (see Section 7) ๐Ÿงš

otherwise, the AS MUST return a 4.00 (Bad Request) response if the diff value is neither 0 nor a positive integer ๐Ÿ™ˆ, no matter whether cursor is also there (more on that below). that response MUST have Content-Format set to "application/concise-problem-details+cbor", and its payload is formatted as defined in Section 6.1. inside the Custom Problem Detail entry ace-trl-error, the error-id field MUST be set to 0 ("Invalid parameter value"), and the cursor field MUST NOT be present ๐Ÿ’€

  • cursor ๐Ÿ’œ: if you include it, you're asking the AS to do a diff query of the TRL together with the "Cursor" extension, as defined in Section 9.2 โœจ its value MUST be either 0 or a positive integer. if cursor is included, then diff MUST also be included ๐Ÿ‘‘

if it's there, the cursor parameter is an unsigned integer that the AS gave you in a previous response from the TRL endpoint (see Sections 9.1, 9.2.2, and 9.2.3) ๐ŸŽ€

if the AS doesn't support the "Cursor" extension, it ignores the cursor parameter when it shows up in the GET request. in that case, the AS just carries on the way the rest of this document says, which means:

  1. it does a diff query of the TRL (see Section 8), if it supports diff queries and the diff parameter is present in the GET request; otherwise,
  2. it does a full query of the TRL (see Section 7).

now, if the AS supports both diff queries and the "Cursor" extension, and the GET request includes cursor, then the AS MUST return a 4.00 (Bad Request) response in any of the cases below ๐Ÿ”ฅ

that 4.00 (Bad Request) response MUST have Content-Format set to "application/concise-problem-details+cbor", and its payload is formatted as defined in Section 6.1 ๐ŸŒˆ

  • the GET request doesn't include diff, no matter what cursor is set to ๐Ÿ˜ญ. inside ace-trl-error, the error-id MUST be 1 ("Invalid set of parameters"), and the cursor field MUST NOT be present.
  • the cursor value is neither 0 nor a positive integer, or it's strictly greater than MAX_INDEX (see Section 6.2.1) ๐Ÿฆ‹. inside ace-trl-error, the error-id MUST be 0 ("Invalid parameter value"). the entry MUST include the cursor field, and its value is either:
    • the CBOR simple value null (0xf6), if the update collection associated with the requester is empty; or, otherwise
    • the corresponding current value of last_index.
  • ALL of these are true: the update collection for the requester isn't empty, no wraparound of the index value has happened, and the cursor value is strictly greater than the current last_index on the update collection (see Section 6.2.1) ๐Ÿ™ˆ. inside ace-trl-error, the error-id MUST be 2 ("Out of bound cursor value"), and the cursor field MUST NOT be present.

7. Full Query of the TRL

so when the requester wants a full query of the TRL, the AS does two things ๐Ÿ’–

  1. it builds a HASHES set from the TRL like this:
    • if the requester is a registered device, HASHES has the token hashes that are currently in the TRL and tied to access tokens belonging to that registered device. the AS can always use the device's authenticated identity to filter the TRL content ๐Ÿ”
    • if the requester is an administrator, HASHES has all the token hashes currently in the TRL ๐Ÿ‘‘
  2. it sends a 2.05 (Content) response to the requester. that response MUST have Content-Format set to "application/ace-trl+cbor" ๐Ÿฆ‹. the payload is a CBOR map, and it MUST be formatted like this ๐Ÿงš
    • the full_set parameter MUST be included and MUST encode a CBOR array full_set_value. each element of full_set_value is a CBOR byte string holding one of the token hashes from HASHES. if HASHES is empty, full_set is the empty CBOR array ๐ŸŒธ

the CBOR array MUST be treated as a set, which means the order of its elements has no meaning โœจ

  • the cursor parameter MUST be included if the AS supports both diff queries and the related "Cursor" extension (see Sections 6.2 and 6.2.1) ๐ŸŽ€. its value is set as specified in Section 9.1, and it gives the requester what they need to send a new request asking the AS for a follow-up diff query using the "Cursor" extension (see Section 9.2) ๐Ÿ’…

if the AS doesn't support both diff queries and the "Cursor" extension, this parameter MUST NOT be included. and if the requester doesn't support both of those, it MUST silently ignore the cursor parameter if it shows up ๐Ÿซถ

Figure 6 is the CDDL definition [RFC8610] of the CBOR array full_set_value that the AS puts in the full_set parameter of its response ๐Ÿ“œ

token_hash = bytes
full_set_value = [* token_hash]

Figure 6: CDDL Definition of 'full_set_value'

Figure 7 is an example response from the AS after a full query request to the TRL endpoint. in this example the AS does NOT support diff queries or the "Cursor" extension, so the cursor parameter isn't in the payload. the full token hashes are left out to keep it short ๐Ÿ“

Header: Content (Code=2.05)
Content-Format: 262 (application/ace-trl+cbor)
Payload:
{
   / full_set / 0: [
     h'01fa51cc...4819', / elided for brevity /
     h'01748190...223d'  / elided for brevity /
   ]
}

Figure 7: Example of a Response Following a Full Query Request to the TRL Endpoint

8. Diff Query of the TRL โœจ

okay so bestie, this is where the AS builds a (notification) response to a GET request asking for a diff query of the TRL ๐Ÿ’… there are four steps, and if the AS supports both diff queries and the related "Cursor" extension, Steps 3 and 4 get extended the way Section 9.2 spells out ๐Ÿซถ

  1. The AS defines the positive integer NUM ๐Ÿ”ข if the value N from the 'diff' query parameter in the GET request is equal to 0 or greater than the predefined positive integer MAX_N (see Section 6.2), then NUM takes the value of MAX_N. Otherwise, NUM takes N.
  2. The AS determines U = min(NUM, SIZE), where SIZE <= MAX_N. SIZE is the number of diff entries currently stored in the requester's update collection, so it's basically the stash for that one requester ๐Ÿงš
  3. The AS prepares U diff entries. If U is equal to 0 (e.g., because SIZE is equal to 0 at Step 2), then no diff entries get prepared, and that's totally fine ๐ŸŒธ

The prepared diff entries relate to the U most recent TRL updates pertaining to the requester, as kept in the update collection for that requester (see Section 6.2). The first diff entry refers to the most recent of those updates, the second refers to the second-to-last, and so on, like a reverse-chron timeline ๐Ÿฆ‹

Each diff entry is a CBOR array 'diff_entry' with two elements:

  • a. A trl_patch set of token hashes encoded as a CBOR array 'removed'. Each element is a CBOR byte string whose value is the token hash of an access token that pertains to the requester and got removed from the TRL during the update tied to that diff entry.
  • b. A trl_patch set of token hashes encoded as a CBOR array 'added'. Each element is a CBOR byte string whose value is the token hash of an access token that pertains to the requester and got added to the TRL during the update tied to that diff entry.

The CBOR arrays 'removed' and 'added' MUST be treated as sets, i.e., the order of their elements has no meaning ๐Ÿ™ˆ order doesn't matter here, babe

  1. The AS prepares a 2.05 (Content) response for the requester ๐Ÿ’Œ The response MUST have Content-Format set to "application/ace-trl+cbor". The payload is a CBOR map, and it MUST be formatted as follows:
    • The 'diff_set' parameter MUST be present and MUST encode a CBOR array 'diff_set_value' of U elements. Each element of 'diff_set_value' specifies one of the CBOR arrays 'diff_entry' prepared above as a diff entry. U might be 0, in which case 'diff_set_value' is the empty CBOR array, which is giving "nothing to see here" ๐Ÿซง

Within 'diff_set_value', the 'diff_entry' CBOR arrays MUST be sorted to reflect the corresponding updates to the TRL in reverse chronological order. So the first 'diff_entry' element of 'diff_set_value' relates to the most recent TRL update pertaining to the requester, the second one relates to the second-to-last most recent update, and so on ๐Ÿ‘‘

  • The 'cursor' parameter and the 'more' parameter MUST be included if the AS supports both diff queries and the related "Cursor" extension (see Section 6.2.1). Their values are set as Section 9.2 specifies, and they give the requester what it needs to send a new request asking the AS for a follow-up query of the TRL (see Section 9.2). ๐ŸŽ€

If the AS supports diff queries but not the "Cursor" extension, these parameters MUST NOT be included. If the requester supports diff queries but not the "Cursor" extension, the requester MUST silently ignore the 'cursor' parameter and the 'more' parameter, if present. Silently. No drama. ๐Ÿคซ

Figure 8 gives the CDDL definition [RFC8610] of the CBOR array 'diff_set_value' that the AS puts in the response, as the value of the 'diff_set' parameter ๐Ÿ“œ

token_hash = bytes
trl_patch = [* token_hash]
diff_entry = [removed: trl_patch, added: trl_patch]
diff_set_value = [* diff_entry]

Figure 8: CDDL Definition of 'diff_set_value'

Figure 9 is an example of a response from the AS after a diff query request to the TRL endpoint, where U = 3 diff entries are included. In this example the AS doesn't support the "Cursor" extension, so the 'cursor' and 'more' parameters are not in the payload. Full token hashes are omitted for brevity, so we're keeping it short and sweet โœจ

Header: Content (Code=2.05)
Content-Format: 262 (application/ace-trl+cbor)
Payload:
{
   / diff_set / 1: [
     [
       [ h'01fa51cc...0f6a', / elided for brevity /
         h'01748190...8bce'  / elided for brevity /
       ],
       [ h'01cdf1ca...563d', / elided for brevity /
         h'01be41a6...a057'  / elided for brevity /
       ]
     ],
     [
       [ h'0144dd12...77bc', / elided for brevity /
         h'01231fff...a2ce'  / elided for brevity /
       ],
       []
     ],
     [
       [],
       [ h'01ca986f...ffc1', / elided for brevity /
         h'01fe1a2b...def0'  / elided for brevity /
       ]
     ]
   ]
}

Figure 9: Example of Response Following a Diff Query Request to the TRL Endpoint

Appendix A explains how doing a diff query of the TRL is actually a usage example of the Series Transfer Pattern defined in [STP] ๐Ÿฆ‹ it's giving pattern-reuse energy

9. Response Messages when Using the "Cursor" Extension

okay this is the part where the "Cursor" extension comes in ๐ŸŒˆ if the AS supports both diff queries and the "Cursor" extension, it composes a response to a full query request or a diff query request as defined in Sections 9.1 and 9.2, respectively.

The exact format of the response depends on:

  • the request being a full query or a diff query request,
  • the presence of the 'diff' and 'cursor' query parameters and their values in the diff query request, and
  • the current status of the update collection associated with the requester.

Error handling and the possible resulting error responses are as defined in Section 6.3. โš ๏ธ check Section 6.3 if things go sideways

9.1. Response to Full Query

When processing a full query request to the TRL endpoint, the AS composes a response as defined in Section 7. Easy peasy ๐Ÿ“

In particular, the 'cursor' parameter in the CBOR map carried in the response payload specifies either the CBOR simple value null (0xf6) or a CBOR unsigned integer.

The 'cursor' parameter MUST encode the CBOR simple value null (0xf6) in case there are currently no TRL updates pertaining to the requester, i.e., the update collection for that requester is empty. That's the situation from the moment the requester registers at the AS until the first update pertaining to that requester hits the TRL ๐ŸŒ™

Otherwise, the 'cursor' parameter MUST encode a CBOR unsigned integer. The unsigned integer MUST take the 'index' value of the last series item in the update collection associated with the requester (see Section 6.2.1), which corresponds to the most recent TRL update pertaining to the requester. Honestly, that value is just the current value of 'last_index' for that requester's update collection ๐Ÿ’œ

9.2. Response to Diff Query

When processing a diff query request to the TRL endpoint, the AS composes a response as defined in the following subsections. Three possible plot twists, let's go ๐ŸŽฌ

9.2.1. Empty Update Collection

If the update collection associated with the requester has no elements, the AS returns a 2.05 (Content) response. The response MUST have Content-Format set to "application/ace-trl+cbor", and its payload MUST be a CBOR map formatted as follows:

  • The 'diff_set' parameter MUST be included and MUST encode the empty CBOR array.
  • The 'cursor' parameter MUST be included and MUST encode the CBOR simple value null (0xf6).
  • The 'more' parameter MUST be included and MUST encode the CBOR simple value false (0xf4).

Note that the above applies when the update collection associated with the requester has no elements, regardless of whether or not the 'cursor' query parameter is included in the diff query request, and irrespective of the specified unsigned integer value if present. So even if the requester sends a cursor, empty is empty ๐Ÿซ 

9.2.2. Cursor Not Included in the Diff Query Request

If the update collection associated with the requester is not empty and the diff query request does not include the 'cursor' query parameter, the AS performs the actions defined in Section 8, with the following differences:

  • At Step 3, the AS considers the value MAX_DIFF_BATCH (see Section 6.2.1) and prepares L = min(U, MAX_DIFF_BATCH) diff entries.

If U <= MAX_DIFF_BATCH, the prepared diff entries are the last series items in the update collection associated with the requester, corresponding to the L most recent TRL updates pertaining to the requester.

If U > MAX_DIFF_BATCH, the prepared diff entries are the eldest of the last U series items in the update collection associated with the requester, as corresponding to the first L of the U most recent TRL updates pertaining to the requester. Batching vibes, we're taking it one chunk at a time ๐Ÿ“ฆ

  • At Step 4, the CBOR map to carry in the payload of the 2.05 (Content) response MUST be formatted as follows:
    • The 'diff_set' parameter MUST be present and MUST encode a CBOR array 'diff_set_value' of L elements. Each element of 'diff_set_value' specifies one of the CBOR arrays 'diff_entry' prepared as a diff entry.
    • The 'cursor' parameter MUST be present and MUST encode a CBOR unsigned integer. The unsigned integer MUST take the 'index' value of the series item of the update collection included as first diff entry in the 'diff_set_value' CBOR array, which is specified by the 'diff_set' parameter. In other words, the 'cursor' parameter takes the 'index' value of the series item in the update collection corresponding to the most recent TRL update pertaining to the requester and returned in this diff query response.

Note that the 'cursor' parameter takes the same 'index' value of the last series item in the update collection when U <= MAX_DIFF_BATCH. ๐Ÿ”

  • The 'more' parameter MUST be present. The parameter MUST encode the CBOR simple value false (0xf4) if U <= MAX_DIFF_BATCH; otherwise, it MUST encode the CBOR simple value true (0xf5). more = true means there's more tea to spill โ˜•

If the 'more' parameter in the payload of the received 2.05 (Content) response has a value of true, the requester can send a follow-up diff query request including the 'cursor' query parameter with the same value of the 'cursor' parameter specified in this diff query response. As defined in Section 9.2.3, this results in the AS transferring the following subset of series items as diff entries, picking up right where it left off in the previous transfer ๐Ÿฆ‹๐Ÿซถ

9.2.3. Cursor Included in the Diff Query Request ๐ŸŽ€

okay so this is the part where the requester actually sends a cursor with their diff query ๐Ÿ’… The AS checks whether the requester's update collection is empty, and if it isn't and the request has cursor with value P, the AS takes one of two paths depending on which case holds ๐Ÿฆ‹

Case A: Neither series item X (with index = P) nor series item Y (with index = (P + 1) % (MAX_INDEX + 1)) can be found in the requester's update collection. This happens when Y (and maybe some items after it) was removed from that requester's update collection earlier (see Step 5 at Section 6.2). ๐Ÿ˜ญ

In this case, the AS sends back a 2.05 (Content) response. The Content-Format MUST be application/ace-trl+cbor, and the payload MUST be a CBOR map that looks like this:

  • The diff_set parameter MUST be included and MUST encode the empty CBOR array.
  • The cursor parameter MUST be included and MUST encode the CBOR simple value null (0xf6).
  • The more parameter MUST be included and MUST encode the CBOR simple value true (0xf5).

The combo (cursor, more) = (null, true) is the AS saying "the update collection is actually not empty, but some series items got lost because they were removed." That includes the item with index value (P + 1) % (MAX_INDEX + 1), which is the first one the requester wanted after the reference point P ๐Ÿฅบ

When the requester gets this diff query response, the requester SHOULD send a new full query request to the AS. A successful response gives the requester the full current relevant subset of the TRL, plus a valid cursor value (see Section 9.1) that can be used in a later diff query request ๐ŸŒธ

Case B: Either series item X (with index = P) is found in the requester's update collection, or X is not found but series item Y (with index = (P + 1) % (MAX_INDEX + 1)) is. ๐Ÿ™Œ

In this case the AS does everything from Section 8, with a few differences:

  • At Step 3, the AS considers the value MAX_DIFF_BATCH (see Section 6.2.1) and prepares L = min(SUB_U, MAX_DIFF_BATCH) diff entries, where SUB_U = min(NUM, SUB_SIZE). SUB_SIZE is the number of series items in the update collection, starting from and including the series item added right after X. If L is equal to 0 (for example, because SUB_U is equal to 0), then no diff entries get prepared.

If SUB_U <= MAX_DIFF_BATCH, the prepared diff entries are the last series items in the requester's update collection, which correspond to the L most recent TRL updates for that requester. โœจ

If SUB_U > MAX_DIFF_BATCH, the prepared diff entries are the eldest of the last SUB_U series items in the requester's update collection, which correspond to the first L of the SUB_U most recent TRL updates for that requester. ๐Ÿงš

  • At Step 4, the CBOR map in the payload of the 2.05 (Content) response MUST be formatted as follows:
    • The diff_set parameter MUST be present and MUST encode a CBOR array diff_set_value with L elements. Each element of diff_set_value is one of the CBOR arrays diff_entry that was prepared as a diff entry. Note that L might be 0, in which case diff_set_value is the empty CBOR array.
    • The cursor parameter MUST be present and MUST encode a CBOR unsigned integer. Specifically:
      • If L is equal to 0, i.e., series item X is the last one in the update collection, then the cursor parameter MUST take the same index value as the last series item in the update collection. In fact, that value is the current value of last_index for the update collection.
      • If L is different from 0, then the cursor parameter MUST take the index value of the series element in the update collection that was included as the first diff entry in the diff_set CBOR array. In other words, cursor takes the index value of the series item corresponding to the most recent TRL update for that requester that was returned in this diff query response.

Note that the cursor parameter takes the same index value as the last series item in the update collection when SUB_U <= MAX_DIFF_BATCH.

  • The more parameter MUST be present. It MUST encode the CBOR simple value false (0xf4) if SUB_U <= MAX_DIFF_BATCH; otherwise, it MUST encode the CBOR simple value true (0xf5).

If the more parameter in the payload of the received 2.05 (Content) response is true, the requester can send a follow-up diff query request that includes the cursor query parameter with the same cursor value from this diff query response. The AS then transfers the next subset of series items as diff entries, picking up right where the previous transfer stopped ๐Ÿฆ‹๐Ÿ’œ


10. Registration at the Authorization Server โœจ

During the registration process at the AS, an administrator or a registered device gets the following information as part of the registration response:

  • The url-path to the TRL endpoint at the AS.
  • The hash function used to compute token hashes. This is specified by pointing to an entry in the "Named Information Hash Algorithm Registry" [IANA.Hash.Algorithms]. The specific way to do that is outside the scope of this document.
  • A positive integer MAX_N, if the AS supports diff queries of the TRL (see Sections 6.2 and 8).
  • A positive integer MAX_DIFF_BATCH, if the AS supports diff queries of the TRL as well as the related "Cursor" extension (see Sections 6.2.1 and 9).

Once registration is complete, the AS keeps the registration and related information until a possible deregistration happens, so it can keep track of active administrators and registered devices. How it does that is implementation specific. Either way, this kind of registration tracking is enforced at the AS so that requests sent by clients to the /token endpoint (see Section 5.8 of [RFC9200]) and by RSs to the /introspect endpoint (see Section 5.9 of [RFC9200]) get processed as intended ๐ŸŒˆ

When registered devices and the AS talk to each other, they have to use a secure communication association and be mutually authenticated (see Section 5 of [RFC9200]). ๐Ÿ”

In the same spirit, communications between the AS and an administrator MUST be ensured to be mutually authenticated, encrypted, and integrity protected as well as protected against message replay. ๐Ÿ’–

Before starting its registration process at the AS, an administrator has to set up such a secure communication association with the AS, if they don't already share one. Mutual authentication is REQUIRED during the establishment of the secure association ๐Ÿซถ For this, the administrator and the AS can rely, for example, on a TLS or DTLS secure session with mutual authentication (see [RFC8446] and [RFC9147]), or on an Object Security for Constrained RESTful Environments (OSCORE) Security Context [RFC8613] set up by running the authenticated key exchange protocol EDHOC [RFC9528].

When the AS gets authenticated requests from the administrator to access the TRL endpoint, it can always check whether the requester is authorized to take on that role, i.e., to access the content of the whole TRL. ๐Ÿ‘‘

To do this, the AS may rely on a local access control list or something similar, which lists the authentication credentials of trusted, authorized administrators. In particular, the AS verifies the requester to the TRL endpoint as an authorized administrator only if the access control list includes the same authentication credential that the requester used when setting up the mutually authenticated secure communication association with the AS. ๐Ÿ“

Further details about the registration process at the AS are out of scope for this specification. Note that the registration process is also out of scope for the ACE framework (see Section 5.5 of [RFC9200]). ๐Ÿงš

11. Notification of Revoked Access Tokens โœจ

Okay so bestie, once the administrator or a registered device is registered at the AS, they can send a GET request to the TRL endpoint at the AS ๐Ÿ’Œ. That request can ask for a full query (see Section 7) or a diff query (see Section 8) of the TRL. It can also include the CoAP Observe Option set to 0 (register) ๐Ÿ‘€ to start an observation of the TRL endpoint, as per Section 3.1 of [RFC7641]. It's giving subscription energy ๐Ÿซถ

If the request gets processed successfully, the AS replies with a 2.05 (Content) response ๐ŸŽ€. Now here's the tea ๐Ÿต: if the AS supports diff queries but NOT the "Cursor" extension (see Sections 6.2 and 6.2.1), then the payload is formatted as defined in Sections 7 or 8, depending on whether the GET request yielded a full query or a diff query of the TRL. But if the AS supports both diff queries AND the related "Cursor" extension, then the payload is formatted as defined in Section 9 ๐Ÿ’…

Now, if a requester doesn't get a response from the TRL endpoint, or gets an error response, the requester does not make any assumptions or draw any conclusions about the revocation or expiration of its access tokens ๐Ÿ™ˆ No guessing, no vibes-based decisions! The requester MAY try again by sending a new request to the TRL endpoint ๐Ÿ”

When the TRL gets updated (see Section 5.1), the AS sends Observe notifications to the observers whose subset of the TRL has changed ๐Ÿ””. Those notifications are sent as per Section 4.2 of [RFC7641]. If the AS supports it, an observer can configure how the AS sends those notifications โš™๏ธ. One possible way is the conditional control parameter "c.pmax" defined in [COND-PARAMETERS], which can be included as a "name=value" query parameter in an Observation Request. This makes sure that no more than c.pmax seconds elapse between two consecutive notifications sent to that observer, regardless of whether or not the TRL has changed ๐Ÿ•ฐ๏ธ

After a first exchange with the AS, an administrator or a registered device can keep sending GET requests to the TRL endpoint at any time, same as above ๐Ÿงš They can ask for a full query (see Section 7) or a diff query (see Section 8) of the TRL. For diff queries, the requester can additionally rely on the "Cursor" extension (see Sections 6.3 and 9.2) ๐Ÿฆ‹

As specified in Section 6.2, an AS supporting diff queries keeps an update collection of maximum MAX_N series items for each administrator or registered device, called a "requester" ๐Ÿ“š. If an update collection already holds MAX_N series items, adding a new one deletes the oldest one ๐Ÿ—‘๏ธ

From then on, the requester tied to that update collection won't be able to retrieve the deleted series item in a new diff query request to the TRL endpoint ๐Ÿ˜ฌ. If that deleted item showed the revocation of an access token belonging to the requester, the requester won't find out about it when it gets the diff query response from the AS ๐Ÿ’€

Sending a diff query request as an Observation Request, and therefore relying on Observe notifications, greatly reduces the chances of a requester missing updates to its update collection ๐Ÿ™Œ. But this depends on the requester successfully receiving the Observe notification responses from the TRL (see also Section 14.3) ๐Ÿ“ฌ

To limit how long a requester is unaware of its revoked-but-not-yet-expired access tokens, a requester SHOULD NOT rely solely on diff query requests ๐Ÿšซ. Instead, it SHOULD also regularly send a full query request to the TRL endpoint, following its related application policy ๐Ÿ“…

11.1. Handling of Revoked Access Tokens and Token Hashes ๐Ÿ”

When a registered device gets a response from the TRL endpoint, it MUST expunge every stored access token associated with a token hash specified in that response ๐Ÿงน. If the registered device is an RS, it MUST NOT delete the stored token hash after expunging the associated access token ๐Ÿซ 

If an RS uses the method defined in this document with the AS that issued an access token, then the RS MUST NOT accept and store that access token if any of the following holds ๐Ÿšจ

  • The token hash corresponding to the access token is among the currently stored ones.
  • The access token is a CWT and any of the following holds:
    • The access token includes a non-empty "unprotected" field, i.e., the field's value is not encoded as the empty CBOR map (0xa0). This applies to the top-level "unprotected" field of the COSE object used for the CWT, the "unprotected" field of each element of the "signatures" array, and the "unprotected" field of each element of any "recipients" array.
    • The received CBOR data item that embodies the access token does not comply with what is defined in Section 3. This concerns:

o the use of exactly two nested CBOR tags, where the outer tag is the CWT CBOR tag and the inner tag is one of the COSE CBOR tags;

o the tag numbers encoded with the minimum possible length; and

o the access token being the innermost tag content of the received CBOR data item.

  • In the received CBOR data item that embodies the access token, the inner tag has a tag number that is not consistent with the actual COSE data item to process. For instance, the inner tag number is 16 (COSE_Encrypt0) but the CWT is actually a COSE_Sign data item.
  • The access token relies on a JSON object for encoding its claims, but it is not a JWT [RFC7519] and any of the following holds:
    • The access token uses the JWS JSON Serialization from [RFC7515] and includes the JWS Unprotected Header.
    • The access token uses the JWE JSON Serialization from [RFC7516] and includes the JWE Shared Unprotected Header and/or includes the "header" member in any of the elements of the "recipients" array.

An RS MUST store the token hash th1 corresponding to an access token t1 until both of the following conditions hold ๐Ÿ—‚๏ธ

  • The RS has received and seen t1, whether or not it accepted and stored it.
  • The RS has gained knowledge that t1 has expired. This can be achieved, e.g., through the following means:
    • A response from the TRL endpoint indicating that t1 has expired after its earlier revocation, i.e., the token hash th1 has been removed from the TRL. This can be indicated, for instance, in a response from the TRL endpoint following a diff query of the TRL (see Section 8).
    • The value of the 'exp' claim specified in t1 indicates that t1 has expired.
    • The locally determined expiration time for t1 has passed, based on the time at the RS when t1 was first accepted and on the value of its 'exi' claim.
    • The result of token introspection performed on t1 (see Section 5.9 of [RFC9200]), if supported by both the RS and the AS.

The RS MUST NOT delete the stored token hashes whose corresponding access tokens don't fulfill both of the two conditions above, unless it becomes necessary due to memory limitations ๐Ÿง . In that case, the RS MUST delete the earliest stored token hashes first โณ

Keeping the stored token hashes as specified above limits the impact from a (dishonest) client whose access token ๐Ÿ˜ˆ:

  1. includes the 'exi' claim,
  2. is uploaded at the RS for the first time after it has been revoked and later expired, and
  3. has the sequence number encoded in the 'cti' claim (for CWTs) or in the 'jti' claim (for JWTs) greater than the highest sequence number among the expired access tokens including the 'exi' claim for the RS (see Section 5.10.3 of [RFC9200]).

So the RS won't accept that revoked and expired access token for as long as it stores the corresponding token hash ๐Ÿ›ก๏ธ

This risk can be reduced even further ๐Ÿ’ซ. If token introspection is implemented by both the RS and the AS, the RS can introspect the access token (see Section 5.9 of [RFC9200]) when it receives an access token that includes the 'exi' claim and has no corresponding stored token hash.

When a token hash th2 is stored and, because of it, an access token t2 that includes the 'exi' claim gets expunged or isn't accepted on upload, the RS retrieves the sequence number sn2 encoded in the 'cti' claim (for CWTs) or in the 'jti' claim (for JWTs) (see Section 5.10.3 of [RFC9200]). Then the RS stores sn2 as associated with th2. If expunging or not accepting t2 leads to th2 being deleted, the RS MUST associate sn2 with th2 before continuing with the deletion of th2 ๐Ÿ“

When deleting any token hash, the RS checks whether that token hash is associated with a sequence number sn_th. If it is, the RS checks whether sn_th is greater than the highest sequence number sn* among the expired access tokens including the 'exi' claim for the RS. If so, sn* MUST take the value of sn_th ๐Ÿ”ข

Thanks to what Section 5.10.3 of [RFC9200] defines, this ensures that after a token hash is deleted, the RS will not accept the access token at that point in time or in the future ๐Ÿ™…โ€โ™€๏ธ

12. ACE Token Revocation List Parameters ๐ŸŽ€

This specification defines a bunch of parameters that can be carried in the response from the TRL endpoint, when the response payload is a CBOR map ๐Ÿ—บ๏ธ. Note that such a response MUST use the Content-Format "application/ace-trl+cbor" defined in Section 15.2 of this specification ๐Ÿ“ฆ

The table below sums up the parameters. For each one, it gives the value to use as CBOR key, i.e., the abbreviation used in the key of the map pair for the parameter, instead of the parameter's name as a text string ๐Ÿ”ค

         +==========+==========+==========================+
         | Name     | CBOR Key | CBOR Type                |
         +==========+==========+==========================+
         | full_set | 0        | array                    |
         +----------+----------+--------------------------+
         | diff_set | 1        | array                    |
         +----------+----------+--------------------------+
         | cursor   | 2        | Null or unsigned integer |
         +----------+----------+--------------------------+
         | more     | 3        | True or False            |
         +----------+----------+--------------------------+

           Table 1: CBOR Abbreviations for the ACE Token
                     Revocation List Parameters

13. ACE Token Revocation List Error Identifiers โœจ

okay so this section is the cheat sheet for error IDs ๐Ÿ“ the AS can use when something goes sideways with the TRL. they show up in error responses with Content-Format "application/concise-problem-details+cbor", as values of the 'error-id' field inside the Custom Problem Detail entry 'ace-trl-error' (see Section 6.1). there are three of them, and they're all right here:

                   +=======+===========================+
                   | Value | Description               |
                   +=======+===========================+
                   | 0     | Invalid parameter value   |
                   +-------+---------------------------+
                   | 1     | Invalid set of parameters |
                   +-------+---------------------------+
                   | 2     | Out of bound cursor value |
                   +-------+---------------------------+

                       Table 2: ACE Token Revocation
                           List Error Identifiers

14. Security Considerations ๐Ÿ”

bestie, this part is serious, so let's go ๐Ÿ’…. the protocol in this document inherits the security considerations from the ACE framework [RFC9200], plus the ones about using CWTs from [RFC8392], using JWTs from [RFC7519] and [RFC8725], using CoAP Observe from [RFC7641], and computing token hashes from [RFC6920]. there are also a few extra considerations that apply on top of those ๐Ÿ‘€

14.1. Content Retrieval from the TRL

the AS MUST make sure each registered device can only access and retrieve its own slice of the TRL ๐Ÿซถ the AS can always do that filtering based on the authenticated identity of the registered device, which is a (non-public) identifier the AS can securely link to the device and the secure association they use to talk to each other.

the AS MUST also make sure that, apart from registered devices looking at their own slice, only authorized and authenticated administrators can access the full TRL (see Section 10).

heads up: the TRL endpoint only supports the GET method (see Section 6) ๐Ÿ™ˆ. so, as covered in Sections 7 and 8, access is only through protected and authenticated GET requests, which are safe in the REST sense by definition and don't change what's in the TRL. registered devices and administrators can only do read-only operations on the TRL endpoint, full stop.

the content of the TRL can only be updated internally by the AS, in the two situations described in Section 5.1. so an adversary that doesn't control the AS can't mess with the TRL. for example, they can't remove a token hash to sneakily let a client into protected resources even though the access token was revoked, and they can't add a token hash to sneakily block a client even though its access token is still valid ๐Ÿšซ

14.2. Size of the TRL

if a lot of non-expired access tokens tied to one registered device get revoked, its slice of the TRL could grow bigger than the device can handle when a response comes back from the TRL endpoint. this is especially true if it relies on a full query of the TRL (see Section 7).

attackers could totally exploit this to mess with how a registered device behaves ๐Ÿ˜ค. so, to help keep the TRL small, the AS SHOULD refrain from issuing access tokens with an excessively long expiration time ๐Ÿ’–

14.3. Communication Patterns

the communication about revoked access tokens in this specification is expected to lean heavily on CoAP Observe notifications sent from the AS to a requester (that's an administrator or a registered device). if an external attacker with network access suppresses those notifications, requesters would never find out their access tokens were revoked ๐Ÿ˜ฑ

to avoid that, a requester SHOULD NOT rely only on CoAP Observe notifications. it SHOULD also regularly poll the AS for the latest info about revoked access tokens by sending GET requests to the TRL endpoint. the specific strategies and schedules for polling are for a related application policy to define, taking into account the operational and availability patterns the requester follows (for example, saving energy and other optimizations) ๐Ÿ”‹

14.4. Request of New Access Tokens

if a client is storing an access token it still thinks is valid, and it tries to access a protected resource at the RS, the client may get an unprotected 4.01 (Unauthorized) response from the RS ๐Ÿ™ƒ

there are a few possible reasons, for example:

  • the access token got revoked, the RS found out about it, and the RS expunged the access token, but the client doesn't know that (yet).
  • the access token is still valid, but an on-path active adversary might have injected a forged 4.01 (Unauthorized) response, or the RS might have deleted the access token from its local storage because its dedicated storage space was all used up.

either way, if the client thinks the access token is still valid, it SHOULD NOT immediately ask the AS for a new access token just because it got a 4.01 (Unauthorized) response from the RS. instead, the client SHOULD send a request to the TRL endpoint at the AS. if the client finds out the access token is no longer valid, it expunges the access token and can ask for a new one. otherwise, it can try uploading the same access token to the RS again or request a new one ๐Ÿ”„

14.5. Vulnerable Time Window at the RS

a client might try to access a protected resource at an RS after the access token allowing that access got revoked, but before the RS knows about the revocation ๐Ÿ˜ฌ

in that case, if the RS is still storing the access token, the client will be able to access the protected resource even though it shouldn't. that's a security violation, even if the client isn't trying to be malicious ๐Ÿšจ

to minimize this risk, if an RS relies only on polling through individual requests to the TRL endpoint to learn about revoked access tokens, the RS SHOULD implement an adequate trade-off between how often it polls and the maximum length of the vulnerable time window โณ

14.6. Preventing Unnoticed Manipulation of Access Tokens

as defined in Section 3, issued access tokens MUST NOT rely on unprotected headers to specify information as header parameters. also, when issued access tokens are CWTs, they MUST be tagged using the COSE CBOR tag that matches the COSE object being used. then the result MUST be tagged using the CWT CBOR tag, and no further tagging happens ๐Ÿท๏ธ

this makes sure the RS always computes the correct token hash for an access token, meaning the same token hash the AS and C computed for that access token. the rules in Section 3 stop an active adversary from pulling off an attack against the RS that would otherwise be possible if the access token got uploaded to the RS over an unprotected channel ๐Ÿ›ก๏ธ

here's the attack, bestie ๐Ÿ‘€. the adversary intercepts the access token on its way to the RS. then they tweak the access token so the RS won't notice, but it still passes the cryptographic validation at the RS. they have two options:

  • adding and/or removing fields in the unprotected header(s) of the access token, as long as those fields don't play a role in the cryptographic validation of the access token.
  • specifically when the access token is a CWT, adding, removing, or changing possible CBOR tags that enclose the access token.

after that, the adversary sends the tweaked access token to the RS.

once the RS has validated the tweaked access token successfully, it computes a corresponding token hash that's different from the one C and the AS computed and stored. the RS then stores the tweaked access token along with the wrong token hash.

later on, if the access token gets revoked and the AS gives the RS the correct token hash, the RS doesn't recognize that token hash among the ones it stored. so the RS does not delete the revoked access token ๐Ÿ˜ญ

14.7. Two Token Hashes at the RS Using JWTs

Section 4.3.2 says an RS using JWTs as access tokens has to compute and store two token hashes for the same access token. that's because the RS doesn't know for sure whether the AS gave the access token to the client in an AS-to-Client response encoded in CBOR or in JSON ๐Ÿค”

and a dishonest client could take advantage of that. the client can first receive the JWT in an AS-to-Client response encoded in CBOR (or JSON). then it uploads the JWT to the RS in a way that makes the RS think the client actually got it in an AS-to-Client response encoded in JSON (or CBOR).

so the RS ends up considering a HASH_INPUT that's different from the one the AS and the client used (see Section 4.2). therefore, the RS computes a token hash h' that's different from the token hash h computed by the AS and the client. if the AS revokes the access token and advertises the right token hash h, the RS won't learn about the revocation, and so it won't delete the access token ๐Ÿ˜ฑ

basically, this happens because the HASH_INPUT used to compute the token hash of a JWT depends on whether the AS-to-Client response is encoded in CBOR or in JSON. that makes the RS vulnerable to the attack above when JWTs are used as access tokens. this isn't a problem for CWTs, though, because the HASH_INPUT used for a CWT's token hash does not depend on whether the AS-to-Client response is encoded in CBOR or JSON โœจ

this asymmetry can't be avoided completely. still, the method defined for the AS and the client in Section 4.2 deliberately penalizes the case where the RS uses JWTs as access tokens. in that case, the RS neutralizes the attack by computing and storing two token hashes for the same access token (see Section 4.3.2) ๐Ÿ› ๏ธ

on the flip side, this design deliberately favors the case where the RS uses CWTs as access tokens. that's a preferable option for resource-constrained RSs, and it's also the default case in the ACE framework (see Section 3 of [RFC9200]). so if an RS uses CWTs as access tokens, it isn't exposed to the attack above, and it safely computes and stores only one token hash per access token (see Section 4.3.1) ๐Ÿฆ‹

14.8. Additional Security Measures ๐Ÿ”ฅ

Okay so, registered devices and admins can check the TRL at the AS and see that their access tokens got revoked ๐Ÿ‘€. BUT they can't see why ๐Ÿ™ˆ. Not the reason, not even if it's because the device got compromised, is misbehaving, or was decommissioned. ngl that's a big gap.

And honestly? Even the AS might not know that a registered device has been specifically compromised, misbehaving, or decommissioned ๐Ÿ˜ญ. Also, it might not be acceptable to just revoke the access tokens tied to that one device. So we need more layers ๐Ÿ’….

To keep the system and application secure ๐Ÿ”’, the entity that authoritatively declares a registered device to be compromised, misbehaving, or decommissioned should also promptly trigger additional revocation processes as deemed appropriate. Some examples:

  • The deregistration of the registered device from the AS so that the AS does not issue further access tokens pertaining to that device.
  • If applicable, the revocation of the public authentication credential associated with the registered device (e.g., its public key certificate).

How those processes get triggered and carried out is out of scope for this document, bestie ๐Ÿซถ.

15. IANA Considerations ๐ŸŒธ

The IANA actions for this document are described in the following subsections. Let's go ๐Ÿฆ‹.

15.1. Media Type Registrations ๐ŸŽ€

IANA has registered the media type application/ace-trl+cbor for messages of the protocol defined in this document encoded in CBOR. This registration follows the procedures specified in [RFC6838]. Here's the full template:

Type name:  application

Subtype name:  ace-trl+cbor

Required parameters:  N/A

Optional parameters:  N/A

Encoding considerations:  Must be encoded as a CBOR map containing
   the protocol parameters defined in RFC 9770.

Security considerations:  See Section 14 of RFC 9770.

Interoperability considerations:  N/A

Published specification:  RFC 9770

Applications that use this media type:  The type is used by
   authorization servers, clients, and RSs that support the
   notification of revoked access tokens according to a Token
   Revocation List maintained by the authorization server as
   specified in RFC 9770.

Fragment identifier considerations:  N/A

Additional information:  N/A

Person & email address to contact for further information:  ACE WG
   mailing list (ace@ietf.org) or IETF Applications and Real-Time
   Area (art@ietf.org)

Intended usage:  COMMON

Restrictions on usage:  None

Author/Change controller:  IETF

15.2. CoAP Content-Formats Registry ๐Ÿ“ฆ

IANA has registered the following entry to the "CoAP Content-Formats" registry within the "Constrained RESTful Environments (CoRE) Parameters" registry group. Slay โœจ.

Content Type:  application/ace-trl+cbor
Content Coding:  -
ID:  262
Reference:  RFC 9770

15.3. Custom Problem Detail Keys Registry ๐Ÿงš

IANA has registered the following entry in the "Custom Problem Detail Keys" registry within the "Constrained RESTful Environments (CoRE) Parameters" registry group.

Key Value:  1
Name:  ace-trl-error
Brief Description:  Carry RFC 9770 problem details in a Concise
   Problem Details data item.
Change Controller:  IETF
Reference:  Section 6.1 of RFC 9770

15.4. ACE Token Revocation List Parameters Registry ๐Ÿ“

IANA has established the "ACE Token Revocation List Parameters" registry within the "Authentication and Authorization for Constrained Environments (ACE)" registry group. It's giving organized ๐Ÿ“‹.

One of the following registration policies is used: "Standards Action With Expert Review", "Specification Required" per Section 4.6 of [RFC8126], or "Expert Review" per Section 4.5 of [RFC8126]. Expert Review guidelines are provided in Section 15.6.

All assignments according to "Standards Action With Expert Review" are made on a "Standards Action" basis per Section 4.9 of [RFC8126] with Expert Review additionally required per Section 4.5 of [RFC8126]. The procedure for early IANA allocation of Standards Track code points defined in [RFC7120] also applies. When such a procedure is used, IANA will ask the designated expert(s) to approve the early allocation before registration. In addition, WG chairs are encouraged to consult the expert(s) early during the process outlined in Section 3.1 of [RFC7120].

The columns of this registry are as follows:

  • Name: This field contains a descriptive name that enables easier reference to the item. The name MUST be unique, and it is not used in the encoding.
  • CBOR Key: This field contains the value used as CBOR map key of the item. The value MUST be unique. The value is an unsigned integer or a negative integer. Different ranges of values use different registration policies [RFC8126]. Integer values from -256 to 255 are designated as "Standards Action With Expert Review". Integer values from -65536 to -257 and from 256 to 65535 are designated as "Specification Required". Integer values greater than 65535 are designated as "Expert Review". Integer values less than -65536 are marked as "Private Use".
  • CBOR Type: This field contains the allowable CBOR data types for values of this item or a pointer to the registry that defines its type, when that depends on another item.
  • Reference: This field contains a pointer to the public specification for the item.

This registry has been initially populated by the values in Section 12. The "Reference" column for all of these entries refers to this document.

15.5. ACE Token Revocation List Errors ๐Ÿšจ

IANA has established the "ACE Token Revocation List Errors" registry within the "Authentication and Authorization for Constrained Environments (ACE)" registry group. Okay, the errors get their own little home too ๐Ÿ ๐Ÿ’œ.

One of the following registration policies is used: "Standards Action With Expert Review", "Specification Required" per Section 4.6 of [RFC8126], or "Expert Review" per Section 4.5 of [RFC8126]. Expert Review guidelines are provided in Section 15.6.

All assignments according to "Standards Action With Expert Review" are made on a "Standards Action" basis per Section 4.9 of [RFC8126] with Expert Review additionally required per Section 4.5 of [RFC8126]. The procedure for early IANA allocation of Standards Track code points defined in [RFC7120] also applies. When such a procedure is used, IANA will ask the designated expert(s) to approve the early allocation before registration. In addition, WG chairs are encouraged to consult the expert(s) early during the process outlined in Section 3.1 of [RFC7120].

The columns of this registry are as follows:

  • Value: The field contains the value to be used to identify the error. The value MUST be unique. The value is an unsigned integer or a negative integer. Different ranges of values use different registration policies [RFC8126]. Integer values from -256 to 255 are designated as "Standards Action With Expert Review". Integer values from -65536 to -257 and from 256 to 65535 are designated as "Specification Required". Integer values greater than 65535 are designated as "Expert Review". Integer values less than -65536 are marked as "Private Use".
  • Description: This field contains a brief description of the error.
  • Reference: This field contains a pointer to the public specification defining the error, if one exists.

This registry has been initially populated by the values in Section 13. The "Reference" column for all of these entries refers to this document.

15.6. Expert Review Instructions ๐Ÿ”

The IANA registries established by this document use "Standards Action With Expert Review", "Specification Required", or "Expert Review" registration procedures depending on the range of values for which an assignment is requested. This section gives some general guidelines for what the experts should be looking for, but they are being designated as experts for a reason, so they should be given substantial latitude. Trust the experts, they know what they're doing ๐Ÿง โœจ.

Expert reviewers should take into consideration the following points:

  • Point squatting should be discouraged. Reviewers are encouraged to get sufficient information for registration requests to ensure that the usage is not going to duplicate one that is already registered and that the point is likely to be used in deployments. The zones tagged as Private Use are intended for testing purposes and closed environments. Code points in other ranges should not be assigned for testing.
  • Specifications are required for the "Standards Action With Expert Review" range of point assignment. Specifications should exist for "Specification Required" ranges, but early assignment before a specification is available is considered to be permissible. For the "Expert Review" range of point assignment, specifications are recommended and are needed if they are expected to be used outside of closed environments in an interoperable way. When specifications are not provided, the description provided needs to have sufficient information to identify what the point is being used for.
  • Experts should take into account the expected usage of fields when approving point assignment. The fact that there is a range for Standards Track documents does not mean that a Standards Track document cannot have points assigned outside of that range. The length of the encoded value should be weighed against how many code points of that length are left, the size of device it will be used on, and the number of code points left that encode to that size.

16. References ๐Ÿ“š

16.1. Normative References

Okay bestie, these are the docs this spec leans on the hardest ๐Ÿซถ Every one of them is a must-have read for the full picture ๐Ÿ’…

16.2. Informative References

These ones are more like extra reading for the curious โœจ Nothing here is needed to get the core ideas, but they're a lot of fun ๐ŸŒธ

Appendix A. On Using the Series Transfer Pattern

Okay so this appendix is the big "aha" moment ๐Ÿฆ‹ Doing a diff query of the TRL (the way Section 8 describes) is, in fact, a usage example of the Series Transfer Pattern (STP) defined in [STP]. Literally the same idea, just dressed up for the TRL ๐Ÿ’…

That is, a diff query lets a series of diff entries get transferred, with the AS providing U <= MAX_N diff entries that relate to the U most recent TRL updates pertaining to a requester, i.e., a registered device or an administrator. Main character energy for the requester ๐Ÿ‘‘

When a requester sends a diff query request (see Section 8), 'diff_set' is a subset of the update collection associated with that requester, and each 'diff_entry' record is a series item from that update collection. Note that 'diff_set' specifies the whole current update collection when the value of U is equal to SIZE, i.e., the current number of series items in the update collection. So if U is SIZE, you get the entire collection, no cap ๐Ÿซถ

The value N of the 'diff' query parameter in the GET request lets the requester and the AS trade off how much information gets provided against the latency of the information transfer. It's a bit of a give-and-take situation, lowkey very relatable โœจ

Since the update collection associated with each requester includes up to MAX_N series items, the AS deletes the oldest series item when a new one is generated and added to the end of the update collection, due to a new TRL update pertaining to that requester (see Section 6.2). This answers the question "When can the server decide to no longer retain older items?" that was raised in Section 3.2 of [STP]. Iconic answer, honestly ๐ŸŒˆ

Furthermore, performing a diff query of the TRL together with the "Cursor" extension as specified in Section 9 relies, in fact, on the "cursor" pattern of the STP (see Section 3.3 of [STP]). So the cursor stuff is basically the STP's cursor pattern showing up again, and it's giving pattern-matching realness ๐ŸŽ€

Appendix B. Local Supportive Parameters of the TRL Endpoint ๐ŸŽ€

okay so Table 3 is basically the cheat sheet ๐Ÿ“‹โœจ of all the local supportive parameters the AS quietly uses at its TRL endpoint when it's handling diff queries (see Section 6) and the "Cursor" extension (see Section 6.2.1). it's giving organized!! ๐Ÿ’…

the only one that's not from Section 6.2.1 is MAX_N, which lives in Section 6.2. every other parameter is defined in Section 6.2.1, and they only matter if the AS actually supports the "Cursor" extension ๐Ÿซถ

for each parameter, the table columns tell you:

  • Name: the parameter name. if the name has uppercase letters, it's a parameter whose value never changes after it's been initialized ๐Ÿ”’
  • Single instance: "Y" means there's one parameter instance for the whole TRL, and "N" means there's one instance per update collection, which is per requester ๐Ÿงš
  • Description: a short description of what it does ๐Ÿ“
  • Values: the unsigned integer values the parameter can take, where LB and UB are the inclusive lower and upper bounds ๐Ÿ”ข

quick heads up: both a registered device and an administrator get called a "requester" ๐Ÿ‘‘

   +================+==========+====================+==================+
   | Name           | Single   | Description        | Values           |
   |                | instance |                    |                  |
   +================+==========+====================+==================+
   | MAX_N          | Y        | Max number of      | LB = 1           |
   |                |          | series items in    |                  |
   |                |          | the update         | If supporting    |
   |                |          | collection of each | the "Cursor"     |
   |                |          | requester          | extension, then  |
   |                |          |                    | UB =             |
   |                |          |                    | MAX_INDEX+1      |
   +----------------+----------+--------------------+------------------+
   | MAX_DIFF_BATCH | N        | Max number of diff | LB = 1           |
   |                |          | entries included   |                  |
   |                |          | in a diff query    | UB = MAX_N       |
   |                |          | response when      |                  |
   |                |          | using the "Cursor" |                  |
   |                |          | extension          |                  |
   +----------------+----------+--------------------+------------------+
   | MAX_INDEX      | Y        | Max value of each  | LB = MAX_N-1     |
   |                |          | instance of        |                  |
   |                |          | 'index'            | UB = (2^64)-1    |
   +----------------+----------+--------------------+------------------+
   | index          | N        | Value associated   | LB = 0           |
   |                |          | with a series item |                  |
   |                |          | of an update       | UB = MAX_INDEX   |
   |                |          | collection         |                  |
   +----------------+----------+--------------------+------------------+
   | last_index     | N        | The 'index' value  | LB = 0           |
   |                |          | of the most        |                  |
   |                |          | recently added     | UB = MAX_INDEX   |
   |                |          | series item in an  |                  |
   |                |          | update collection  |                  |
   +----------------+----------+--------------------+------------------+

Table 3: Local Supportive Parameters of the TRL Endpoint

Appendix C. Interaction Examples ๐Ÿงš

this section is the fun part, bestie ๐ŸŽฌ it walks through how an RS (as a registered device) talks to an AS. in every example, all the access tokens the AS hands out are meant to be used by the RS we're looking at ๐Ÿซถ

the AS supports both full queries and diff queries of the TRL, as defined in Sections 7 and 8 respectively ๐ŸŒˆ

so the RS registration is assumed to be a POST request with an unspecified payload sent to the AS, and the AS replies with a 2.01 (Created) response ๐Ÿ“ฎ. the registration response payload is assumed to be a CBOR map, and it's assumed to include these entries:

  • a 'trl_path' parameter specifying the path of the TRL endpoint ๐Ÿ›ค๏ธ
  • a 'trl_hash' parameter specifying the "Hash Name String" of the hash function used to compute token hashes, as defined in Section 4 ๐Ÿ”
  • a 'max_n' parameter specifying the value of MAX_N, i.e., the maximum number of series items that the AS retains in the update collection associated with a registered device (see Section 6.2) ๐Ÿ“
  • possible further parameters related to the registration process ๐Ÿ’œ

also, 'h(x)' refers to the hash function used to compute the token hashes, as defined in Section 4 of this specification and according to [RFC6920]. assuming CWTs are transported in AS-to-Client responses encoded in CBOR (see Section 4.2.1), 'bstr.h(t1)' and 'bstr.h(t2)' are CBOR byte strings whose values are the token hashes of the access tokens t1 and t2, respectively ๐Ÿฆ‹

C.1. Full Query with Observe ๐Ÿ‘€

okay this one's a CoAP observation plus a full query of the TRL, and Figure 10 shows the whole vibe ๐ŸŽž๏ธ

in this example the AS does not support the "Cursor" extension, so the 'cursor' parameter isn't included in the payload of responses to a full query request ๐Ÿ™…โ€โ™€๏ธ

     RS                                                     AS
     |                                                       |
     |  Registration: POST                                   |
     +------------------------------------------------------>|
     |                                                       |
     |<------------------------------------------------------+
     |                        2.01 Created                   |
     |                          Payload: {                   |
     |                            / ... /                    |
     |                            "trl_path": "/revoke/trl", |
     |                            "trl_hash": "sha-256",     |
     |                               "max_n": 10             |
     |                          }                            |
     |                                                       |
     |  GET coap://as.example.com/revoke/trl/                |
     |    Observe: 0                                         |
     +------------------------------------------------------>|
     |                                                       |
     |<------------------------------------------------------+
     |      2.05 Content                                     |
     |        Observe: 42                                    |
     |        Content-Format: 262 (application/ace-trl+cbor) |
     |        Payload: {                                     |
     |          / full_set / 0: []                           |
     |        }                                              |
     |                                                       |
     |                          ...                          |
     |                                                       |
     |             (Access tokens t1 and t2 issued           |
     |             and successfully submitted to RS)         |
     |                                                       |
     |                          ...                          |
     |                                                       |
     |              (Access token t1 is revoked)             |
     |                                                       |
     |<------------------------------------------------------+
     |      2.05 Content                                     |
     |        Observe: 53                                    |
     |        Content-Format: 262 (application/ace-trl+cbor) |
     |        Payload: {                                     |
     |          / full_set / 0: [bstr.h(t1)]                 |
     |        }                                              |
     |                                                       |
     |                          ...                          |
     |                                                       |
     |              (Access token t2 is revoked)             |
     |                                                       |
     |<------------------------------------------------------+
     |      2.05 Content                                     |
     |        Observe: 64                                    |
     |        Content-Format: 262 (application/ace-trl+cbor) |
     |        Payload: {                                     |
     |          / full_set / 0: [bstr.h(t1), bstr.h(t2)]     |
     |        }                                              |
     |                                                       |
     |                          ...                          |
     |                                                       |
     |               (Access token t1 expires)               |
     |                                                       |
     |<------------------------------------------------------+
     |      2.05 Content                                     |
     |        Observe: 75                                    |
     |        Content-Format: 262 (application/ace-trl+cbor) |
     |        Payload: {                                     |
     |          / full_set / 0: [bstr.h(t2)]                 |
     |        }                                              |
     |                                                       |
     |                          ...                          |
     |                                                       |
     |               (Access token t2 expires)               |
     |                                                       |
     |<------------------------------------------------------+
     |      2.05 Content                                     |
     |        Observe: 86                                    |
     |        Content-Format: 262 (application/ace-trl+cbor) |
     |        Payload: {                                     |
     |          / full_set / 0: []                           |
     |        }                                              |
     |                                                       |

Figure 10: Interaction for Full Query with Observe

C.2. Diff Query with Observe ๐Ÿ’–

okay bestie, this one is a whole vibe ๐Ÿฆ‹ Figure 11 shows an interaction example where a CoAP observation and a diff query of the TRL are running together โœจ

The RS sets N = 3 as the value of the diff query parameter ๐Ÿซถ That means it's the maximum number of diff entries the AS can include in one response.

Plot twist ๐Ÿ’€ in this example the AS does not support the "Cursor" extension, so the cursor parameter and the more parameter just aren't in the payload of the responses to a diff query request. no cap, that's the whole point of this example ๐Ÿ™ˆ

         RS                                                     AS
         |                                                       |
         |  Registration: POST                                   |
         +------------------------------------------------------>|
         |                                                       |
         |<------------------------------------------------------+
         |                        2.01 Created                   |
         |                          Payload: {                   |
         |                            / ... /                    |
         |                            "trl_path": "/revoke/trl", |
         |                            "trl_hash": "sha-256",     |
         |                               "max_n": 10             |
         |                          }                            |
         |                                                       |
         |  GET coap://as.example.com/revoke/trl?diff=3          |
         |    Observe: 0                                         |
         +------------------------------------------------------>|
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 42                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: []                           |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |             (Access tokens t1 and t2 issued           |
         |             and successfully submitted to RS)         |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t1 is revoked)            |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 53                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: [                            |
         |                           [ [], [bstr.h(t1)] ]        |
         |                          ]                            |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t2 is revoked)            |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 64                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: [                            |
         |                           [ [], [bstr.h(t2)] ],       |
         |                           [ [], [bstr.h(t1)] ]        |
         |                          ]                            |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t1 expires)               |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 75                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: [                            |
         |                           [ [bstr.h(t1)], [] ],       |
         |                           [ [], [bstr.h(t2)] ],       |
         |                           [ [], [bstr.h(t1)] ]        |
         |                          ]                            |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t2 expires)               |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 86                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: [                            |
         |                           [ [bstr.h(t2)], [] ],       |
         |                           [ [bstr.h(t1)], [] ],       |
         |                           [ [], [bstr.h(t2)] ]        |
         |                          ]                            |
         |        }                                              |
         |                                                       |

Figure 11: Interaction for Diff Query with Observe โœจ

C.3. Full Query with Observe and Diff Query โœจ

okay so Figure 12 is the interaction example bestie ๐Ÿฆ‹ It shows a CoAP observation plus a full query of the TRL, and it's giving main character energy ๐Ÿ’–

Plot twist ๐Ÿ˜ญ one of the notifications from the AS gets lost in transmission, so it never reaches the RS. Not it ๐Ÿ’€

When that happens, and after the waiting time defined by the application has passed, the RS sends a GET request with no Observe Option to the AS. It's basically asking the AS to do a diff query of the TRL ๐ŸŽ€ The RS sets the diff query parameter to N = 8, which means the max number of diff entries the AS can include in a response. Iconic ๐Ÿ‘‘

Also, in this example the AS does not support the "Cursor" extension ๐Ÿ™ˆ So the cursor parameter isn't in the payload of full query responses. And the cursor and more parameters are both missing from the payloads of diff query responses too ๐Ÿ’…

         RS                                                     AS
         |                                                       |
         |  Registration: POST                                   |
         +------------------------------------------------------>|
         |                                                       |
         |<------------------------------------------------------+
         |                        2.01 Created                   |
         |                          Payload: {                   |
         |                            / ... /                    |
         |                            "trl_path": "/revoke/trl", |
         |                            "trl_hash": "sha-256",     |
         |                               "max_n": 10             |
         |                          }                            |
         |                                                       |
         |  GET coap://as.example.com/revoke/trl/                |
         |    Observe: 0                                         |
         +------------------------------------------------------>|
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 42                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / full_set / 0: []                           |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |           (Access tokens t1 and t2 issued             |
         |           and successfully submitted to RS)           |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |              (Access token t1 is revoked)             |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 53                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / full_set / 0: [bstr.h(t1)]                 |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |              (Access token t2 is revoked)             |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 64                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / full_set / 0: [bstr.h(t1), bstr.h(t2)]     |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t1 expires)               |
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 75                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / full_set / 0: [bstr.h(t2)]                 |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |               (Access token t2 expires)               |
         |                                                       |
         |  Lost X <---------------------------------------------+
         |      2.05 Content                                     |
         |        Observe: 86                                    |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / full_set / 0: []                           |
         |        }                                              |
         |                                                       |
         |                          ...                          |
         |                                                       |
         |           (Enough time has passed since               |
         |           the latest received notification)           |
         |                                                       |
         |                                                       |
         |  GET coap://as.example.com/revoke/trl?diff=8          |
         +------------------------------------------------------>|
         |                                                       |
         |<------------------------------------------------------+
         |      2.05 Content                                     |
         |        Content-Format: 262 (application/ace-trl+cbor) |
         |        Payload: {                                     |
         |          / diff_set / 1: [                            |
         |                           [ [bstr.h(t2)], [] ],       |
         |                           [ [bstr.h(t1)], [] ],       |
         |                           [ [], [bstr.h(t2)] ],       |
         |                           [ [], [bstr.h(t1)] ]        |
         |                          ]                            |
         |        }                                              |
         |                                                       |

Figure 12: Interaction for Full Query with Observe and Diff Query

16. Appendix C.4 and C.5 Interactions โœจ

C.4. Diff Query with Observe and "Cursor" Extension

okay so in this example, the AS supports the "Cursor" extension ๐Ÿ’… which means the CBOR map in the registration response also sneaks in a "max_diff_batch" parameter ๐ŸŽ€ this is the value of MAX_DIFF_BATCH, aka the max number of diff entries that can show up in a response to a diff query request from this RS ๐ŸŒธ

Figure 13 is the interaction example for a CoAP observation plus a diff query of the TRL ๐Ÿฆ‹

the RS sets the 'diff' query parameter to 3, so that's the max number of diff entries the AS can include in a response ๐Ÿ’–

if the RS hasn't heard a notification from the AS within a waiting time the application defines โฐ, it sends a GET with no Observe Option to the AS and basically asks it to do a diff query of the TRL ๐Ÿงš

then there's a follow-up diff query that includes the 'cursor' query parameter ๐Ÿซถ and bestie, notice that the payload of this response is different from the one in the previous diff query response ๐Ÿ˜ญ

   RS                                                         AS
   |                                                           |
   |  Registration: POST                                       |
   +---------------------------------------------------------->|
   |                                                           |
   |<----------------------------------------------------------+
   |                      2.01 Created                         |
   |                        Payload: {                         |
   |                          / ... /                          |
   |                                "trl_path": "/revoke/trl", |
   |                                "trl_hash": "sha-256",     |
   |                                   "max_n": 10,            |
   |                          "max_diff_batch": 5              |
   |                        }                                  |
   |                                                           |
   |  GET coap://as.example.com/revoke/trl?diff=3              |
   |    Observe: 0                                             |
   +---------------------------------------------------------->|
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Observe: 42                                    |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [],                          |
   |                / cursor / 2: null,                        |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |             (Access tokens t1 and t2 issued               |
   |             and successfully submitted to RS)             |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |               (Access token t1 is revoked)                |
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Observe: 53                                    |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [                            |
   |                               [ [], [bstr.h(t1)] ]        |
   |                              ],                           |
   |                / cursor / 2: 0,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |               (Access token t2 is revoked)                |
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Observe: 64                                    |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [                            |
   |                               [ [], [bstr.h(t2)] ],       |
   |                               [ [], [bstr.h(t1)] ]        |
   |                              ],                           |
   |                / cursor / 2: 1,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |                 (Access token t1 expires)                 |
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Observe: 75                                    |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [                            |
   |                               [ [bstr.h(t1)], [] ],       |
   |                               [ [], [bstr.h(t2)] ],       |
   |                               [ [], [bstr.h(t1)] ]        |
   |                              ],                           |
   |                / cursor / 2: 2,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |                 (Access token t2 expires)                 |
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Observe: 86                                    |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [                            |
   |                               [ [bstr.h(t2)], [] ],       |
   |                               [ [bstr.h(t1)], [] ],       |
   |                               [ [], [bstr.h(t2)] ]        |
   |                              ],                           |
   |                / cursor / 2: 3,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |                             ...                           |
   |                                                           |
   |             (Enough time has passed since                 |
   |             the latest received notification)             |
   |                                                           |
   |                                                           |
   |  GET coap://as.example.com/revoke/trl?diff=3              |
   +---------------------------------------------------------->|
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [                            |
   |                               [ [bstr.h(t2)], [] ],       |
   |                               [ [bstr.h(t1)], [] ],       |
   |                               [ [], [bstr.h(t2)] ]        |
   |                              ],                           |
   |                / cursor / 2: 3,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |
   |  GET coap://as.example.com/revoke/trl?diff=3&cursor=3     |
   +---------------------------------------------------------->|
   |                                                           |
   |<----------------------------------------------------------+
   |          2.05 Content                                     |
   |            Content-Format: 262 (application/ace-trl+cbor) |
   |            Payload: {                                     |
   |              / diff_set / 1: [],                          |
   |                / cursor / 2: 3,                           |
   |                  / more / 3: false                        |
   |            }                                              |
   |                                                           |

Figure 13: Interaction for Diff Query with Observe and "Cursor" Extension ๐ŸŒˆ

C.5. Full Query with Observe and Diff Query with "Cursor" Extension

okay bestie, same vibe here ๐ŸŽ€ the AS supports the "Cursor" extension, so the registration response's CBOR map also includes a "max_diff_batch" parameter ๐Ÿ’œ that specifies the value of MAX_DIFF_BATCH, i.e., the max number of diff entries allowed in a response to a diff query request from this RS ๐Ÿ“

Figure 14 is the interaction example for a CoAP observation plus a full query of the TRL ๐Ÿ‘‘

the example also shows some notifications from the AS getting lost in transmission ๐Ÿ˜ญ so those notifications never reach the RS ๐Ÿ’€

when that happens, and once a waiting time the application defines has passed โฐ, the RS sends a GET with no Observe Option to the AS to ask for a diff query of the TRL โœจ specifically, the RS sets:

  • The 'diff' query parameter to 8, i.e., the max number of diff entries to be included in a response from the AS. ๐Ÿฆ‹
  • The 'cursor' query parameter with a value of 2, so the RS is asking the update collection for the series items after the one whose 'index' value is 2 (in other words, the items after the last series item the RS successfully received in an earlier notification response) ๐Ÿ’Œ

The AS sends back a first batch of MAX_DIFF_BATCH = 5 series items from the update collection that belong to the RS ๐Ÿ“ฆ It says there's more to come by setting the 'more' parameter of the response to true ๐Ÿ™Œ The 'cursor' parameter in that response is 7, which is the 'index' value of the newest series item in the batch ๐ŸŽฏ

Then the RS follows up with another diff query that includes the 'cursor' query parameter with a value of 7, so it can grab the next and final batch of series items from the update collection ๐Ÿโœจ

okay so this is the rest of the story ๐ŸŽ€ the RS keeps watching the TRL, and every single update comes with its own Observe number ๐Ÿฆ‹

   RS                                                                AS
   |                                                                  |
   |  Registration: POST                                              |
   +----------------------------------------------------------------->|
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                             2.01 Created                         |
   |                               Payload: {                         |
   |                                 / ... /                          |
   |                                       "trl_path": "/revoke/trl", |
   |                                       "trl_hash": "sha-256",     |
   |                                          "max_n": 10,            |
   |                                 "max_diff_batch": 5              |
   |                               }                                  |
   |                                                                  |
   |  GET coap://as.example.com/revoke/trl/                           |
   |    Observe: 0                                                    |
   +----------------------------------------------------------------->|
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 42                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [],                          |
   |                       / cursor / 2: null                         |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                 (Access tokens t1, t2, t3 issued                 |
   |                 and successfully submitted to RS)                |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                 (Access tokens t4, t5, t6 issued                 |
   |                 and successfully submitted to RS)                |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                   (Access token t1 is revoked)                   |
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 53                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t1)],                |
   |                       / cursor / 2: 0                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                    (Access token t2 is revoked)                  |
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 64                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t1), bstr.h(t2)],    |
   |                       / cursor / 2: 1                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t1 expires)                    |
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 75                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t2)],                |
   |                       / cursor / 2: 2                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t2 expires)                    |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 86                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [],                          |
   |                       / cursor / 2: 3                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t3 is revoked)                 |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 88                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t3)],                |
   |                       / cursor / 2: 4                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t4 is revoked)                 |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 89                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t3), bstr.h(t4)],    |
   |                       / cursor / 2: 5                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t3 expires)                    |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 90                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t4)],                |
   |                       / cursor / 2: 6                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t4 expires)                    |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 91                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [],                          |
   |                       / cursor / 2: 7                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |               (Access tokens t5 and t6 are revoked)              |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 92                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t5), bstr.h(t6)],    |
   |                       / cursor / 2: 8                            |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t5 expires)                    |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 93                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [bstr.h(t6)],                |
   |                       / cursor / 2: 9                            |
   |                   }                                              |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                     (Access token t6 expires)                    |
   |                                                                  |
   |  Lost X <--------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Observe: 94                                    |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / full_set / 0: [],                          |
   |                       / cursor / 2: 10                           |
   |                   }                                              |
   |                                                                  |
   |                                ...                               |
   |                                                                  |
   |                 (Enough time has passed since                    |
   |                 the latest received notification)                |
   |                                                                  |
   |                                                                  |
   |  GET coap://as.example.com/revoke/trl?diff=8&cursor=2            |
   +----------------------------------------------------------------->|
   |                                                                  |
   |<-----------------------------------------------------------------+
   |                 2.05 Content                                     |
   |                   Content-Format: 262 (application/ace-trl+cbor) |
   |                   Payload: {                                     |
   |                     / diff_set / 1: [                            |
   |                                      [ [bstr.h(t4)], [] ],       |
   |                                      [ [bstr.h(t3)], [] ],       |
   |                                      [ [], [bstr.h(t4)] ],       |
   |                                      [ [], [bstr.h(t3)] ],       |
   |                                      [ [bstr.h(t2)], [] ]        |
   |                                     ],                           |
   |                       / cursor / 2: 7,                           |
   |                         / more / 3: true                         |
   |                   }                                              |
   |                                                                  |
   |  GET coap://as.example.com/revoke/trl?diff=8&cursor=7            |
   +----------------------------------------------------------------->|
   |                                                                  |
   |<-----------------------------------------------------------------+
   |            2.05 Content                                          |
   |              Content-Format: 262 (application/ace-trl+cbor)      |
   |              Payload: {                                          |
   |                / diff_set / 1: [                                 |
   |                                 [ [bstr.h(t6)], [] ],            |
   |                                 [ [bstr.h(t5)], [] ],            |
   |                                 [ [], [bstr.h(t5), bstr.h(t6)] ] |
   |                                ],                                |
   |                  / cursor / 2: 10,                               |
   |                    / more / 3: false                             |
   |              }                                                   |
   |                                                                  |

Figure 14: Interaction for Full Query with Observe and Diff Query with "Cursor" Extension

Acknowledgments ๐Ÿซถ

Ludwig Seitz was a coauthor on the early versions of this document, so huge thanks to him ๐Ÿ’œ.

The authors really, truly thank Christian Amsรผss, Carsten Bormann, Deb Cooley, Roman Danyliw, Dhruv Dhody, Rikard Hรถglund, Benjamin Kaduk, David Navarro, Joerg Ott, Marco Rasori, Michael Richardson, Kyle Rose, Zaheduzzaman Sarker, Jim Schaad, Gรถran Selander, Travis Spencer, Orie Steele, ร‰ric Vyncke, Niklas Widell, Dale Worley, and Paul Wouters for their comments and feedback ๐ŸŒธโœจ.

The work on this document was partly supported by Sweden's Innovation Agency VINNOVA and the Celtic-Next projects CRITISEC and CYPRESS, and by the H2020 project SIFIS-Home (Grant agreement 952652) ๐Ÿ‡ธ๐Ÿ‡ช๐Ÿ’–.

Authors' Addresses ๐Ÿฆ‹

Marco Tiloca
RISE AB
Isafjordsgatan 22
SE-164 40 Kista
Sweden
Email: marco.tiloca@ri.se


Francesca Palombini
Ericsson AB
Torshamnsgatan 23
SE-164 40 Kista
Sweden
Email: francesca.palombini@ericsson.com


Sebastian Echeverria
CMU SEI
4500 Fifth Avenue
Pittsburgh, PA 15213-2612
United States of America
Email: secheverria@sei.cmu.edu


Grace Lewis
CMU SEI
4500 Fifth Avenue
Pittsburgh, PA 15213-2612
United States of America
Email: glewis@sei.cmu.edu

That's the whole RFC, bestie! ๐ŸŒˆ๐Ÿ‘‘ Every figure, every author, every last word. Periodt. ๐Ÿ’…

๐Ÿ“ฆ where this page comes from