Re: [OAUTH-WG] Benjamin Kaduk's Discuss on draft-ietf-oauth-jwt-introspection-response-08: (with DISCUSS and COMMENT)

Benjamin Kaduk <> Sun, 15 March 2020 16:04 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 2CF753A1851; Sun, 15 Mar 2020 09:04:06 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -1.899
X-Spam-Status: No, score=-1.899 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id FTBlq0hRbmNH; Sun, 15 Mar 2020 09:04:04 -0700 (PDT)
Received: from ( []) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by (Postfix) with ESMTPS id AE8613A184F; Sun, 15 Mar 2020 09:04:04 -0700 (PDT)
Received: from ([]) (authenticated bits=56) (User authenticated as kaduk@ATHENA.MIT.EDU) by (8.14.7/8.12.4) with ESMTP id 02FG3snO018502 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sun, 15 Mar 2020 12:03:57 -0400
Date: Sun, 15 Mar 2020 09:03:54 -0700
From: Benjamin Kaduk <>
To: Torsten Lodderstedt <>
Cc:,, oauth <>, Rifaat Shekh-Yusef <>, Roman Danyliw <>
Message-ID: <>
References: <> <> <> <>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
In-Reply-To: <>
User-Agent: Mutt/1.12.1 (2019-06-15)
Archived-At: <>
Subject: Re: [OAUTH-WG] Benjamin Kaduk's Discuss on draft-ietf-oauth-jwt-introspection-response-08: (with DISCUSS and COMMENT)
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: OAUTH WG <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Sun, 15 Mar 2020 16:04:06 -0000

Hi Torsten,

Sorry for the delayed response; it seems this got buried beneath some other
things.  Thanks to everyone else for contributing, and I think there's just
one point left that needs a response (inline)...

On Mon, Mar 02, 2020 at 03:19:11PM +0100, Torsten Lodderstedt wrote:
> Hi Ben,
> >> 
> >>> 
> >>> I don't think the new semantics for "jti" in the introspection response
> >>> are compatible with the RFC 7519 definition.  Specifically, we say that
> >>> "jti" will be tied to the input access token, but 7519 says that "jti"
> >>> has to change when the contents of the JWT change ("MUST be assigned in
> >>> a manner that ensures that there is a negligible probability that the
> >>> same value will be accidentally assigned to a different data object"),
> >>> and we admit at least the possibility of "active" and "iat" changing.
> >> 
> >> I think the key word is “accidentally”. This spec causes the AS to purposefully issue JWTs with the same “jti” in order to allow replay detection with respect to the introspected access token. “iat” is changed in order to give the RS an indication and proof when the introspection response was minted by the AS.
> > 
> > I think "accidentally" is just there to emphasize that there's a risk of
> > accidental collision when using a random string as an identifier, since "of
> > course you wouldn't deliberately reuse a token identifier".  This stance
> > seems to supported by "[t]he 'jti' (JWT ID) claim provides a unique
> > identifier for the JWT".  It's really hard for me to read that sentence as
> > allowing the use of a single identifier for two different JWT values, since
> > it then ceases to be a *unique* identifier.
> > 
> > I seem to have forgotten how this replay detection is supposed to work;
> > would you mind giving me a pointer and/or refresher?
> Sure. 
> 1) Let’s assume a client obtains access token “123456789”, which obviously is a handle the RS needs to resolve using Introspection. 
> 2) The RS calls the introspection endpoint, the AS looks up the access token data and responds with a JWT formatted introspection response (including a fresh jti “abc234567”). 
> 3) The RS stores the jti in its replay cache (long with the tokens max lifetime)
> 4) The client calls the RS again using the same token “123456789”
> 5) The RS calls the introspection endpoint again, the AS looks up the access token data and responds with a JWT formatted introspection response (including a fresh jti “abc8912345”).
> 6) The RS compares the jti with its replay cache, no hit - it thinks all is good and performs the requested transaction. 
> But it just accepted the same token for the 2nd time. 
> If the AS would have created JWT formatted introspection responses with the same jti, the RS would had a cache hit in step (6) and refused the request. 
> > 
> >> 
> >> “Active" does not really change, since the introspection response of an inactive token is empty except the “active” element. 
> > 
> > I mean, the token artifact still changes.  What am I supposed to interpret
> > "the JWT" as meaning if not the actual encoded artifact?
> Sorry I don't understand. Can you please elaborate?

I was trying to combine two thoughts here but didn't provide a good
connection.  Consider the test from RFC 7519, "The "jti" (JWT ID) claim
provides a unique identifier for the JWT."  If we are to claim that the JWT
introspection response is a JWT, then the "jti" value contained therein is
supposed to pose a "unique identifier for the JWT [introspection
response]".  But if it is clearly specified that the contents of the
introspection response (a JWT) are to change from having [a bunch of stuff]
to [mostly just "active":false], I can't come up with an interpretation
in which both [a bunch of stuff] and [mostly just "active":false] can be
considered "the same JWT".  In a more general sense, I would treat "the
JWT" as the actual encoded artifact with header, claim set, and signature,
and under that assumption it's clear that any change in the encoded
artifact would be a different "JWT".

IIUC this point is now moot based on the direction we seem to be going in,
but hopefully that helps clarify my thinking.