<?xml version="1.0" encoding="UTF-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
  ipr="trust200902"
  docName="draft-external-assertion-oauth-grant-00"
  category="std"
  consensus="true"
  submissionType="IETF"
  tocInclude="true"
  sortRefs="true"
  symRefs="true" version="3">

  <front>
    <title abbrev="OAuth2 External Assertion Grant">
      OAuth 2.0 External Assertion Authorization Grant
    </title>

    <author fullname="Jorge Turrado" initials="JT." surname="Turrado">
      <organization>Independent</organization>
      <address>
        <email>jorge_turrado@hotmail.es</email>
      </address>
    </author>
    <author fullname="Fernando Escolar" initials="FE." surname="Escolar">
      <organization>Independent</organization>
      <address>
        <email>fer.escolar@gmail.com</email>
      </address>
    </author>

    <date year="2025" month="September" day="26"/>

    <area>Security</area>
    <workgroup>OAuth Working Group</workgroup>

    <abstract>
      <t>
        This document specifies a new OAuth 2.0 authorization grant type,
        "external assertion", identified by
        <tt>urn:ietf:params:oauth:grant-type:external-assertion</tt>.
        It enables a client to obtain an access token by presenting a verifiable
        JWT assertion issued by a trusted external identity provider to an
        authorization server. The mechanism facilitates short-lived, auditable
        credentials for workloads without provisioning long-lived secrets.
      </t>
    </abstract>
  </front>

  <middle>
    <section anchor="conventions" title="Conventions and Definitions">
      <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>
      <dl>
        <dt>Authorization Server (AS):</dt>
        <dd>
          <t>The OAuth 2.0 server that issues access tokens.</t>
        </dd>
        <dt>External Identity Provider (External IdP):</dt>
        <dd>
          <t>An OAuth 2.0/OpenID Connect compliant identity provider that issues
          JWT assertions trusted by the AS.</t>
        </dd>
        <dt>Assertion:</dt>
        <dd>
          <t>A signed JWT bearing claims used by the AS to authenticate and
          authorize the client for token issuance.</t>
        </dd>
      </dl>
    </section>

    <section anchor="introduction" title="Introduction">
      <t>
        The OAuth 2.0 Authorization Framework (<xref target="RFC6749"/>) defines
        multiple grant types for obtaining access tokens. In many environments,
        workloads already possess identities issued by an external IdP. In order
        to interact with a resource server that relies on an AS different from
        the external IdP, a mechanism is needed to exchange the external identity
        for a locally-issued access token without provisioning long-lived secrets.
      </t>
      <t>
        This document defines the "external assertion" grant type, identified by
        <tt>urn:ietf:params:oauth:grant-type:external-assertion</tt>,
        which allows a client to present a verifiable JWT assertion issued by a
        trusted external IdP to an AS in exchange for a short-lived access token.
        The grant is conceptually similar to token exchange (<xref target="RFC8693"/>)
        but is narrowly focused on a client-submitted external JWT assertion used
        as an authorization grant, and it does not define subject or actor token
        semantics beyond the validation rules herein.
      </t>
    </section>

    <section anchor="overview" title="Protocol Overview">
      <t>
        At a high level, the client obtains a JWT assertion from an external IdP
        and submits it to the AS's token endpoint using the grant type defined
        in this document. The AS validates the assertion according to its local
        trust configuration (e.g., trusted issuers and keys), and, if validation
        succeeds and policy permits, issues an access token to the client.
      </t>
    </section>

    <section anchor="grant" title="External Assertion Grant Type">
      <section anchor="request" title="Token Request">
        <t>
          The client makes a request to the token endpoint with the
          content type <tt>application/x-www-form-urlencoded</tt>
          as defined in <xref target="RFC6749"/> Appendix&#160;B, including the
          following parameters:
        </t>
        <dl>
          <dt><tt>grant_type</tt>:</dt>
          <dd>
            <bcp14>REQUIRED</bcp14>. Value <bcp14>MUST</bcp14> be
            <tt>urn:ietf:params:oauth:grant-type:external-assertion</tt>.
          </dd>
          <dt><tt>client_id</tt>:</dt>
          <dd>
            <bcp14>REQUIRED</bcp14> - <tt>unless client authentication is otherwise
            established by means outside of this request. The OAuth client
            identifier.</tt>
          </dd>

          <dt><tt>client_assertion</tt>:</dt>
          <dd>
            <bcp14>REQUIRED</bcp14>. A JWT assertion issued by a trusted
            external IdP. The assertion <bcp14>MUST</bcp14> be integrity-protected
            (e.g., JWS) and <bcp14>MUST</bcp14> contain the claims specified in
            <xref target="validation"/>.
          </dd>

          <dt><tt>scope</tt>:</dt>
          <dd>
            <bcp14>OPTIONAL</bcp14>. The scope of the access request as described
            in <xref target="RFC6749" section="3.3"/>.
          </dd>
        </dl>

        <t>Example request:</t>
        <artwork name="HTTP" type="inline"><![CDATA[
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:external-assertion&
client_id=s6BhdRkqt3&
client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
]]></artwork>
      </section>

      <section anchor="validation" title="Assertion Validation">
        <t>
          Upon receiving the request, the AS <bcp14>MUST</bcp14> perform all of
          the following validation steps:
        </t>
        <dl>
          <dt>Client Authorization</dt>
          <dd>
            Verify the client is recognized,
            authenticated as required by AS policy, and permitted to use this
            grant type (<tt>unauthorized_client</tt> if not).
          </dd>


          <dt>Issuer Trust</dt>
          <dd>
            Ensure the assertion's <tt>iss</tt>
            value is configured as a trusted issuer. Trust configuration is out
            of scope; it may use local policy, static keys, or dynamic discovery
            (e.g., OpenID Provider configuration and JWKS).
          </dd>

          <dt>Signature Verification</dt>
          <dd>
            Validate the JWS signature using keys associated with the trusted issuer (see <xref target="RFC7517"/>
            and <xref target="RFC7515"/>).
          </dd>
          <dt>Expiration and Not-Before</dt>
          <dd>
            Enforce <tt>exp</tt> and, if present, <tt>nbf</tt>. The assertion
            <bcp14>MUST</bcp14> be unexpired at the time of processing.
          </dd>

          <dt>Audience</dt>
          <dd>
            Ensure the assertion's <tt>aud</tt> identifies the AS (e.g., token endpoint or an AS audience value).
          </dd>

          <dt>Subject</dt>
          <dd>
            Validate the <tt>sub</tt> according to AS policy for the trusted issuer (e.g., allowlists or mappings).
          </dd>

          <dt>JWT ID (Replay)</dt>
          <dd>
            If a <tt>jti</tt> is present, the AS <bcp14>MUST</bcp14> prevent replay by rejecting previously seen
            <tt>jti</tt> values within the assertion's validity window. Use of <tt>jti</tt> is <bcp14>RECOMMENDED</bcp14>.
          </dd>

          <dt>Issued-At (Freshness)</dt>
          <dd>
            If <tt>iat</tt> is present, the AS <bcp14>SHOULD</bcp14> enforce a maximum assertion age according to policy.
          </dd>

          <dt>Scope and Policy</dt>
          <dd>
            Enforce that requested scopes are authorized for the client and permitted for the asserted identity.
          </dd>
        </dl>

        <t>
          The assertion <bcp14>MUST</bcp14> be a JWT (<xref target="RFC7519"/>)
          and <bcp14>MUST</bcp14> include the following claims:
        </t>
        <dl>
          <dt><tt>iss</tt></dt>
          <dd>Issuer identifier of the external IdP.</dd>
          <dt><tt>sub</tt></dt>
          <dd>Subject identifier at the external IdP.</dd>
          <dt><tt>aud</tt></dt>
          <dd>Audience for the AS (value defined by AS policy).</dd>
          <dt><tt>exp</tt></dt>
          <dd>Expiration time.</dd>
          <dt><tt>iat</tt></dt>
          <dd>Issued-at time (<bcp14>RECOMMENDED</bcp14>).</dd>
          <dt><tt>jti</tt></dt>
          <dd>JWT ID for replay detection (<bcp14>RECOMMENDED</bcp14>).</dd>
        </dl>

        <t>
          The assertion <bcp14>SHOULD</bcp14> be signed with an algorithm acceptable
          to the AS (e.g., RS256 or ES256). Use of <tt>none</tt>
          <bcp14>MUST NOT</bcp14> be accepted. If the AS supports nested JWTs,
          additional confidentiality mechanisms are deployment-specific.
        </t>
      </section>

      <section anchor="response" title="Token Response">
        <t>
          If the request is valid and authorized, the AS issues an access token
          per <xref target="RFC6749" section="5.1"/>. A refresh token
          <bcp14>MUST NOT</bcp14> be issued for this grant type.
        </t>

        <t>Example successful response:</t>
        <artwork name="HTTP" type="inline"><![CDATA[
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "access_token": "2YotnFZFEjr1zCsicMWpAA",
  "token_type": "bearer",
  "expires_in": 3600
}
]]></artwork>
      </section>

      <section anchor="errors" title="Error Response">
        <t>
          On error, the AS returns an error response as defined in
          <xref target="RFC6749" section="5.2"/>. The following error codes are
          commonly applicable:
        </t>
        <dl>
          <dt><tt>invalid_request</tt></dt>
          <dd>Malformed or missing parameters.</dd>
          <dt><tt>unauthorized_client</tt></dt>
          <dd>Client not authorized for this grant.</dd>
          <dt><tt>invalid_grant</tt></dt>
          <dd>Assertion invalid, expired, audience mismatch, replayed, or issuer not trusted.</dd>
          <dt><tt>unsupported_grant_type</tt></dt>
          <dd>Grant type not enabled at the AS.</dd>
        </dl>
      </section>
    </section>

    <section anchor="security" title="Security Considerations">
      <t>
        The security of this grant depends on the AS's trust configuration and
        assertion validation. Deployments <bcp14>MUST</bcp14>:
      </t>
      <ul>
        <li><t>Restrict trusted issuers and acceptable algorithms by policy.</t></li>
        <li><t>Validate signatures and enforce <tt>exp</tt>/<tt>nbf</tt> strictly.</t></li>
        <li><t>Bind assertions to the AS via <tt>aud</tt>; reject generic or missing audiences.</t></li>
        <li><t>Prevent replay via unique <tt>jti</tt> and server-side caching within validity windows.</t></li>
        <li><t>Issue short-lived access tokens and prefer least-privilege scopes.</t></li>
        <li><t>Use TLS for all endpoints and key retrieval.</t></li>
        <li><t>Log validation outcomes and consider rate limiting failed attempts.</t></li>
      </ul>
      <t>
        If the AS includes an "actor" representation in issued tokens (e.g., the
        <tt>act</tt> claim from <xref target="RFC8693"/>), it
        <bcp14>SHOULD</bcp14> ensure that privacy and policy constraints are respected.
      </t>
    </section>

    <section anchor="privacy" title="Privacy Considerations">
      <t>
        Assertions may include identifiers that can correlate clients across
        services. Deployments <bcp14>SHOULD</bcp14> minimize personally
        identifiable information in assertions, avoid unnecessary claim
        propagation into issued tokens, and follow data minimization practices.
      </t>
    </section>

    <section anchor="iana" title="IANA Considerations">
      <t>
        This document requests registration of the following value in the
        "OAuth URI" registry:
      </t>
      <artwork><![CDATA[
URN: urn:ietf:params:oauth:grant-type:external-assertion
Common Name: External Assertion Grant
Change Controller: IESG
Specification Document: This document
]]></artwork>
    </section>
  </middle>

  <back>
    <references>
      <name>Normative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6749.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7515.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7517.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7519.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8693.xml"
                  xmlns:xi="http://www.w3.org/2001/XInclude"/>
    </references>

    <section anchor="ack" title="Acknowledgments">
      <t>
        The authors thank the OAuth community for prior work on assertion-based
        flows and token exchange.
      </t>
    </section>
  </back>
</rfc>
