connect-js icon indicating copy to clipboard operation
connect-js copied to clipboard

Transparently refreshing access tokens

Open tomkersten opened this issue 9 years ago • 4 comments

Problem: When an access token expires, we must get a new one so the user can both remain logged in and make requests to authorized service endpoints.

Ideally:

  1. The process of refreshing should be invisible to the user. If an application "requires authentication" on all routes and auto-redirects to the anvil server, it will get a new token back (via the normal auth flow). However, this is obviously not using the "refresh" token that is in place...and it causes a redirect & full page refresh.
  2. Refreshing should not take place as part of a user-initiated task. Ideally the token would be "auto-refreshed" prior to its expiration to prevent subsequent requests to service endpoints the application depends on from failing. It would be relatively trivial to monitor outgoing request and refresh only when necessary. However, I feel this is less than ideal because:
    1. The next service API call would incur the cost of the refresh request in addition to the API service request itself.
    2. If multiple API calls were to take place in parallel, I believe a race condition/error would present itself at some point (one succeeds, one does not...how does failed request recover).

Any ideas on how to handle this? (my primary concern with this is in the context of an AngularJS application...but feel free to interpret outside that context as well if desired)

tomkersten avatar Jul 18 '14 17:07 tomkersten

Problem: When an access token expires, we must get a new one so the user can both remain logged in and make requests to authorized service endpoints.

  1. The process of refreshing should be invisible to the user.
  2. Refreshing should not take place as part of a user-initiated task.

Unlike the Authorization Code Flow, OpenID Connect and OAuth 2.0 make no provision for refreshing tokens via the Implicit Flow implemented by this library. This is because there is no secure way to store and send the client credentials required to refresh a token from the browser. The only way to get a new token is to re-authenticate via the implicit auth flow.

If I understand correctly, the issue you're experiencing is that the session expires too quickly, requiring users to re-authenticate more frequently than desirable.

In fact, there may be two underlying issues:

  1. Anvil Connect needs to support configurable expiration settings (client default_max_age property and max_age authorization parameter?) and probably a more sensible default. Perhaps two weeks or 30 days instead of one hour. This needs to be addressed in the server code itself.
  2. The browser client code in this repository should provide your application code with the means to know whether or not a session is expired, so that your app can prompt the user to re-authenticate when necessary or desirable, preferably before any action that would result in a failed API request.

As you mentioned, there are a few ways to approach this (2) in AngularJS, and more than one may prove useful:

  • $watch the session expiration value and initiate authorization when the session expires
  • resolve routes by checking that there is a valid session and it is not expiring within some threshold
  • request interceptors that check session expiration status prior to making a request
  • response interceptors that handle 401/403 statusCodes
  • Anvil.request could manually check the expiration and fire an event/reject a promise

@tomkersten Would having the ability to set a session's expiration much farther out in the future effectively resolve this issue for you?

christiansmith avatar Jul 19 '14 13:07 christiansmith

Ah. Ok. I see the issue, I hadn't put the pieces around the client secret together before...

Setting the expiration much further would definitely help, but woud not "resolve" the issue. Resolution on the client-side would require a way to transparently prompt/redirect for a new access token (via Implicit Flow).

A thought: Potentially making it simple to force initiating the generation of a new token within X-minutes/seconds of it's expiration. If it were possible to take "recent activity" into consideration, that would be great as well.

For example: Assuming you could set a token's expiration for, say, 30-days. If you could say "at some point within 48-hours of expiring the token, if the user is inactive for more than an hour, require a login (trigger Implicit Flow) before loading a page." Something like this would feel relatively transparent to the user, I think, while still making sure you have an "unexpired token". In the event that the "inactivity criteria" is never met (dashboard-type account/app constantly making requests), a redirect would be forced when a token is expired (403 response received).

Thoughts on this approach?

For now, I can see how far the extended expiration gets us...but I'd be curious as to what your thoughts are...

tomkersten avatar Jul 22 '14 17:07 tomkersten

We definitely need to support some kind of prompt to reauthenticate. I like the idea of anticipating the need. Do you have any ideas about how to measure user activity efficiently?

christiansmith avatar Jul 22 '14 17:07 christiansmith

IDK. The simplest thing that comes to mind is recording a timestamp whenever an Anvil request takes place in localstorage. I'd have to think a bit more to come up with anything fancier than that...

tomkersten avatar Jul 22 '14 18:07 tomkersten