-
Notifications
You must be signed in to change notification settings - Fork 208
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
[POC] add refreshtoken to extend accessToken #537
Conversation
a8f6698
to
487e699
Compare
Signed-off-by: derdeka <[email protected]>
Signed-off-by: derdeka <[email protected]>
487e699
to
980a065
Compare
Requesting feedback on this PR. If this is the right direction, i'll add some tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think perhaps dropping ttl and possibly even creation date from refresh token model and replacing with a straight expiry date might be better. Extending token could be a reissue with updated expiry date. Revocation of token would be setting the expiry date in the past, forcing a login. Hope that makes sense. I'll double check tomorrow. But it makes sense to me right now.
@@ -51,6 +52,9 @@ export class User extends Entity { | |||
@hasOne(() => UserCredentials) | |||
userCredentials: UserCredentials; | |||
|
|||
@hasMany(() => UserRefreshtoken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not hasOne? Can user have multiple?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the refreshToken
is newly generated at login time. As you can login multiple times or from multiple browsers/devices this needs to be a hasMany
relation.
Alternatively we could share the refreshToken
accross browsers/devices, than a hasOne
would fit. But this might decrease security!?
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For simplicity, sharing a single token across devices would have a single point of failure, any detected breach would log out all devices. Could be annoying to user.
Your solution is more flexible/complex, but might think about identifying the authorised device linked to the refresh token maybe? Identifying devices, allows the token to be individually invalidated (i.e. Lost device can have access revoked, although any serious security will remote wipe devices). If one token is compromised, is there any practical difference in access compromised? To get updated refresh token we need to log in regardless don't we? Hmm will need to think more.
Both solutions allow:
- Local - individual devices to be logged out(delete local token)
- Server - all devices to be logged out(expire one token vs. expire all tokens)
- Server - update permissions (update one token vs. update all tokens)
Multiple refresh token adds:
- Server - log out single device(given identity is known, expire the individual token)
Do what you think fits the scenario best. I'm just offering thoughts for consideration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
properties: { | ||
refreshtoken: { | ||
type: 'string', | ||
minLength: 8, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minLength: 8
Usually the jwt tokens we generate are very long. What is the reasoning for 8? Why not 1? Or why specify this at all? Just curious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@emonddr If you look at the generateToken
function in
https://github.com/strongloop/loopback4-example-shopping/pull/537/files#diff-90f4ee64422c3d610fc44a185e6d0781R47
A refresh token is the id of a UserRefreshToken
record, not a token generated by jwt service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer a pattern check to the token, but i'm still not sure what is the best way to generate a refreshToken
. Currently it's a objectId
which might not be the best practice. On a mysql
/postgresql
database i would just use a UUID as refreshToken
and use a regular expression for validation.
Do you have any other ideas how to generate a revokeable token?
@@ -0,0 +1,69 @@ | |||
// Copyright IBM Corp. 2020. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derdeka , cool stuff. I was going to just increase the token lifetime from 10 minutes to 6 hours in my last PR ;).
I am wondering :
- are you going to add mocha tests?
- have you interacted with the shopping cart API Explorer to see how this will affect the user experience?
Developers may want to use the API Explorer to interact with the REST API to debug how things work
for extended periods of time. It would annoy me when the jwt token would expire in 10 minutes. So I would
set it for 6 hours while I debug and play with the REST API. How will these changes affect this? - What needs to change in the Shoppy application (UI front-end) that was recently added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@emonddr All good questions. Above @derdeka said he'd add tests after initial feedback.
I think the general idea is for the refresh token to hold the session open on a device for a period of time(say 6 hours, two days or even longer) without the need to log back in. When your access token runs out after 10 minutes, then the refresh token would be used to get new access token without the need to log in again. This would also need to be designed so that the refresh token can be revoked and force a login. Is that correct @derdeka?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- are you going to add mocha tests?
I'll add some tests.
- have you interacted with the shopping cart API Explorer to see how this will affect the user experience?
On a angular
application i would use a serviceworker
or service
to periodically refresh the accesstoken. Is there something similar in the shopping example?
Developers may want to use the API Explorer to interact with the REST API to debug how things work
for extended periods of time. It would annoy me when the jwt token would expire in 10 minutes. So I would
set it for 6 hours while I debug and play with the REST API. How will these changes affect this?
Good catch, i have currently no concept for this. My use case is a regular user on a angular
SPA.
- What needs to change in the Shoppy application (UI front-end) that was recently added?
I did not have a deep look how the frontend is currently working. I guess a serviceworker
could work here too?
const {creation, ttl} = await this.userRefreshtokenRepository.findById( | ||
refreshtoken, | ||
{ | ||
where: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usually findById should return only one result, any reason adding an additional where filter here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to check if the refreshToken
is valid for / belongsTo the current user, validated by the authorization system. It's a kind of additional security check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently learned from @raymondfeng that findById does not respect the where condition. I have to do the check differently.
}) | ||
@authenticate('jwt') | ||
// TODO(derdeka) find out why @authorize.allowAuthenticated() is not working | ||
async refresh( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would the refresh function get called? I am trying to illustrate the flow of how a refresh token works:
- first a user login
- then it gets the token and refresh token
- then how does it decide when to call the
refresh
endpoint to extend the login time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then how does it decide when to call the refresh endpoint to extend the login time?
In a angular
application i would use a serviceworker
or service with a timer to call this endpoint periodically in background.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derdeka Appreciate your contribution 🎉 I left a comment in https://github.com/strongloop/loopback4-example-shopping/pull/537/files#r378960812, otherwise LGTM.
@derdeka Do see opportunity to add some code to loopbackio/loopback-next#4746? |
This pull request has been marked stale because it has not seen activity within two months. It will be closed within 14 days of being stale unless there is new activity. |
This pull request has been closed due to continued inactivity. If you are interested in finishing the proposed changes, then feel free to re-open this pull request or open a new one. |
Proof of concept for loopbackio/loopback-next#4573
Early feedback welcome.
Basic Idea:
accessToken
(e.g. 10 minutes) and very long-livedrefreshToken
(e.g. 48 hours) which are issued at login timeaccessToken
giving access to resourcesrefreshToken
can be used to to create a newaccessToken
refreshToken
can be revoked, e.g. by logout