14

This question follows my previous one: How to securely keep my users signed in with refresh tokens?

What I got from this previous question, is that we need:

  • short-lived access tokens
  • long-lived one-time refresh tokens

Refresh tokens are persisted in DB alongside users in a 1-1 relationship (1 user = 1 refresh token). Each time a refresh token is created for a user, it replaces the previous user's persisted one (if any). This allows possible hackers to have only a limited window to do their stuff:

  • user signs in and receives access token A1 and refresh token R1
  • hacker achieves to get those tokens somehow
  • access token A1 reaches expiration, so the hacker gets new access/refresh tokens A2/R2 thanks to the stolen refresh token R1: newly created refresh token R2 (that only the hacker has) replaces the previous one R1 in DB (the one the user has)
  • user fails getting a new access token because his refresh token R1 no longer exists, so signs in again and get new access/refresh tokens A3/R3: newly created refresh token R3 (that only the user has) replaces the previous one R2 in DB (the one the hacker has)
  • hacker's access token A2 expires, but cannot get a new one because refresh token R2 no longer exist: needs to steal the tokens again (supposed to be tough to do of course)

What I'm wondering here, is how to allow a user to connect from multiple devices with this mechanism? For example, if a user signs in with two different laptops, then refresh tokens will always erase each other... Any clue?

sp00m
  • 193
  • 1
  • 1
  • 9
  • Do you absolutely need to use refresh tokens to keep users logged in? – Limit Aug 09 '17 at 16:33
  • @Limit Well, as refresh tokens seem to be the common approach, this is what I studied, but I'm open to other solutions as well :) – sp00m Aug 09 '17 at 16:37

2 Answers2

11

In my opinion the question you're asking now is not really regarding security anymore, instead it's more focused on the implementation of a concept. You have already established that refresh tokens makes it harder for attackers and it appears like you have a working implementation. From what you are describing, you are making use of a custom solution and not something like an OAuth2 library implementation that will provide this functionality to you.

What you now need to decide is if you want multiple device authentication from the same user account. If you do not want users to sign in from multiple devices on one account, congratulations, your solution is already working. They will be logged out from the old device soon since it will not contain the correct refresh token after a timeout.

If you do want to provide users the ability to sign in from more than one device on the same account, you unfortunately require a few changes. A possible solution is to add a device identification field in your database and issue a refresh token per device.

Joe
  • 1,214
  • 1
  • 11
  • 16
  • This sounds particularly interesting: *add a device identification field in your database and issue a refresh token per device*. Could you detail your thoughts a bit about this? How would this be handled without letting hackers registering their own device? – sp00m Aug 18 '17 at 15:05
  • You can use a banner staying 1 week saying "A new device was registered, if it's not you, you should remove it and change your passphrase. This banner will stay for 6 more days". – A. Hersean Aug 23 '17 at 07:56
  • PS: you should also check if Google doesn't have patents on this idea of them. – A. Hersean Aug 23 '17 at 07:58
10

I agree with Joe's answer

A possible solution is to add a device identification field in your database and issue a refresh token per device.

but I'd like to add some implementation details.

IMO the most robust and functional solution is: for each user to have many records in database, each with two columns: refresh_token and device_id.

Lets examine main cases:

  • User login. He makes it with the help of the password, of course. He passes device_id (any unique number, actualy). If password is correct, server generates new access and refresh tokens, sends access_token to clien and inserts refresh_token and device_id into database.
  • User consumes the service. He sends access_token (witch internelly have to encode user information, obviously (for example his login), and expiration date). Server checks token (with the help of its secret signing key) to make shure that this token isreal and not a fake one. Also server checks access_token expiration date. If expired, user must refresh it with the help of appropriate refresh_token and (important!) device_id. If refresh_token in database doesn't equal user's token, than we get hacked. But don't panic - we just delete refresh token and redirect user to login page! That's it, hacker only will have access as long as access_token is alive (short time period, as usual)! If you need instant access blocking, you can store access_token along with refresh_token in db and also has a in-memory access_tokens blacklist. Then you can add stolen access_token into blacklist, and instantly block access.
  • User logout. He erases his access_token from cookies/localstorage, sends refresh_token and device_id to server, and server deletes them from list. (we don't want to collect obsolete unused tokens)
  • User change password. The server deletes all previous refresh_tokens. If you use token blacklist - add all current users' access_tokens to it. With such solution it's also very easy to implement "logout from all devices" function.
  • Hacker steal access_token and user don't know about it. Hacker only will be able to use it until it expire (short time frame).
  • Hacker also steal refresh_token and made refresh. User will not be able to use his old refresh_token, in that case he will prompt to login again, new refresh and access token will be generated, hacker lost access. (see case #2)

The one thing you should clearly understand : refresh token only can save you in case, when hacker steals access/refresh token, but not the real password! Because if he gets the pass then you're in trouble, and you must implement the protocol for restoring password (with the help of email or SMS, for example)

P.S It's a good idea to have refresh_token that can expire too. To prevent case, when jacker stole access and refresh tokens from old device, that is never in use now.

P.P.S It would be perfect to have periodic database task that will clean rows with expired refresh_token (pehaps you'll have to add column refresh_tocken_expiration_date for that)

P.P.P.S. I'm not security expert, so if you think there is a better solution I would be glad to have a discussion.

Thank you for reading, hope this helps)

DmitriyS
  • 101
  • 1
  • 4
  • Hello @DmitriyS, your solutions works in case you have only mobile app. what about website or mobile browser? we dont have any such unique device id. – Mihir Shah May 29 '20 at 04:27
  • Good question, @MihirShah, thank you. What do you think about generating some random number/string and store it in browsers' localstorage? And by the way I updated my answer: it's not cool to request database to verify access_token. – DmitriyS Jun 02 '20 at 23:13
  • This is basically how I do it for Refresh in native apps. In fact I am surprised it's not part of the OIDC core stuff. – Frank Apr 28 '21 at 18:33