2
votes

Currently I'm starting to pulling my hairs out on this. I've done some researching the past days and it seems that I do not get quite the point how to achieve the following:

I'm currently building a an API in Rails with a mobile application as the client. The mobile application can be either iOS or Android. Now I'm struggling with the authentication. After the client has logged in on the device, I issue a JWT with a short expiration. So if the token expires, the client needs to re-login, which is unacceptable UX in my case. Anyway, I feel that I need to have some kind of expiration to prevent the token from being used forever if it has been stolen.

Now I wonder how to integrate the refresh tokens. How do I ensure that the user can get a new access token only with a valid refresh token which has been issued to this exact device? Or am I overcomplicating things and assigning one refresh token which can be used on alle devices will do the trick?

Update: I've looked into the doorkeeper gem, which supports the password grant flow. But the way doorkeeper handles the tokens is, that it stores every generated access token in a database with a corresponding refresh token. When the token has been revoked or refreshed, the old access token gets invalidated - this will grow to a huge database table over time.

Additionally I prefer using JWT as the token, so I don't have to store anything but the refresh token in the database. Would the following process be secure?

  1. User requests access token with username / password and - let's say a devices name.
  2. Server issues JWT and creates a refresh token for the current device.
  3. The server stores the refresh token.
  4. When the access token expires, the user requests a new one by using it's refresh token.
  5. The server validates the refresh token and issues a new access token. Additionally, the refresh token gets replaced with a new one.

My questions on this: The user could be logged in in multiple devices, how to differentiate between them?

3

3 Answers

0
votes

The way I've seen that work is that the server returns an unauthorized status (401) with a message that indicates that the access token is expired, but then the client has exactly one opportunity to request a new token with that expired token. Once the client uses that expired token to request a new one the expired token becomes invalid and can no longer be used. That way you can just keep trading in old tokens for a new one.

If you have a short expiration time the chances of a stolen token still being valid are pretty slim, but from a UX perspective, the user will stay logged in, as the token negotiation can just happen in the background. A user's token is only invalidated if they log out or if the token they are using is explicitly invalided on the server side.

You should definitely have a unique token for every username/password combination you have too. Having the same token for every client in your app would be very insecure.

0
votes

I like the idea of refresh tokens. On the back-end what about using the doorkeeper gem? This way the tokens expiry gets updated on each client request.

User requests access token with username / password and - let's say a devices name.

Sending a username / password and device name to your server would defeat the purpose of the client not having to give you their credentials.

I see it as the mobile client requests a token id from the oauth provider (you'll need a server clientID). Mobile client sends the token id to your server for validation, thereby assuring the clients identity. The server can find the users email address within the JWT provided by a successful validation. See the rails google-token-id gem.

0
votes

Security is always relative and with more efforts we can make it one step harder for anyone to abuse the system. The bearer tokens are IO bound (cache/database call) and JWT tokens are CPU(encryption/decryption) bound. Both are good enough solution to in regular services world except for one thing that we are taking about mobile devices here. One of the approaches that could be used here is to send the token/refresh token to the device through backchannel instead of sending it as a part of authentication. Seems somewhat extra work but gives somewhat more security around issuing and refreshing the tokens for mobile devices. More details can be found here http://sdhankar.com/2016/10/24/authentication-and-jwt-token-refresh-on-mobile-devices/