uPortal-web-components icon indicating copy to clipboard operation
uPortal-web-components copied to clipboard

Open ID Connect: Cache token

Open ChristianMurphy opened this issue 7 years ago • 10 comments

Context

Single Page Applications want to minimize unneeded network traffic.

Issue

The OIDC helper will generate a network request each time it is called. With a large volume of components on the page this can generate many requests which many not be needed.

Potential Solutions

Caching, keep a copy of the token and reuse the token while valid. The could be done:

  1. In the Open ID Connect (OIDC) helper library
  2. In a High Order Component (HOC)
  3. Network library plugin (E.G. https://github.com/axios/axios#interceptors)
  4. Other?

and could be managed though

A) Timeout based cache clearing, after some number of seconds clear the cached token, next request will need to make a network call B) Expiration based clearing, use expiration given by token to clear token at exact time it expires C) Refresh when not authorized (see 3) D) Other?

Risks

  1. Adds weight to library
  2. Would need an implementation for each language
  3. Would require implementation for each network library

A) Timeout could happen after token expires, leaving UI unauthorized in the mean time B) Timezone and Clock Skew would need to be accounted for C) Could result in a lot of extra traffic if 403 not authorized is used regularly during valid sessions

ChristianMurphy avatar Jul 13 '18 20:07 ChristianMurphy

mhhh have a look on this documentation, this could help too: https://auth0.com/docs/security/store-tokens. tokens could be saved in session/local storage !

jgribonvald avatar Jul 16 '18 07:07 jgribonvald

Thanks @jgribonvald! That's a good resource.

A few notes after reading it.

The article states

:warning: You must verify a JWT's signature before storing and using it.

and jwt decode highlights

IMPORTANT: This library doesn't validate the token, any well formed JWT can be decoded.

Which means it would require reverting #21 .

The article also notes

Web Storage is accessible through JavaScript on the same domain so any JavaScript running on your site will have access to web storage, and because of this can be vulnerable to cross-site scripting (XSS) attacks.

ChristianMurphy avatar Jul 16 '18 13:07 ChristianMurphy

@ChristianMurphy : I don't understand why this will revert #21, you should add only a signature verification. This documentation is clear for me : https://jwt.io/introduction/

Web Storage is accessible through JavaScript on the same domain so any JavaScript running on your site will have access to web storage, and because of this can be vulnerable to cross-site scripting (XSS) attacks.

There isn't alternative expect cookies, and for me it's not a good one, also it's not a big problem as you should know what is deployed under your domain site !

An other important point is to check that all is done only in https !

jgribonvald avatar Jul 16 '18 15:07 jgribonvald

jsonwebtoken includes token verification, jwt-decode does not. jsonwebtoken adds enough weight to the page it generates complaints, even after it has been tree shaken. I'd lean strongly against writing our own custom verifier, writing custom encryption/decryption has a lot of risk of messing up and opening a security vulnerability, and not much reward compared to using an existing well maintained and audited library. There may be another ligher weight version of jsonwebtoken that is robust and includes token verification, there would need to be some additional research done to track one down.

ChristianMurphy avatar Jul 16 '18 16:07 ChristianMurphy

maybe @prigaux can provide some lights...

jgribonvald avatar Jul 16 '18 17:07 jgribonvald

There is a notable performance win if we can sort this out. Here are my thoughts:

Since this matter of pre/re-verifying cached tokens seems to be somewhat dependent on the type of storage, might I suggest an alternative: https://github.com/sindresorhus/mem

What I like about mem is that the expiration is calculated using Date() instead of having a timer reference that can potentially leak later because the developer did not clean up. Other caching libraries tend to use setTimeout(...) more liberally. The code is tiny enough that I think minified and g'zipped would make its additional to the build output trivial. There might be one hazard in its use of a JS WeakMap. If this is a blocker, then I think at least the concept could be borrowed and implemented directly in the OIDC handler.

My endpoints that expect the token run the signature validation, and emit a 403 if it is invalid. Since we trust the portal to emit a valid, authentic token, I do not see the point of continuously re-validating a cached token.

mrapczynski avatar Jul 16 '18 18:07 mrapczynski

Regarding verification of JWT: I do not see any use of verifying a JWT without checking the signature. Checking signature in a web browser means the key to check the signature must be public. This implies having a private/public key pair.

/userinfo has 2 uses:

  • getting info in browser => only the JWT payload is interesting
  • getting a JWT to send to another API => it's this API responsability to check the JWT

prigaux avatar Jul 18 '18 11:07 prigaux

What I like about mem is that the expiration is calculated using Date() instead of having a timer reference that can potentially leak later because the developer did not clean up. Other caching libraries tend to use setTimeout(...) more liberally. The code is tiny enough that I think minified and g'zipped would make its additional to the build output trivial. There might be one hazard in its use of a JS WeakMap. If this is a blocker, then I think at least the concept could be borrowed and implemented directly in the OIDC handler.

mem moves in the right direction, removing the set timeout issue. :+1: The question is how would we handle a situation like sessions last for 30m, client-side timeout lasts for 5m?

E.G.

|-------------------------------------- Valid session 30m ------------------------|
|- user visits for 5m -|                     waits 23m                  |- user visits for 5m -|

The the above example the second client session would get a new token in the same server session, but would keep trying to use the token for 3 minutes after the server session expired.

/cc @drewwills does the above example correctly reflect the current OIDC behavior?

ChristianMurphy avatar Jul 26 '18 22:07 ChristianMurphy

From an offline conversation with @drewwills, another approach could be enhancing the user-info REST endpoint to set appropriate HTTP Cache headers. Using HTTP Cache would allow services outside of this helper to easily take advantage of caching. It also would allow the server to set an appropriate caching time, instead of the client side guessing an appropriate timeline.

ChristianMurphy avatar Jul 27 '18 16:07 ChristianMurphy

A refresh for this issue as we could have the need in a few due to the growing number of web-components... I Think we have to reduce the number of request at least on the user-info.

jgribonvald avatar Apr 04 '19 09:04 jgribonvald