Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase the session expire time whenever call the service #3673

Closed
AnanthGopal opened this issue Aug 12, 2019 · 7 comments
Closed

Increase the session expire time whenever call the service #3673

AnanthGopal opened this issue Aug 12, 2019 · 7 comments
Assignees

Comments

@AnanthGopal
Copy link

I implement the Token-based Authentication service by using below link https://loopback.io/doc/en/lb4/Authentication-Tutorial.html.

I need a small feature that is "Increase the session expire time whenever call the service"

I set an initial session timeout is 20 minutes. but I need to reset this session timeout whenever I call the service. For Ex.

Initial session timeout is 20 minutes, I spent 5 minutes(Session timeout 15 minutes left) on a website, after that, I click a page that time I called a service, so I want to reset the session timeout 15 minutes into 20 minutes.

Every service request I reset session timeout into 20 minutes.

@dhmlau dhmlau transferred this issue from strongloop/loopback Sep 5, 2019
@dhmlau
Copy link
Member

dhmlau commented Sep 5, 2019

@jannyHou @emonddr, could you please take a look? Thanks.

@emonddr
Copy link
Contributor

emonddr commented Sep 13, 2019

JWT is a token-based authentication and it is also stateless authentication.

Our user controller provides a login endpoint which returns a JWT token
https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/controllers/user.controller.ts#L170

( the user controller makes use of our token service )

It is up to the web ui programmer to direct the user to login with credentials, to store the token in local storage, to use this token for every backend request , and to destroy the token when the user logs out or is inactive for a specific amount of time.

So in our demo, you can set this line to a bigger value ( e.g. 24 hours or never expires ) if you want the token to have a longer life
https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/keys.ts#L14
image

Our JWT service's generateToken method:
https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/services/jwt-service.ts#L50
uses the sign method of jsonwebtoken npm .

The sign method for jsonwebtoken is described here:
https://www.npmjs.com/package/jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback

To have a token that doesn't expire, just omit passing the option expiresIn.

https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/services/jwt-service.ts#L64

I hope this answers your question.

A nice diagram of JWT token authentication can be found here:
https://appdividend.com/2018/02/07/node-js-jwt-authentication-tutorial-scratch/

@jannyHou
Copy link
Contributor

Based on @emonddr 's answer, I think the user is looking for feature like rolling in the expressjs session manager.

The token based authentication is different than the session based one, I find some articles talking about refreshing token(similar concept as rolling) in a token based auth system:

https://stackoverflow.com/questions/26739167/jwt-json-web-token-automatic-prolongation-of-expiration
https://auth0.com/learn/refresh-tokens/

So it really depends on which method you use to track the user.

@raymondfeng
Copy link
Contributor

There are two ways to generate access token:

  1. Generate a token with all attributes encoded, including the expiration time.
  2. Generate a token and store its attributes in a backend database.

It's not practical to renew a token with option 1. There is a possibility to do so with option 2. Refresh token is another mechanism.

@jannyHou
Copy link
Contributor

  1. Generate a token and store its attributes in a backend database.

Good point, IIUC users can reset the expire time everytime retrieving the attributes attached to the token.

@AnanthGopal does it answer your question?

@sformisano
Copy link

sformisano commented Dec 3, 2019

@emonddr I don't think it's fair to expect the client app to keep track of how long the user has been inactive for...

I'm going to write a long answer because I'd like to understand the reasoning behind the way the authentication library currently works, and if it's welcome, I'd be happy to contribute code for this functionality (I'd definitely need help in understanding what the best place for this kind of code would be) :)

So!

Most JWT based auth systems do actually carry a refresh mechanism for the access token, either through a secondary refresh token that lasts longer than the access token or with a long-living access token that also works as a refresh token.

Based on how this seems to work at the moment in LB4, if I want to set a low TTL for the access token, which would be good for security, once the token expires, I would have to login again, even if I was using the app right when the access token expired.

I'm sure users having to log in every 10 minutes is not what the LB team is shooting for so why don't we review a few options to avoid that:

1) The simplest approach: very long-lived access token with no refresh mechanism.

This means creating access tokens with TTL set to weeks or months. As a consequence, the problem described above is vastly mitigated, but still not resolved. The user will still, at some point, be logged out, even if it uses the app every single day. The user could even be logged out while it is using the app because when the token does expire, the only way to get a new one is to log in again.

This solution is also not great from a security standpoint: by definition, an access token is a token that provides access to resources, and in a stateless authentication environment a token of this kind that lasts for months can be problematic. There are a number of ways to mitigate this type of issue as well, but they are not very efficient.

One example would be having an access tokens blacklist. Every time there's a request, the token would be verified against the blacklist. Running this type of check for every requested is not the most efficient way to solve this problem.

A user-level ban mechanism would have the same exact efficiency limitation.

As far as I can see, this is the only solution that seems to be available out of the box within @loopback/authentication, which is why I started looking for this kind of discussion.

2) A better approach: long-lived access token with refresh mechanism.

This solution would have a single access token with a considerable TTL, but not as long as for solution (1), perhaps a few days. The improvement over solution (1) is that the API would be responsible for refreshing the access token and sending them back to the client regularly. It would work like this:

  • Every time there's a request, access token TTL is checked by the API.

  • If the access token TTL falls below a certain threshold (e.g. 50% of original TTL), a new access token would be created and automatically sent to the client.

  • The client would notice a new access token has been sent back, and it would replace the old one with the new one.

This resolves the involuntary mandatory logout issue from solution (1), and it also improves scalability and security: the token no longer needs to last for very long (security), and if there needs to be any kind of ban/moderation feature available to intervene against malicious users, it can be implemented within the refresh mechanism rather than in every single request (efficiency) made to the API.

Still, the refresh procedure would only happen when the access token is beyond a certain lifetime threshold, which would still mean that it would take a while to be able to ban someone.

This brings us to my favorite solution:

3) The best solution (IMHO): short-lived access token and very long-lived refresh token.

Under this paradigm, two tokens are issued at login time: an access token, i.e. the token giving access to resources, with a very short TTL (e.g. 10 minutes), and a refresh token, i.e. a token whose only ability is that of requesting a new access token. The auth flow would work like this:

  • The token is implicitly verified to authenticate the user. If the access token expired, we proceed to the next step.

  • The refresh token is verified. As this token lasts for very long, chances are it is still valid, and if it is, we can use this token's authority to generate a new access token that will once again last very little, e.g. 10 minutes.

  • We return the new access token to the client, which will replace the expired access token with a new valid one. While we're at it, we'll also generate a new refresh token and update that one on the app as well, so that we can avoid running into any issues with this one expiring at some point down the road (i.e. the same issue we had in solution 1).

This fixes all the problems reviewed above:

  • The token providing access to the resources in the API is extremely short-lived = better security

  • The refresh token has a high TTL, but it does not provide access to API resources, it can only be used to ask for a new access token, and when that request is made, we can run all our security checks/procedures. The refresh procedure would be triggered very often because the access token has a short TTL, which means a malicious user is forced through security checks regularly while still not causing a potential performance issue at scale.

This last point may sound trivial if your API is a monolith, but in a microservices environment, this matters a lot: any microservice that is not directly responsible for authentication/authorization should only be asked to verify the access token's signature and TTL. If the token is not expired, and if the signature is valid, then for all intents and purposes you are authenticated as far as those microservices know.

Therefore, if a self-sufficient access token lasts for months, all your microservices will consider a potentially malicious user as logged in, for months, and they literally do not have the ability to intervene in any way.

On the other hand, if the access token lasts only for a few minutes, the malicious user will have to go back to the authentication microservice, which is the one holding all the security checks functionality.

I know this was very long, but I think it's important to start a conversation on how the default authentication library for LB4 should work.

Do you guys think implementing a flow like the one described in solution (3) would be unreasonable?

Is there anything I am missing in the current LB4 authentication flow that renders what I wrote above incorrect?

Like I said earlier I'd love to help, so please let me know what your thoughts are.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants