loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.96k stars 1.07k forks source link

Increase the session expire time whenever call the service #3673

Closed AnanthGopal closed 4 years ago

AnanthGopal commented 5 years ago

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 commented 5 years ago

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

emonddr commented 5 years ago

@AnanthGopal , are you referring to

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

https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/application.ts#L90

https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/keys.ts#L14

?

I am trying to understand your question.

emonddr commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago
  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 commented 4 years ago

@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:

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:

This fixes all the problems reviewed above:

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!