Re: [OAUTH-WG] Implementation questions around refresh token rotation

Jeff Craig <> Tue, 06 October 2020 22:56 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 97CCF3A152F for <>; Tue, 6 Oct 2020 15:56:58 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -17.598
X-Spam-Status: No, score=-17.598 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, ENV_AND_HDR_SPF_MATCH=-0.5, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001, USER_IN_DEF_DKIM_WL=-7.5, USER_IN_DEF_SPF_WL=-7.5] autolearn=unavailable autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id hzZzDQUWEwWy for <>; Tue, 6 Oct 2020 15:56:55 -0700 (PDT)
Received: from ( [IPv6:2607:f8b0:4864:20::b35]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id A51E93A1527 for <>; Tue, 6 Oct 2020 15:56:55 -0700 (PDT)
Received: by with SMTP id n142so379655ybf.7 for <>; Tue, 06 Oct 2020 15:56:55 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=zdSxZFx4t1KIbQeZI6IHenF9yw0J/j8zfEQFyz9sEvs=; b=gLEXi/G+KQVYocc3UgLph973MXgBerpS8ym2WyOUR3ZGBvsDPY0XnhXvRK3jAdhJ21 /qUGCM7pLFYaYUtLO3RK5Sl2VQcEGKHyZKaZuAk+uSsdtO+LZI3Q4dTITGef0OeHi8ix Int7zKbCC0sLbaZERFW1KKTOoTgqwLJhv06P7VgdN7jCmC5ke5fycsv3BjBuc5xievZA hVYB2r004vrU5O+dK7kZyoj7aSjvrZRyr6k4+G/0nUjWuGAo4zmlEzYT41bLMNzTtZxQ Y5/E5kVhY3gxBssbnSAhNYVkFs9VFCxhFmAfT6a8olIQKPg2kE5VwKY0N9/nF2vmUn8S jK/A==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=zdSxZFx4t1KIbQeZI6IHenF9yw0J/j8zfEQFyz9sEvs=; b=AIyUBF/W6W5ScUa8/YAB33UhE65tEUktQqobB3gFKZmOnqGPqdTl88Rq1EfcWCNOkh oNjwYvYccVgP8LJIZtuVZwfk0JiM2MmwXbXmIUSrbphjqGWQLvnTGY9AAUN67oSrlFmy wLiq16upnvIrRdHYrJIsoQVkL804Qabzm3mVXwwkKgHldFD1lMKDr/mDkkK83VWjxi24 rkt1B6DJw0gTcuL/FV9zbQyffFvpDARxpBbYsBXcilE24YGCwGy9iAyinuQlaxUaB5mw Xta4DvSF/Bu08Gei/WJ8r5KcyPX7Fw7/tTtU2n5yZUPAr0MCNBCKH74HdWLvtOYONsA3 sgNA==
X-Gm-Message-State: AOAM532zAVNJd0dBXYlUJ3lCwltwZ8TxB8EOBfxVqqsUkYTNfIFIxa+9 zt/J9kjBFWTOyir6fr95J6HLiQrkXZrmqmZc02ht0g==
X-Google-Smtp-Source: ABdhPJz2HfkKi1iyk3jUHVtQP1IJ6YjAp9DR8wCrpfdreR392nsdxaoqnn58+r9iGmVBrU4lC+i+d5m9ulmIKHZ+Ano=
X-Received: by 2002:a5b:d44:: with SMTP id f4mr684365ybr.153.1602025014640; Tue, 06 Oct 2020 15:56:54 -0700 (PDT)
MIME-Version: 1.0
References: <> <067801d69c30$02119bc0$0634d340$>
In-Reply-To: <067801d69c30$02119bc0$0634d340$>
From: Jeff Craig <>
Date: Tue, 06 Oct 2020 17:56:43 -0500
Message-ID: <>
Cc: Aaron Parecki <>, OAuth WG <>
Content-Type: multipart/alternative; boundary="000000000000eba5d105b10883f4"
Archived-At: <>
Subject: Re: [OAUTH-WG] Implementation questions around refresh token rotation
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: Tue, 06 Oct 2020 22:56:59 -0000

My experience is more from the Client side of the equation on this problem,
but I do have some thoughts. Our goal is to prevent cases where we lose the
ability to Refresh a Token due to transient issues (which have run the
gamut from network problems to bad software updates on the AS side). Our
use case also does all token handling server-side, so our threat model is
not the same as the mobile application you described. There is a clear
tradeoff in reducing user friction with additional authorization events,
and securing access.

The recommendation my team typically gives people building Authorization
Servers with Refresh Token Rotation is to keep the old refresh token until
they see the new one (which means that there are generally two refresh
tokens valid at any point in time, an unfortunate trade-off). A more
difficult, but potentially plausible implementation would be to hold onto
the older Refresh Token until the newly issued Access Token is used (thus
implying the refresh was successful on both sides).

We aren't trying to protect against multiple in-flight refreshes though
(we've done a LOT of work to attempt to remove that possibility in a
globally consistent manner), we're trying to protect against a network
interruption that prevents the first use of R1, so our assumption is that
R2.1 was completely lost, and only R2.2 matters moving forward. Meaning: R1
is sent, A/R2.1 is dropped in flight, R1 is sent again, A/R2.2 is returned
and stored. Since R1 was seen a second time, we recommend that R2.1 be
ignored in future. Next refresh will use R2.2, at which point R1 should
never be seen again.

The biggest issue that I see with a time-based grace period is that for
many offline tasks, a single refresh failure may be ignored by the client,
and it could be hours before the second refresh attempt using the older
refresh token is made (depending on time of day and what these requests are
being used for), making the grace period low value in that case.

On Tue, Oct 6, 2020 at 5:28 PM <>

> Hey Aaron,
> Auth0 does offer a configurable grace period, during which the “preceding”
> token can be reused.
> I am not 100% sure what we do in the exact scenario you described, and I
> will double check for you, but here’s my intuition.
> The operation redeem(RT_n) should result in AT, RT_n+1. The grace period
> just extends the time in which the operation can occur, but every operation
> should be idempotent. All repeats of that operation within the grace period
> should have the same result, which means that every resulting RT is a
> representative of the RT_n+1 class, hence all valid at the same time. After
> the grace period elapses, RT_n is invalid, and that’s it.
> So, in your example I would consider RT1.1 and RT1.2 as equivalent, as
> they are both representatives of the RT_n+1 equivalence class.
> It would be very hard to do otherwise, given that network operations
> aren’t guaranteed to be concluded in the order they were executed without
> semaphores, and above all the network failures the grace period is designed
> to handle can apply to any of the requests, regardless of the order.
> *From:* OAuth <> *On Behalf Of *Aaron Parecki
> *Sent:* Tuesday, October 6, 2020 3:06 PM
> *To:* OAuth WG <>
> *Subject:* [OAUTH-WG] Implementation questions around refresh token
> rotation
> Hi all, I have a couple questions for those of you who have implemented
> refresh token rotation...
> Have you included the option of a grace period on refresh token use,
> allowing multiple uses within some time window? I'm wondering because a
> grace period where a refresh token may be used more than once would work
> around the problem that has been brought up, of a mobile app accidentally
> using a refresh token more than once during normal operation because
> different threads are unable to coordinate between themselves. However that
> also kind of defeats the purpose since attacks within that grace period
> would be hard to detect. I'm looking for an idea of where people have
> landed on that issue in practice.
> If you have implemented a grace period, then how do you handle expiring
> the additional refresh tokens that have been granted? For example, if RT
> "R1" is used twice, resulting in new ATs "A1.1", "A1.2" and new RTs "R1.1"
> and "R1.2", what happens if "R1.2" is then later used? Would you invalidate
> "R1.1" at that point? If so, why, and if not, why not?
> It would be most interesting to hear practical experience from people who
> have already built refresh token rotation into a system.
> Thanks!
> ---
> Aaron Parecki
> _______________________________________________
> OAuth mailing list