Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON web token auth logic with refresh tokens

Angular 4 application running in browser (website backend), displaying from the server data owned by particular user. Server: PHP+MySQL, Zend Framework 3 + Doctrine ORM

Naming:

  • access_token: Short lifetime (1 min), allows to access personal resources, carries user_id, base64 encoded, json web token spec valid.
  • refresh_token: Long lifetime (1 week) allows to retrieve new access_token without providing credentials, stored in db, can be revoked by admin if needed.

Main point of using refresh_tokens is to be logged in longer than access_token short lifetime (possibly forever if refresh_token expiration time is updated each time user authorization happens), user needs to provide credentials only in case of inactivity longer than refresh_token lifetime. Refresh tokens are stored in db, so can be easily revoked.

1. Browser tries to authenticate

REQUEST:

  • username and password
  • sent to /api/auth

RESPONSE:

Username and password is validated and checked against database

If valid:

  • access_token is generated with expiration time 60sec
  • user_id is encoded into access_token
  • refresh_token is generated (random string) and saved to db, expiration time 1 week, (refresh_token is not included in access_token, it's a separate key)
  • HTTP 200 OK

If invalid:

  • HTTP 401 Unauthorized

ACTION AFTER

IF valid:

  • access_token and refresh_token stored in browser (private member varialbe of auth service, browser's local storage).

Looks like it's not a good idea to store refresh_token in local storage - but this allows for "keep me signed in". If stored only per browser session in private member variable, user would need to log in every time opens the browser. Any ideas?

If invalid:

  • display errors, suggest to retry

2. Browser requests protected data from server

REQUEST:

  • sends access_token
  • to /api/resource

RESPONSE:

  • if access_token is valid, json data are sent, HTTP 200 OK
  • if access_token is invalid (eg. unable to decode), HTTP 400 Bad Request
  • if access_token expired, HTTP 401 Unauthorized

ACTION AFTER RESPONSE:

  • if HTTP 200: data displayed
  • if HTTP 400: redirected to login page
  • if HTTP 401: retry to obtain new access_token using refresh_token stored in browser

3. Retry to autenticate using refresh_token (after HTTP 401 Unauthorized)

REQUEST:

  • access_token
  • refresh_token

RESPONSE:

  • Validate access_token (everything except expiration time, using \Firebase\JWT)
  • Validate refresh_token against database (user_id decoded from access_token, the string and expiration time)

If valid:

Generate new refresh_token, save to database, update ttl (or should be the same token, only updated ttl?) Generate new access_token HTTP 200 OK

If invalid:

HTTP 401 Unauthorized

ACTIONS AFTER RESPONSE:

If valid:

  • store the new access_key and refresh_key in browser
  • retry the previous resource request using new access_key

If invalid:

  • display login page

Questions

The key point I don't like is that access_token and refresh_token are stored in the same place and sent the same way. Maybe there is another way to do it?

  • is this logic sensible?
  • are there any security flaws, assuming this happens in web app?
  • should I store both the tokens in browser's local storage?
  • should refresh_token be encoded in access_token? If they are still saved in the same place, looks that it may be incorporated into access_token. Any reasons not to do it?
  • when should I update refresh_token lifetime?
  • any open source projects to see similar authentication in action?
  • any other suggestions?
like image 572
Sfisioza Avatar asked Nov 22 '17 11:11

Sfisioza


1 Answers

Regarding where to store tokens and the security flaws (taken from Github user brettpostin https://github.com/IdentityServer/IdentityServer3/issues/2039#issuecomment-288135399) keep in mind that:

  • Storing tokens in localStorage is susceptible to xss.
  • Storing tokens in cookies is susceptible to csrf.

Best option is to protect against both as described here: http://www.redotheweb.com/2015/11/09/api-security.html

Store your tokens in http-only cookies and use a suitable targeted csrf defense as suggested here: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet

when should I update refresh_token lifetime?

If you are asking when to update the refresh token it depends on the implementation of your protocol, for example Google Auth server issued Refresh tokens never expire, they are revoked when the user revokes access to the app. But may I suggest a few weeks, so you don't lose control over it.

any open source projects to see similar authentication in action?

Well, you can download any project that uses OAuth2, for example this project on github: https://github.com/authlib/example-oauth2-server

any other suggestions?

Have a look at OpenID Connect and Identity Providers (IDPs) such as Keycloack.

Hope this helps you in any way.

like image 198
L RodMrez Avatar answered Oct 13 '22 18:10

L RodMrez