18

Per Google's docs it would seem refresh tokens are only necessary for offline applications (applications that may run into an expired access token when the user isn't around).

Access tokens periodically expire. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.

...

Requesting offline access is a requirement for any application that needs to access a Google API when the user is not present. For example, an app that performs backup services or executes actions at predetermined times needs to be able to refresh its access token when the user is not present. The default style of access is called online.

However, a description of refresh tokens in general and this question in particular both seem to imply that refresh tokens are needed anytime you want to request a new access token.

I think I would agree with Google's explanation and not use refresh tokens. My experience with OIDC providers has been that refresh works as follows:

  1. User requests protected resource from client server
  2. Client server determines access token has expired.
  3. Client server redirects user to OP auth endpoint
  4. OP authenticates user without interaction due to cookies stored on user's browser with OP's domain.
  5. Client server finishes the request.

The user might see a few redirects but other than that the re-authentication went by without any interaction from them. Given this, is it necessary to bother with refresh tokens if the user will always be present at the application?

Community
  • 1
  • 1
Pace
  • 41,875
  • 13
  • 113
  • 156

4 Answers4

9

My biggest concern with using refresh tokens for online apps is that it takes away transparency from the user.

Refresh tokens facilitate long term access and should be stored safely. But they also don't provide a natural way to "sign out", and (most importantly) it becomes completely opaque how, when and from where your data is accessed, as the often used scope name offline_access suggests.

OIDC offers a front channel mechanism prompt=none that largely leads to the same effect (i.e. new tokens), and without needing intermediate redirects if the re-authentication is performed inside an iframe.

Hence in my opinion you and Google are right and the answer must be: No, don't use refresh tokens if the user is present.

Pieter Ennes
  • 2,301
  • 19
  • 21
  • Everyone had good input and I appreciate the discussion. This answe is the most straightforward and most in line with the spec and my own research so I am awarding the bounty here. I wish I could spread it out more but that's the rules. – Pace May 11 '17 at 01:22
6

No, it is not necessary to bother with refresh tokens if the user will always be present at the application. The reasoning is largely the OP describes.

But there are reasons why one may still want a refresh token:

  • as the OP mentions the user might see a few redirects and both the UI expert and the branding guy on your team will hate this
  • when an access token expires in the middle of an HTML Form POST action, the redirect may have lost the context/POST-data on return; you may want to minimize this or you'll have to take appropriate (complex) POST-data-save actions
  • if your access token expiry is really short, the redirects create a lot of overhead and nuisance; you may not be able to control access token expiry when dealing a Providers in a different domain and when dealing with multiple Providers it will vary across them
  • when refreshing the access token with a redirect your application now depends on the Provider keeping an SSO session; not all Providers may do this and if they do they may do it in different ways: the SSO session duration may vary between them and the authentication method may vary; as an example: a Provider that doesn't keep an SSO session but does use 2-factor authentication will have large impact on the user experience

Imagine a scenario where you want to use the access token to update user information in almost real-time from the user info endpoint but the access token expiry is relatively short. Either you'll have to perform a lot of redirects with the nuisance as described, or you can use a refresh token.

Hans Z.
  • 50,496
  • 12
  • 102
  • 115
  • Your first point alone is pretty significant. I work with an Angular 2 SPA and it loads a significant amount of data from the server which it caches in memory. Every click then has to request a bit more from the server but not the full set of data. The second point should hopefully be taken care of by your OIDC client library but I suppose not always. The third and fourth bullet points are less convincing to me. If a provider was that paranoid they'd probably disable refresh tokens anyways. – Pace May 04 '17 at 06:39
  • well the 3rd bullet doesn't have to do with paranoia and wrt. the 4th: refresh tokens actually give the provider the ability to apply access/entitlemen control on a regular basis (just like reauthentication would) – Hans Z. May 04 '17 at 06:47
  • First point is a bit stretched, because your session lifetime can differ from access_token/id_token lifetime.If there is a SPA app - then you just use Implicit flow, and refresh access token via authorize request in hidden iframe. https://github.com/IdentityModel/oidc-client-js this client can handle it for example. Second point is also a bit invalid, because access_token lifetime is usually checked against some threshold before sending it to OP/Api.Third one - basic lifetime usually is 15 mins. Lover lifetimes for most cases are not needed, and if you need that, than it is a matter of balance – Aleksei Anufriev May 05 '17 at 13:12
  • Forth point - you can have your session with your lifetime. Basically you treat successful token grant as authorization to start your session. So it depends on OP and your needs - but refresh is also can be omitted. As for scenario - userInfo provides claims about user, not session, and this data is not a subject for frequent change. Just in case you will rise an argument with user blocking or something - there is a specific spec for shutting down user sessions from OP http://openid.net/specs/openid-connect-session-1_0.html. This theme is to big for comments section ;) Feel free to contact me. – Aleksei Anufriev May 05 '17 at 13:21
  • 1
    @Alexy Anufriyev I don't agree that a SPA implies you need the implicit flow. I think the implicit flow is only for situations where there is no server. I feel I shouldn't sacrifice security by using the implicit flow simply because I want to avoid using refresh tokens. – Pace May 08 '17 at 15:24
  • Implicit flow is for clients, that cannot store secrets securly, like SPA apps. Section 3.2 of spec. So they recieve all of their stuff from authorize endpoint and can silently update it quite often. If you have specific attack vector in mind - we can discuss it :) – Aleksei Anufriev May 08 '17 at 23:34
  • 1
    @AlexeyAnufriyev My understanding was that the implicit flow requires trusting the user agent and should only be used if there is no server. Meanwhile the authorization flow does not require a trusted user agent. In the implicit flow a corrupted user agent could make requests on the user's behalf by intercepting the access token. My SPA app cannot store secrets security but it communicates with a server that does and so even though my application is a SPA I can use the authorization code flow. – Pace May 09 '17 at 03:54
  • User agent is not trusted. You only expose access and id tokens there. If you care about exposure of data, there are reference access tokens. On other hand Code Flow requires you to trust client and its ability to store secrets(because we dont see em in github commits, arent we? :)). Implicit flow does not use client secrets at all. Because it cannot store them in secure manner so it is untrusted. Also you have to secure backchannel with Code Flow, and it can be an issue in environments that have two leg ssl and log some data for example or IT stuff is not expirienced. – Aleksei Anufriev May 09 '17 at 08:37
  • One can use the Authorization Code grant type also with public clients i.e. no client secret is required. That has an impact on security ad mentioned in the comments. – Hans Z. May 09 '17 at 16:03
  • Why is nobody mentioning a `prompt=none` flow in an iframe? Because that would mitigate almost all of the points, provided the Provider keeps an SSO session. – Pieter Ennes May 09 '17 at 21:53
  • Then it goes to hybrid flow, and there should be some reasons to do that. One of them to limit number of redirects/calls, like code id_token response_type. But what connection it has with refresh tokens ? :) – Aleksei Anufriev May 09 '17 at 21:54
  • 1
    @PieterEnnes JS lib I mentioned in my answer doing it by default :) – Aleksei Anufriev May 09 '17 at 21:59
  • @HansZ. Also you can't use refresh_token grant with public clients. So you either get id and access from authorize endpoint, or go to token endpoint and do the same. – Aleksei Anufriev May 09 '17 at 22:15
  • @PieterEnnes agree on the use of an iframe to improve the UX but it doesn't take away most of the points and brings its own problems; it deserves to be mentioned though. – Hans Z. May 09 '17 at 22:59
  • @HansZ. Only point left is a reliance on OP session. In most cases it will be in place and if you have that rare case - there is an option to run your own and federate to target OP with options like MITREid or IdentityServer. Sidenote: discussing in context of Public client/SPA app. – Aleksei Anufriev May 09 '17 at 23:37
  • @Pace this case is also possible. But if user agent is compromised it can also leak a session cookie :) – Aleksei Anufriev Aug 01 '17 at 17:57
3

Refresh token is essentially a credential reference, that your client can exchange for access token, when there is no active user session. For example if you want to periodically sync issues from Github with your inhouse system.

It is often misused like some kind of session. It is essential to differentiate those things. And scope name offline_access is there for a reason.

So in simple cases - you just rely on OP session and get new token with authorize/token endpoints combo. You should not be prompted to provide credentials as long as session is alive and consent is given for that particular app.

If you need to do some background stuff - ask for refresh token also.

As for question: no.

EDIT (more in-depth explanation): if we are talking about web there are two main cases:

Client that can securely store secrets like usual web app with server page rendering and clients, that cant store secrets, like SPA apps. From that perspective there are two main flows (omitting hybrid to not over-complicate): Authorization Code Flow and Implicit Flow respectively.

Authorization Code Flow

On first request your app checks it own session(client session) and if there is none - redirects to external OP(OpenID Connect provider) authorize url. OP authenticates user according to requirements expressed in request, gathers consent and other stuff and returns authorization code. Then client asks token endpoint with it and receives access_token/id_token pair with optional refresh token if user granted offline access consent. This is important, because user can deny it for your app. After this client can request userInfo endpoint to get all user claims that were granted during consent. Those claims represent user identity and do not contain stuff like authentication method, acr etc. Those claims present in id_token alongside with expiration for example. After that client starts it own session and have option to set its lifetime equal to id_token lifetime or use it own to provide smooth UX for example. At this point you can discard access_token and id_token at all if you don't need access to other APIs(like all scopes in access_token are specific to OP and subject). If you need access to some API you can store access_token and use it for access. It becomes invalid - redirect to OP for new one. Expiration can be more lax here, because of more secure environment on server. So even 1hr is an option. No refresh tokens used at all.

Implicit Flow

In this case your lets say Angular app redirects to OP, gets its id_token and optional access_token from authorize endpoint directly and uses it to access some APIs. On every request expiration is checked an if needed, client sends request to OP in hidden iFrame, so there won't be any visible redirects as long as OP session is alive. There are some great libs for that like openid-client.js. No refresh is allowed here at all.

It is important to differentiate client session from OP session, token lifetime and session lifetime.

To address some specific needs there is Hybrid Flow. It can be used to get authorization code and id_token for your session in one request. No chit chat over network.

So when you think about refresh token just check your needs and map them to a spec :) And if you need it anyway - store it as secure as you can.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Aleksei Anufriev
  • 3,206
  • 1
  • 27
  • 31
1

Refresh tokens are useful for applications that keep access tokens in a server session. For example if a web application doesn't call a protected service using JavaScript XHR, but calls its backend and the backend calls the service. In this scenario, it's easier to get a new access token whenever it's needed than asking a user for a new one.

In JavaScript applications running in browsers, refresh tokens cannot be used, because you need a client secret to get an access token from the /token endpoint and you cannot keep the secret safe in such applications.

The process for getting new access tokens you described can be improved - an application may ask for a new access token just before the current one expires, so the user doesn't get redirected to the OAuth2 server, but the application calls the /auth endpoint with prompt=none parameter in an iframe.

Ján Halaša
  • 8,167
  • 1
  • 36
  • 36
  • Refresh tokens should not be used alongside active session. If your app is doing code flow - just redirect to OP and get new pair of tokens. – Aleksei Anufriev May 03 '17 at 23:31
  • Alexey, what's wrong in keeping refresh tokens in an application backend session? Asking for new tokens every time adds complexity and I cannot see any security benefits in that either. – Ján Halaša May 05 '17 at 12:24
  • It does not add complexity. It is transparent for application. You should not store refresh tokens in session. They should live in secure persistent storage if you need access to apis, protected by OP, when there is no active session. – Aleksei Anufriev May 05 '17 at 12:56