OAuth 2.0 Best Practice


This text is based upon some research done in April 2012, and is copied to oauth.no in June 2015. Since then, the spec has evolved to be much clearer about this.

Not dealing properly with token validity can cause rare and unwanted effects for end-users. You don’t want to do that. This documents tries to sum up the best practice on this.

Estimate the validity period of an obtained token

When the consumer obtains a new access token, it will usually store the token in some storage for potential future use. It is very reccomended to also estimate the validity period for a token when sent to storage, in order to know that the token is expired without the need to “test” it.

At the time the consumer receives an access token response, the validity period of the token can be decided using any of these indicators, in the given order:

  • The expires_in property in the access token response. This value is reccomended to be included by the provider.
  • A special scope indicating validity period of the token. In example a scope offline may be defined to mean infinite validity.
  • A default value of validity period, typically per provider.
  • A default value of the consumer implementation, if none of the above is given. It may be infinite, or it may be a limited period.

Note 1: there is no way to indicate infinite validity period in the expires_in property (unfortunately).

Note 2: Of the indicators listed above, only the expires_in parameter is obtained in a standarized way. All other parameters are somehow configured in the client, based upon textual documentation of the provider API, or out of band agreements.

Detecting an expired tokens when using it

Even if you follow best-practice to associate a stored access token with a validity period, for a number of reasons you also should be able to deal with the fact that the token is invalid even if you assume it is valid:

  • the OAuth spec does not provide measures to protect against clock skew
  • as all of the indicators (except expires_in) are defined in documentation, out of band or manually entered, if the default value of the provider changes, the consumer is unlikely to adopt to the new validity period immediately.
  • the token may be invalidated by other means at the provider than expiration. There is no mechamisms built in for notifying the consumer about a token being invalidated.

Consequently, a consumers will have to deal with the situation of using an expired token.

First, it is extremely important that the consumer is able to detect the difference between generic errors at the protected endpoint and errors due to an expired token. If and only if the token is expired it is very important that the consumer uses the refresh token or starts a new flow to obtain a new valid token.

The OAuth 2.0 Core spec does not mention with a single word how to detect expired tokens. Instead this is discussed in each of the token type specs, including the popular Bearer Tokens.

For the bearer tokens, an expired token will result in the following response from a protected endpoint:

HTTP/1.1 401 Unauthorized 
	WWW-Authenticate: Bearer realm="example", 
		error_description="The access token expired"

The consumer should check for both the status code 401, and that the error field is set to invalid_token before requesting a new token from the provider.


Facebook claims to be using OAuth 2.0 for its graph API. OAuth 2.0 library writers will have to balance facebook and spec compliance with care.

When a consumer uses an expired token, Facebook responds with:

HTTP/1.1 400 Bad Request
	WWW-Authenticate: OAuth "Facebook Platform" "invalid_token" 
		"Error validating access token: Session has expired 
		at unix time 1334415600. The current unix time is 1334822619."

It could be tempting to treat all 4XX responses as expired tokens, but that is not reccomended.

Instead I would provide a specific fallback for Facebook, like this:

if (status_code === 401 and (parsed_www_authenticate["error"] === "invalid_token"))
    throw new InvalidTokenException();
if (status_code === 400 and 
        (www_authenticate contains 'OAuth "Facebook Platform" "invalid_token"'))
    throw new InvalidTokenException();

Other broken providers

If you know about other popular providers that do not follow the specification on expired tokens, contact me and I’ll adjust this document.