<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.43 (Ruby 3.4.9) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-richer-oauth-oob-authcode-00" category="info" consensus="true" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.34.1 -->
  <front>
    <title abbrev="OOB Auth Codes">Out of Band Authorization Code Delivery for OAuth 2.0</title>
    <seriesInfo name="Internet-Draft" value="draft-richer-oauth-oob-authcode-00"/>
    <author fullname="Justin Richer">
      <organization>MongoDB</organization>
      <address>
        <email>ietf@justin.richer.org</email>
      </address>
    </author>
    <date year="2026" month="September" day="28"/>
    <area>Security</area>
    <workgroup>Web Authorization Protocol</workgroup>
    <keyword>next generation</keyword>
    <keyword>unicorn</keyword>
    <keyword>AI-native</keyword>
    <abstract>
      <?line 41?>

<t>This client-side process allows clients to use the authorization code grant type without the ability to host the redirect_uri themselves. This process creates a single copyable value that the resource owner can copy from a simple helper page into the waiting client application.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://jricher.github.io/draft-richer-oauth-oob-authcode/draft-richer-oauth-oob-authcode.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-richer-oauth-oob-authcode/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Web Authorization Protocol Working Group mailing list (<eref target="mailto:oauth@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/oauth/"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/oauth/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/jricher/draft-richer-oauth-oob-authcode"/>.</t>
    </note>
  </front>
  <middle>
    <?line 45?>

<section anchor="intro">
      <name>Introduction</name>
      <t>The OAuth authorization code grant type defined in <xref target="OAUTH"/> relies on the front-channel to communicate parameters between the client and authorization server (AS) using query parameters. This process relies on the resource owner's browser being able to launch a URI that is hosted on each of the AS and the client.</t>
      <t>In some circumstances, such as a command line client running on a remote terminal, the resource owner's browser cannot reach a URL hosted by the client. These kinds of clients have traditionally used alternatives such as the device code grant type <xref target="DEVICECODE"/>, but the split between different grant types depending on where the client is running is a complication that requires server-side support, multiple network roundtrips (with polling), and potentially multiple different client registrations.</t>
      <t>Instead of using a different grant type, an out-of-band mechanism, such as the resource owner copying and pasting values into the waiting client, to deliver the authorization code and state value from the AS to the client. This process can be error prone as multiple separate values need to be transferred intact to the client without having their values conflated with each other.</t>
      <t>This specification defines a method to harden the out-of-band delivery of the <tt>code</tt> and <tt>state</tt> parameters to the client to be a single value that is less error-prone. No changes or participation by the AS is required (apart from a valid redirect_uri for the helper page).</t>
      <section anchor="terminology">
        <name>Terminology</name>
        <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
        <?line -18?>

</section>
    </section>
    <section anchor="protocol">
      <name>Out of Band Authorization Code Transfer</name>
      <artset>
        <artwork type="svg"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="512" width="632" viewBox="0 0 632 512" class="diagram" text-anchor="middle" font-family="monospace" font-size="13px" stroke-linecap="round">
            <path d="M 8,32 L 8,64" fill="none" stroke="black"/>
            <path d="M 56,72 L 56,464" fill="none" stroke="black"/>
            <path d="M 104,32 L 104,64" fill="none" stroke="black"/>
            <path d="M 136,32 L 136,64" fill="none" stroke="black"/>
            <path d="M 184,72 L 184,312" fill="none" stroke="black"/>
            <path d="M 184,328 L 184,464" fill="none" stroke="black"/>
            <path d="M 208,384 L 208,416" fill="none" stroke="black"/>
            <path d="M 232,32 L 232,64" fill="none" stroke="black"/>
            <path d="M 264,32 L 264,64" fill="none" stroke="black"/>
            <path d="M 312,72 L 312,120" fill="none" stroke="black"/>
            <path d="M 312,136 L 312,312" fill="none" stroke="black"/>
            <path d="M 312,328 L 312,464" fill="none" stroke="black"/>
            <path d="M 360,32 L 360,64" fill="none" stroke="black"/>
            <path d="M 392,32 L 392,64" fill="none" stroke="black"/>
            <path d="M 440,72 L 440,312" fill="none" stroke="black"/>
            <path d="M 440,328 L 440,464" fill="none" stroke="black"/>
            <path d="M 488,32 L 488,64" fill="none" stroke="black"/>
            <path d="M 520,32 L 520,64" fill="none" stroke="black"/>
            <path d="M 568,72 L 568,464" fill="none" stroke="black"/>
            <path d="M 592,256 L 592,288" fill="none" stroke="black"/>
            <path d="M 616,32 L 616,64" fill="none" stroke="black"/>
            <path d="M 8,32 L 104,32" fill="none" stroke="black"/>
            <path d="M 136,32 L 232,32" fill="none" stroke="black"/>
            <path d="M 264,32 L 360,32" fill="none" stroke="black"/>
            <path d="M 392,32 L 488,32" fill="none" stroke="black"/>
            <path d="M 520,32 L 616,32" fill="none" stroke="black"/>
            <path d="M 8,64 L 104,64" fill="none" stroke="black"/>
            <path d="M 136,64 L 232,64" fill="none" stroke="black"/>
            <path d="M 264,64 L 360,64" fill="none" stroke="black"/>
            <path d="M 392,64 L 488,64" fill="none" stroke="black"/>
            <path d="M 520,64 L 616,64" fill="none" stroke="black"/>
            <path d="M 192,96 L 208,96" fill="none" stroke="black"/>
            <path d="M 224,96 L 304,96" fill="none" stroke="black"/>
            <path d="M 192,128 L 208,128" fill="none" stroke="black"/>
            <path d="M 224,128 L 432,128" fill="none" stroke="black"/>
            <path d="M 320,160 L 336,160" fill="none" stroke="black"/>
            <path d="M 352,160 L 432,160" fill="none" stroke="black"/>
            <path d="M 320,192 L 336,192" fill="none" stroke="black"/>
            <path d="M 352,192 L 432,192" fill="none" stroke="black"/>
            <path d="M 448,224 L 464,224" fill="none" stroke="black"/>
            <path d="M 480,224 L 560,224" fill="none" stroke="black"/>
            <path d="M 576,256 L 592,256" fill="none" stroke="black"/>
            <path d="M 576,288 L 592,288" fill="none" stroke="black"/>
            <path d="M 64,320 L 80,320" fill="none" stroke="black"/>
            <path d="M 96,320 L 560,320" fill="none" stroke="black"/>
            <path d="M 64,352 L 80,352" fill="none" stroke="black"/>
            <path d="M 96,352 L 176,352" fill="none" stroke="black"/>
            <path d="M 192,384 L 208,384" fill="none" stroke="black"/>
            <path d="M 192,416 L 208,416" fill="none" stroke="black"/>
            <path d="M 192,448 L 208,448" fill="none" stroke="black"/>
            <path d="M 232,448 L 304,448" fill="none" stroke="black"/>
            <polygon class="arrowhead" points="584,288 572,282.4 572,293.6" fill="black" transform="rotate(180,576,288)"/>
            <polygon class="arrowhead" points="568,224 556,218.4 556,229.6" fill="black" transform="rotate(0,560,224)"/>
            <polygon class="arrowhead" points="440,192 428,186.4 428,197.6" fill="black" transform="rotate(0,432,192)"/>
            <polygon class="arrowhead" points="440,128 428,122.4 428,133.6" fill="black" transform="rotate(0,432,128)"/>
            <polygon class="arrowhead" points="328,160 316,154.4 316,165.6" fill="black" transform="rotate(180,320,160)"/>
            <polygon class="arrowhead" points="312,448 300,442.4 300,453.6" fill="black" transform="rotate(0,304,448)"/>
            <polygon class="arrowhead" points="312,96 300,90.4 300,101.6" fill="black" transform="rotate(0,304,96)"/>
            <polygon class="arrowhead" points="200,416 188,410.4 188,421.6" fill="black" transform="rotate(180,192,416)"/>
            <polygon class="arrowhead" points="184,352 172,346.4 172,357.6" fill="black" transform="rotate(0,176,352)"/>
            <polygon class="arrowhead" points="72,320 60,314.4 60,325.6" fill="black" transform="rotate(180,64,320)"/>
            <g class="text">
              <text x="60" y="52">User</text>
              <text x="188" y="52">Client</text>
              <text x="316" y="52">AS</text>
              <text x="440" y="52">Browser</text>
              <text x="572" y="52">Helper</text>
              <text x="216" y="100">1</text>
              <text x="216" y="132">2</text>
              <text x="344" y="164">3</text>
              <text x="344" y="196">4</text>
              <text x="472" y="228">5</text>
              <text x="616" y="276">(6)</text>
              <text x="88" y="324">7</text>
              <text x="88" y="356">8</text>
              <text x="232" y="404">(9)</text>
              <text x="220" y="452">10</text>
            </g>
          </svg>
        </artwork>
        <artwork type="ascii-art"><![CDATA[
  +-----------+   +-----------+   +-----------+   +-----------+   +-----------+
  |    User   |   |   Client  |   |     AS    |   |  Browser  |   |   Helper  |
  +-----------+   +-----------+   +-----------+   +-----------+   +-----------+
        |               |               |               |               |
        |               |--(1)--------->|               |               |
        |               |               |               |               |
        |               |--(2)------------------------->|               |
        |               |               |               |               |
        |               |               |<-(3)----------|               |
        |               |               |               |               |
        |               |               |--(4)--------->|               |
        |               |               |               |               |
        |               |               |               |--(5)--------->|
        |               |               |               |               |
        |               |               |               |               |--+
        |               |               |               |               |  | (6)
        |               |               |               |               |<-+
        |               |               |               |               |
        |<-(7)----------------------------------------------------------|
        |               |               |               |               |
        |--(8)--------->|               |               |               |
        |               |               |               |               |
        |               |--+            |               |               |
        |               |  | (9)        |               |               |
        |               |<-+            |               |               |
        |               |               |               |               |
        |               |--(10)-------->|               |               |
        |               |               |               |               |

]]></artwork>
      </artset>
      <ol spacing="normal" type="1"><li>
          <t>The client registers its helper page <tt>https://c.example.com/callback</tt> as a valid <tt>redirect_uri</tt></t>
        </li>
        <li>
          <t>The client creates a <tt>state</tt> value and sends it with other parameters to the browser as part of the authorization URI; the client waits for a paste input</t>
        </li>
        <li>
          <t>The browser fetches the authorization URI at the AS</t>
        </li>
        <li>
          <t>The AS completes the OAuth transaction and creates a <tt>code</tt> value and sends this to the browser as part of the callback</t>
        </li>
        <li>
          <t>The browser fetches the helper page with the <tt>code</tt> and <tt>state</tt> parameters</t>
        </li>
        <li>
          <t>The helper page parses the <tt>code</tt> and <tt>state</tt> parameters in javascript and calculates the combined code using HKDF in <xref target="create"/></t>
        </li>
        <li>
          <t>The user copies the combined code from the helper page</t>
        </li>
        <li>
          <t>The user pastes the combined code into the client</t>
        </li>
        <li>
          <t>The client uses its stored <tt>state</tt> value to derive the <tt>code</tt> value from the combined code in <xref target="process"/></t>
        </li>
        <li>
          <t>The client presents the derived <tt>code</tt> value to the token endpoint for an access token, along with other parmeters</t>
        </li>
      </ol>
      <t>Throughout this process, the client and AS need not have previously agreed on any shared secrets or encodings. The client does not start an HTTP server and does not receive the call to the redirect URI at all.</t>
      <t>The client does need to know the value of the INFO parameter of the HKDF that the helper page uses and use the same value.</t>
      <section anchor="create">
        <name>Creating the Combined Code</name>
        <t>The <tt>state</tt> and <tt>code</tt> values are combined using a HKDF function <xref target="HKDF"/> and a simple bytewise XOR.</t>
        <artwork><![CDATA[
1.  C  = UTF8(code)
2.  KS = HKDF(ikm  = UTF8(state),
              salt = "" (zero-length),
              info = INFO,
              L    = len(C)) ; always the same length as C
3.  E  = C ^ KS ; bytewise XOR
4.  T  = SHA256(C)[0..2] ; checksum
5.  CC = B64Uenc(T) || B64Uenc(E) ; concatenate the checksum
]]></artwork>
        <ol spacing="normal" type="1"><li>
            <t>The <tt>code</tt> is translated to UTF8 bytes. Since the authorization code is ASCII per <xref target="OAUTH"/>, no additional encoding is needed for valid inputs.</t>
          </li>
          <li>
            <t>The HKDF function creates a derived key based on the <tt>state</tt> value, the result that is exactly the same length as the <tt>code</tt> value.</t>
          </li>
          <li>
            <t>The <tt>code</tt> value and the HKDF output are XORed together, bytewise, to create an encoded value E.</t>
          </li>
          <li>
            <t>A 3-byte checksum is created by hashing the code value with <xref target="SHA256"/>.</t>
          </li>
          <li>
            <t>The combined code CC is the concatenated checksum from (4) and the encoded value E from (3), both separately base64 URL encoded (with no padding). <xref target="BASE64"/></t>
          </li>
        </ol>
        <t>The helper page <bcp14>MUST</bcp14> display the combined code value to the user.</t>
      </section>
      <section anchor="process">
        <name>Processing the Combined Code</name>
        <t>When the client receives the combined code, it uses its <tt>state</tt> value and extracts the <tt>code</tt> value for use at the AS.</t>
        <artwork><![CDATA[
1.  T' = B64Udec(CC[0..3])
2.  E  = B64Udec(CC[4..len(CC)])
3.  KS = HKDF(ikm  = UTF8(state),
              salt = "" (zero-length),
              info = INFO,
              L    = len(E))
4.  C = E ^ KS ; bytewise XOR
5.  T = SHA256(C)[0..2] ; checksum
6.  If T != T': FAIL
]]></artwork>
        <ol spacing="normal" type="1"><li>
            <t>Extract the checksum as sent over the wire and decode it using Base64 URL with no padding <xref target="BASE64"/> into 3 bytes (4 Base64 chars).</t>
          </li>
          <li>
            <t>Decode the remainder of the string into a byte array representing the encoded value E.</t>
          </li>
          <li>
            <t>The HKDF function creates the derived key based on the <tt>state</tt> value, the result is exactly the same length as the encoded value E.</t>
          </li>
          <li>
            <t>The combined code and the HKDF output are XORed together, bytewise, to extract the <tt>code</tt>.</t>
          </li>
          <li>
            <t>A 3-byte checksum is created by hashing the extracted code value with <xref target="SHA256"/>.</t>
          </li>
          <li>
            <t>If the checksum against the extracted code doesn't match the checksum sent over the wire, fail.</t>
          </li>
        </ol>
        <t>Any combined codes equal to or fewer than the checksum length (4 characters / 3 decoded bytes) <bcp14>MUST</bcp14> be discarded.</t>
        <t>The client then uses the <tt>code</tt> value in its request to the token endpoint.</t>
        <t>If the <tt>state</tt> value doesn't match, the key derivation will fail to produce a valid code, and the token request will fail.</t>
      </section>
    </section>
    <section anchor="implementation">
      <name>Limitations and Implementation Considerations</name>
      <section anchor="helper-page">
        <name>Helper Page</name>
        <t>The helper page is designed to be a statically hosted page with no backend processing and no state. Consequently, any person (or attacker) could use the same page to create combined codes using this algorithm by providing arbitrary <tt>code</tt> and <tt>state</tt> values. As such, the use of this does not protect clients from theft of <tt>code</tt> and <tt>state</tt> values.</t>
      </section>
      <section anchor="stateless-clients">
        <name>Stateless Clients</name>
        <t>In order to use this, a client needs to know its <tt>state</tt> value at the time the request comes in. Stateless clients that depend on the <tt>state</tt> value to bootstrap the request won't be able to extract the <tt>code</tt> value from a combined code and shouldn't use this method.</t>
      </section>
      <section anchor="fallthrough">
        <name>Fall-through For Static Pages</name>
        <t>If the helper page is unable to process the HKDF calculation, such as JavaScript being disabled, the helper page <bcp14>SHOULD</bcp14> tell the user as much.</t>
        <t>To support such cases, the client <bcp14>SHOULD</bcp14> additionally support the user copying and pasting the entire helper page URL, with parameters, and extracting the <tt>code</tt> and <tt>state</tt> values directly, just as if the client had served the URL itself. For this case, there is no use of the HKDF to create a combined code.</t>
      </section>
      <section anchor="issuer-parameter">
        <name>Issuer Parameter</name>
        <t>This process focuses only on the <tt>code</tt> and <tt>state</tt> parameters, and drops all other parameters including <tt>iss</tt>. This value could be used as the INFO parameter to the HKDF, but only if the client knows that the <tt>iss</tt> parameter is returned from the AS.</t>
      </section>
    </section>
    <section anchor="security">
      <name>Security Considerations</name>
      <t>This process provides no additional security protections beyond that already provided by the basic authorization code grant. While cryptographic functions are used, the <tt>code</tt> value is still passed in the browser during the first steps and is not kept secret.</t>
      <t>The <tt>code</tt> and <tt>state</tt> values are passed to whatever server, CDN, or system serves the static helper page, including its mirrors and caches, as part of the HTTP GET request.</t>
      <t>The <tt>state</tt> value now needs to be cryptographically random enough to support the HKDF, and at the very least needs to be unique per request. Two identical (or guessable) state values could decode different codes on different requests.</t>
    </section>
    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="OAUTH">
          <front>
            <title>The OAuth 2.0 Authorization Framework</title>
            <author fullname="D. Hardt" initials="D." role="editor" surname="Hardt"/>
            <date month="October" year="2012"/>
            <abstract>
              <t>The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. This specification replaces and obsoletes the OAuth 1.0 protocol described in RFC 5849. [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="6749"/>
          <seriesInfo name="DOI" value="10.17487/RFC6749"/>
        </reference>
        <reference anchor="HKDF">
          <front>
            <title>HMAC-based Extract-and-Expand Key Derivation Function (HKDF)</title>
            <author fullname="H. Krawczyk" initials="H." surname="Krawczyk"/>
            <author fullname="P. Eronen" initials="P." surname="Eronen"/>
            <date month="May" year="2010"/>
            <abstract>
              <t>This document specifies a simple Hashed Message Authentication Code (HMAC)-based key derivation function (HKDF), which can be used as a building block in various protocols and applications. The key derivation function (KDF) is intended to support a wide range of applications and requirements, and is conservative in its use of cryptographic hash functions. This document is not an Internet Standards Track specification; it is published for informational purposes.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="5869"/>
          <seriesInfo name="DOI" value="10.17487/RFC5869"/>
        </reference>
        <reference anchor="BASE64">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
            <date month="October" year="2006"/>
            <abstract>
              <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes. It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings. [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
        <reference anchor="SHA256">
          <front>
            <title>US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)</title>
            <author fullname="D. Eastlake 3rd" initials="D." surname="Eastlake 3rd"/>
            <author fullname="T. Hansen" initials="T." surname="Hansen"/>
            <date month="May" year="2011"/>
            <abstract>
              <t>Federal Information Processing Standard, FIPS</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="6234"/>
          <seriesInfo name="DOI" value="10.17487/RFC6234"/>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="DEVICECODE">
          <front>
            <title>OAuth 2.0 Device Authorization Grant</title>
            <author fullname="W. Denniss" initials="W." surname="Denniss"/>
            <author fullname="J. Bradley" initials="J." surname="Bradley"/>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <author fullname="H. Tschofenig" initials="H." surname="Tschofenig"/>
            <date month="August" year="2019"/>
            <abstract>
              <t>The OAuth 2.0 device authorization grant is designed for Internet- connected devices that either lack a browser to perform a user-agent- based authorization or are input constrained to the extent that requiring the user to input text in order to authenticate during the authorization flow is impractical. It enables OAuth clients on such devices (like smart TVs, media consoles, digital picture frames, and printers) to obtain user authorization to access protected resources by using a user agent on a separate device.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8628"/>
          <seriesInfo name="DOI" value="10.17487/RFC8628"/>
        </reference>
      </references>
    </references>
    <?line 214?>

<section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thank you to Jeff Lombardo for an early review of this work.</t>
    </section>
  </back>
  <!-- ##markdown-source:
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

-->

</rfc>
