0

I want to allow users of my app to sign in anonymously (that will create an account for them with a randomly generated email & password), and if and when they'll want to create a permanent account, they will be able to simply bind an email & password.

I work with tokens, access and refresh, which means the regular tokens flow is the same for both anonymous and authenticated users.

The problem with anonymous users is what if a user didn't logged in to the app for a long amount of time (that is larger than the refresh token) which means that now the refresh token is expired and invalid.

How could I handle such case? I couldn't ask them for a reauthentication as usual because they don't know they're credentials, and I can't risk to lose the user's data.

Only thing I could think of is to try and save their credentials securely with encryption using local preferences such as DataStore/SharedPreferences combined with KeyStore, and when such case does happens, I'll simply relogin them. But I'm not sure if that's good enough.

Any suggestions are much appreciated!

Ofek
  • 324
  • 3
  • 13

1 Answers1

1

It seems like the thing you want to implement is ignoring JWT token's exp field. Omitting it or setting to a value which is years after iat would solve the issue.

But this means you'll have to tune token acquiring mechanism and it won't be identical to both anonymous and signed up users.

Edit

Refresh token is not valid forever in any case. You should validate it with a secret (randomly generated string that is unique for every user in the database). This secret must be regenerated on every password change (via app or forgot password flow) and every "Log off from all devices" action. It is used for every access token acquiring.

As for anonymous auth - it is not what considered a standard approach when working with JWT tokens. You should not treat these users the same as authenticated ones and should restrict their access to app contents. Usually you would keep as much info as possible on the client rather than storing it on the server.

What I wanted to say is that you just cannot make your anonymous users as secure as authenticated ones, as you just ommit one (and the most important) level of protection by not asking for email and password.

Having access to refresh token is almost the same as having access to user's password. When password changes - the refresh token does so too. The only difference is that refresh token has an expiration date. As password is not stored anywhere on the device and only the user knows it, it actually adds an additional layer of security - attacker won't be able to relogin as he does not know the password.

The thing is that you want to store credentials on the device. This is identical to storing refresh token with no expiration, as an attacker would be able to read credentials from the storage and relogin forever. Thats the point.

You should look at How to securely store access token and secret in Android? for some info on storing tokens.

In sum, I would never bother about security of anonymous users and design the system keeping that in mind

Teempy
  • 491
  • 4
  • 11
  • Hi, thanks for the answer. But wouldn't that mean that if now an attacker gets his hands on such token without an expiry date (or a with a very long expiry date) he would always have access to the specific user's account? – Ofek Oct 24 '21 at 07:29
  • Yes, exactly. Thats why we set expiration dates on them and require user to relogin and provide with an opportunity to log off all the devices, just for security reasons. The thing is, that there is no difference between encrypting credentials for relogin or encrypting refresh token. Its the same thing. If an attacker gets access to credentials - he would have the same power as having refresh token instead. But anonymous auth usually means low level of security requirements, so I either would not bother about a possible attack or force user to sign up. – Teempy Oct 24 '21 at 08:58
  • So either way I need to encrypt something whether its credentials or a refresh token. Would say using KeyStore is safe to save the key? Could you elaborate a little about what changes I would have to make to the token acquiring code? thanks! – Ofek Oct 24 '21 at 09:37
  • Also, isn't there a difference? with credentials you may be able to fix such attack by changing the user's password. But with an unlimited refresh token because its valid forever. Correct me if I'm wrong – Ofek Oct 24 '21 at 09:48
  • I guess I still have some research to do because I didn't thought JWT access & refresh tokens needs to be implemented using a client secret. Another thing I thought is to let an anonymous user to have complete access to the app and server functionalities (same as regular users), just make it possible for them to attach email & password and transform from an anonymous to regular account. Thanks for your very detailed answer. It would also help if you have any recommended sources to learn about these client secret that I have to save on both the client & server? – Ofek Oct 24 '21 at 12:38
  • After reading a bit, I don't see why would I have to implement the additional client_secret field for the application to save. How does it help my server authenticate requests better? assuming the client already keeps access & refresh tokens, the server has all it needs in order to authenticate a request a regular request (with access token) or a refresh request (with a refresh token) – Ofek Oct 24 '21 at 13:58
  • But how do you validate access tokens and refresh tokens at the moment? To make it work as intended, you have to store one global secret for validating access tokens that does not change over time. This the point of using JWT - you do not have to check database to validate token, only to use one (we can say hardcoded) secret and validate tokens with it. But to validate refresh tokens, you have to store an additional secret key for every user. You must store them only on the authorization server and use it for validation before issuing an access token. – Teempy Oct 25 '21 at 04:50
  • Currently, for both access & refresh token I use a global secret. Every implementation I saw was doing it like this also. About invalidating them, I have a blacklist so that when a refresh token is has used, it's no longer valid and the same applies when the user changes his password. What you suggest is to simply save another secret for each user (server side) and make sure that the secret the client sends matches the value in the database with each request & then also validate the jwt as usual. Again, thanks. this is really helpful. – Ofek Oct 25 '21 at 05:15
  • How do you distinguish access and refresh tokens then? Can't one send an access token to you as refresh token and get a new one? Or are there two global secrets? – Teempy Oct 25 '21 at 05:49
  • Correct, two global secrets. – Ofek Oct 25 '21 at 06:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/238496/discussion-between-teempy-and-ofek). – Teempy Oct 25 '21 at 06:23