node-solid-server icon indicating copy to clipboard operation
node-solid-server copied to clipboard

Re-consider access token lifespan

Open timea-solid opened this issue 4 years ago • 6 comments

At the moment NSS defines a default 14-day token lifespan: https://github.com/solid/oidc-op/blob/main/src/AccessToken.js#L7 Why re-consider?

  • a long life for NSS access tokens affects the entire Solid ecosystem. If another server (e.g. ESS, CSS) wants to support NSS access tokens, it needs to accept tokens with a very long life.
  • Bearer tokens (which is what we're talking about) are global in nature for Solid. They are extremely powerful and so any exfiltration can have significant negative consequences (i.e. a user loses data to a malicious actor). Reducing the lifespan of a global access token reduces risk for everyone
  • for access tokens (especially global access tokens) the industry standard is <= 1 hour. For access tokens that secure "sensitive" data, the standard is closer to 10 minutes or less

Suggestions:

  1. Request a refresh token during the authorization code flow (this is part of OpenID Connect). Then, when the token is nearing expiry, exchange the refresh token for a new access token
  2. Completely refresh the app, passing the user through the full authorization code flow (part of OIDC). There will still be a cookie available on the identity provider, so the user will, in all likelihood, be passed right through the redirect flow and end up where they started. There are patterns for maintaining state between the start and end of that flow
  • If the cookie expired on the Identity Provider, then the user would need to re-enter credentials
  • NSS uses cookies so we could exchange the access token with a cookie. Example: Gmail uses cookies. Cookies are easy to scope to a particular app, so there are no significant security issues.

timea-solid avatar Sep 16 '21 13:09 timea-solid

What happens with ESS at the moment (say with the Pod Browser)? @acoburn

timbl avatar Sep 16 '21 15:09 timbl

At the moment, in order to support access tokens generated by NSS, an ESS instance must accept access tokens with a 14-day lifespan. But an operator of ESS may choose to restrict this to more common industry standards, such as 1 hour. In fact, if an instance of ESS (or any Solid server implementation) contains sensitive or private data, it is quite likely that an operator would choose to restrict access tokens to a shorter maximum lifespan, which would necessarily cause all NSS access tokens with this default lifespan to be rejected.

In this context it is important to distinguish between what a user can do with a global access/ID tokens and what they can do with application-scoped session tokens, such as cookies. A global access token is extremely powerful. It allows a user in possession of one to do anything in the entire Solid ecosystem for as long as it is valid. That means that any exfiltration of the tokens is potentially catastrophic.

There are two ways the power of these tokens is constrained: (1) binding these tokens to keypairs, i.e. DPoP and (2) limiting the lifespan of a token.

Even with DPoP, if both the access token and the DPoP proof are exfiltrated from an application, a malicious user can replay that token/proof combination against a single endpoint repeatedly, provided that a server does not enforce jti claim uniqueness. In that case, a shorter lifespan of an access token becomes vitally important.

I see less of a problem with long-lived access tokens if those tokens are scoped to a particular Pod and if those tokens are not accessibly to JavaScript (i.e. httpOnly cookies), especially if that Pod is not storing sensitive data. But expecting that every Solid server will accept access tokens of arbitrary lifespans will likely not pass a rigorous security review.

acoburn avatar Sep 16 '21 17:09 acoburn

@acoburn - thanks for the helpful explanation. What is a more reasonable expiration time? Is there a process for automatically refreshing the token on expiration that is missing from NSS?

jeff-zucker avatar Sep 16 '21 17:09 jeff-zucker

What is a more reasonable expiration time?

A common value that I see is 1 hour (e.g. Google, Auth0, Cognito). CSS also issues tokens with a 1 hour lifespan. ESS issues tokens with a 30 minute lifespan. I also see tokens with a shorter lifespan, down to 5 minutes, especially in cases where sensitive user data is present. Outside of NSS, I don't believe I have ever encountered a system in the wild that issues access tokens with a lifespan > 1 hour.

Is there a process for automatically refreshing the token on expiration that is missing from NSS?

I can't speak to what may or may not be missing in NSS, but there are several standard OAuth2/OpenID ways that tokens can be refreshed:

  • Use refresh tokens: when requesting the offline_scope at an OpenID authorization_endpoint, a client app can send the refresh token to the identity provider's token_endpoint to request that a new access token be issued.
  • Redirect the user through the authorization_code flow. There is typically some type of state that will need to be restored in this flow; for that, an app can place the state in session storage (or another convenient location) and use that state to restore data for the user. Alternatively, this state can be placed in the state parameter of the OpenID interaction

(historically, browsers would use an iframe-based approach, but recent browser security changes have made that approach no longer workable)

All things considered, the better approach IMO is to use refresh tokens as that is much less disruptive to how users interact with browser-based apps. The browser refresh flow requires navigating away from the active page and if a user was, for example, typing into a text box, it would be a very non-optimal user experience.

An alternative to the above is to fetch a global access/ID token from an identity provider and then exchange that token for an application- or pod-specific token, which could be a cookie. That pattern is also in line with some ongoing conversations in the Solid Authentication panel about scoping access tokens to particular resource (i.e. Pod) servers.

acoburn avatar Sep 16 '21 18:09 acoburn

@acoburn I'm a bit embarrassed to ask but do you mean scope=offline_access?

SharonStrats avatar Sep 17 '21 11:09 SharonStrats

Yes, offline_access. I should have checked the spec text :-)

acoburn avatar Sep 17 '21 12:09 acoburn