6

OAuth's access token/refresh token flow seems wildly UN-thread-safe to me. Help me understand it better.

Let's say I'm integrating with an API that leverages OAuth (like this one). I have my access token and I'm making API calls -- all is well in the world. But then my access token expires, and I need a new one. No problem, I use the refresh token that I was issued, and I get a new one.

Everything above sounds fine and dandy... But not in a multi-threaded world. Meaning, if the above actions all occur twice at the exact same instance on separate threads (e.g., two users request an API call simultaneously against the same object), and there can only ever be ONE access token alive at any given time, then won't one cancel out the other? And in a highly-transactional app wouldn't this happen a lot.

I have a strong feeling that this is a dumb question, but I can't wrap my brain around how this can be thread-safe.

filmnut
  • 746
  • 1
  • 7
  • 16
  • Well, multi-threading is one issue. Problems is also, when the first `refresh_token` reponse is lost somewhere on the net, then next `refresh_token` call may return `invalid_grant` if the servers revokes the refresh token on each call. (Spec. permits that.) And yes, it's unfortunate the spec allows. such non-thread-safe, non-error prone behaviour. – xmedeko Apr 08 '19 at 12:15

2 Answers2

7

Oauth is a protocol. It depends on a particular implementation whether or not that implementation is "thread safe".

Oauth2 != Oauth: How is OAuth 2 different from OAuth 1?

And REST APIs (like the one you cited) are inherently stateless, so there's really no question of "thread safety".

Finally, here's a good discussion on how to share an OAuth2 credential (that is, once you've established the credential) between multithreaded applications:

Optimizing OAuth 2.0 Requests

In multithreaded applications, the credential should be shared between threads. Refreshing of the credential should be performed synchronously to avoid a race condition.

The client libraries make sharing a credential across threads straightforward. Each client library has a session (or user) object which is constructed with a credential that it reuses throughout its lifetime. To share the credential across threads, simply construct each session using the same credential. In all client libraries, the credential is a thread-safe object and refreshes itself synchronously when its access token expires.

For example, in the Java client library, you would create a Credential as a singleton and share it across all sessions.

Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 2
    Thanks! That link is very helpful, especially this part: "In multiprocess or distributed applications, sharing the credential requires you to persist it. To ensure that multiple processes or servers don't attempt to refresh the credential at the same time, resulting in excessive refresh requests, we advise proactively refreshing the credential in a central place and sharing it across processes/servers." – filmnut May 07 '16 at 05:24
2

I have some issues with oauth grant_type password flow.

When my app make a request to a protected resource it, using a ExchangeFilterFunction in a spring WebClient, make a request to obtain a access_token. If access_token is expired the app make a new request.

The problem is: in my implementation, if a thread detect a expired access_token it make a request to obtain a new token, in the meantime, other threads will do the same and N threads may at the same be trying to get a new access_token.

The fastest and most primitive way to solve this problem is blocking (e.g. syncrhonized keyword in java) the code snippet that gets a new token from the other threads, in this way only one request is made, but this will block all threads. When the first thread receives the new token, the other threads will be released, but now they will no longer need to make the request because they will detect a valid token.

As stated earlier, this is an implementation-specific tweak. I don't know if spring-security takes that care, but as far as I know, there's nothing in the oauth protocol specifying how to handle this.