Avoiding false positives in OAuth 2.0 refresh token theft detection (2024)

This article explores a specific edge case that can happen when OAuth 2.0 authorization servers use rotating refresh tokens to detect refresh token theft. That’s a mouthful, so let’s explore those ideas one by one.

What is OAuth 2.0?

OAuth 2.0 is a framework for authorization on the web, where a user can give one service (known as the client) access to data stored in another service (known as a resource server). The framework explains how to do a three-way “handshake” of sorts, where the user grants access via an authorization server and the client obtains an access token. The client then can use that access token to make authorized requests to the resource server (a deeper explainer of OAuth 2.0 is beyond the scope of this article, but here’s a really good explanation by Aaron Parecki).

What are refresh tokens?

Because access tokens can be used to access protected resources, they are usually short-lived (think lifetimes measured in hours). Refresh tokens are a way for an app to get a new access token without re-prompting the user to grant access. The client sends the refresh token to the authorization server in exchange for a new access token. This is often referred to as “offline access” because it allows the app to continue acting on the user’s behalf even when the user is not present.

Why rotate refresh tokens?

If refresh tokens never expire, then a malicious actor with a stolen refresh token can easily get persistent access to the token’s resources. But if refresh tokens do expire, then apps that should have persistent access to certain resources will need a way to do that. Enter: token rotation. Each time the app uses a refresh token, the authorization server issues a new access token and a new refresh token (with a new expiration time). The authorization server then invalidates the refresh token that was just used, since it’s not needed anymore. This has a few benefits:

  • It allows apps to have persistent access without relying on non-expiring tokens. Yay!
  • It also allows the authorization server to invalidate refresh tokens more often — the old refresh token is invalidated each time a new refresh token is issued, which happens frequently since access tokens have short lifetimes. This reduces the window of time in which a stolen refresh token is useful.
  • Lastly, using a clever technique, the authorization server can detect stolen refresh tokens to further mitigate abuse. Let’s explore that next.

Detecting stolen refresh tokens 🕵

As described above, when refresh token rotation is used, each refresh token should only be used once. Because of that, if the authorization server receives multiple “refresh” requests with the same token, it can assume that one of those two requests was a malicious actor with a stolen token. There’s no way to tell which request was the invalid one, but it can then invalidate both requesters’ tokens - in other words, it invalidates the whole “grant”, requiring the user to reauthorize the client. This stops the attack by invalidating any tokens the malicious actor had stolen from that grant. (See Section 4.14.2 of the current “OAuth 2.0 Security Best Current Practice” document for more about this technique.)

Side note: How clients do refreshing

In practice, there are at least two ways for a client to use refresh tokens.

  • Eagerly: The client uses the refresh token as soon as (or right before) the access token expires, using some kind of recurring refresh job. If access tokens last 1 hour, this means refreshing tokens every hour.
  • Lazily: When the client needs to use an access token, it checks its expiration time. If it’s expired, it refreshes it, stores the new tokens, and then continues as normal.

Many clients choose the lazy approach because (1) it’s easier to implement and (2) it can be less resouce-heavy, because if the access token isn’t used for a while, it won’t be refreshed until it’s needed again.

Where things go wrong: False positives 💥

Maybe you can already see where I’m going with this. The theft detection strategy described above causes a false positive if a legitimate client refreshes a token multiple times. This can easily happen when (1) a client uses lazy refreshing described above and (2) the token is sometimes needed for multiple things at the same time, such as if it’s used by multiple end users at once, or if an end user does two actions concurrently. For example, imagine a web app that uses OAuth2 to load some data. If the user has multiple tabs open at the same time, each tab tries to request the data, each request invokes a refresh (because the current access token is expired), and whichever refresh happens second then triggers the theft detection, revoking the app’s access. Here’s a step-by-step walkthrough of how the problem happens (made in Lucidchart!):Oddly, the current OAuth2 Security BCP doc doesn’t mention this risk, but the older OAuth2 threat model RFC mentions it offhandedly: “This [theft detection] measure may cause problems in clustered environments, since usage of the currently valid refresh token must be ensured.” In addition, refresh token rotation can cause problems even without the theft detection technique. If a refresh token is used, but the response never makes it to the client (e.g. the network fails to deliver the response), then the client is left with an invalid refresh token and no recourse except asking the user to re-authorize.

Mitigation for clients

To mitigate this problem from the client’s side, you’ll need some kind of locking or mutex around the refresh token. Whenever you refresh a token, you set a lock that tells other threads/flows/etc to wait until it’s refreshed. As database locking tends to be, this can be finicky. You have lots of edge cases to consider. What happens if the process crashes? Or the refresh request fails? Or times out? Or…? If you’re building a generic OAuth2 client system, you probably ought to handle this. But if you’re building an OAuth2 provider (i.e. the authorization server), you should think twice before asking clients to do this. There’s a better way!

Building better authorization servers 💡

To solve the false-positive problem on the authorization server’s side, add a small grace period to refresh token refreshes. After a refresh token is used, for a short window of time, allow it to be used again (and return the same new tokens as the first time it was used). In other words, make the request “idempotent” during that window of time. The window should be no more than 60 seconds or so. Because the “refresh-and-store-new-tokens” process hopefully only takes a few seconds, a minute is plenty of time to allow concurrent requests to settle. This does weaken the breach detection slightly, but it’s very slight. A malicious actor would have to guess when the true client is going to refresh the token. And even if they guess successfully, as long as you give both of them (the malicious actor and the true client) the same new tokens, then the malicious actor will have to make another lucky guess the next time the client refreshes its tokens. (Note: Due to the tighter security requirements of public clients, you might decide to shorten or disable the grace period for those clients.) Although this idea isn’t formalized in the spec yet, several authorization servers support some kind of grace period on refresh tokens, including Auth0, Okta, Fitbit, Slack, and Lucid, which we’ll discuss further next (I’ve also found it in Fauna’s auth “blueprints” code and the Django OAuth toolkit library).

How we do this at Lucid

Lucid's REST API uses OAuth 2.0 with refresh tokens, so we mitigated this edge case in the authorization server to make it easier to build integrations. Whenever a refresh token is rotated, we store the new tokens in an encrypted cache with a short TTL. We then return the tokens from the cache if the refresh token is reused within the grace period. This mitigates the problem for clients without introducing too much complexity in our authorization server implementation.

Conclusion

Security is a constant balancing act between risk and usability. Industry standards like OAuth 2.0 are constantly evolving as risks emerge, usability requirements change, and the industry learns as a whole. The best solutions are ones that give benefits on one side of the balance with no harm to the other. If you’re careful about it, implementing theft detection for refresh tokens can be that kind of idea—it can increase the security of your OAuth 2.0 system, and be completely transparent to the clients consuming your APIs, no matter how they’re architected. Interested in getting started with Lucid’s APIs? Check out our new developer platform! This post was originally published on Dan Mercer’s blog.

Avoiding false positives in OAuth 2.0 refresh token theft detection (2024)
Top Articles
A must-have product for any detailer. The IPA 50/50 - SweetCars
Permanently end your Google payments service
Fat Hog Prices Today
123 Movies Black Adam
Driving Directions To Fedex
Call of Duty: NEXT Event Intel, How to Watch, and Tune In Rewards
Urinevlekken verwijderen: De meest effectieve methoden - Puurlv
Large storage units
Sitcoms Online Message Board
Shariraye Update
Power Outage Map Albany Ny
Nonuclub
Youravon Comcom
Comics Valley In Hindi
Florida History: Jacksonville's role in the silent film industry
1773X To
Vanessawest.tripod.com Bundy
Persona 5 Royal Fusion Calculator (Fusion list with guide)
Fsga Golf
The Old Way Showtimes Near Regency Theatres Granada Hills
Like Some Annoyed Drivers Wsj Crossword
Talkstreamlive
Knock At The Cabin Showtimes Near Alamo Drafthouse Raleigh
Drift Hunters - Play Unblocked Game Online
EVO Entertainment | Cinema. Bowling. Games.
Cal State Fullerton Titan Online
Shiny Flower Belinda
Maths Open Ref
Alternatieven - Acteamo - WebCatalog
Eegees Gift Card Balance
Primerica Shareholder Account
Willys Pickup For Sale Craigslist
Rubmaps H
Cbs Trade Value Chart Week 10
Top-ranked Wisconsin beats Marquette in front of record volleyball crowd at Fiserv Forum. What we learned.
Is Arnold Swansinger Married
Red Dead Redemption 2 Legendary Fish Locations Guide (“A Fisher of Fish”)
How to Print Tables in R with Examples Using table()
Lonely Wife Dating Club בקורות וחוות דעת משתמשים 2021
Vérificateur De Billet Loto-Québec
Craigslist Rooms For Rent In San Fernando Valley
Cabarrus County School Calendar 2024
Playboi Carti Heardle
Dietary Extras Given Crossword Clue
Blog Pch
What Time Do Papa John's Pizza Close
Rubmaps H
O'reilly's On Marbach
How to Get a Check Stub From Money Network
Used Curio Cabinets For Sale Near Me
Latest Posts
Article information

Author: Annamae Dooley

Last Updated:

Views: 5673

Rating: 4.4 / 5 (45 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Annamae Dooley

Birthday: 2001-07-26

Address: 9687 Tambra Meadow, Bradleyhaven, TN 53219

Phone: +9316045904039

Job: Future Coordinator

Hobby: Archery, Couponing, Poi, Kite flying, Knitting, Rappelling, Baseball

Introduction: My name is Annamae Dooley, I am a witty, quaint, lovely, clever, rich, sparkling, powerful person who loves writing and wants to share my knowledge and understanding with you.