8

I am implementing a token-based authentication system for a REST API using a short-lived access token and a long-lived refresh token. This is an abstract overview of the relevant API endpoints (HTTPS is enforced for all endpoints):

Endpoints:

POST /register/
POST /login/
POST /logout/
POST /password/change/

Implementation:

POST /register/:

  • Request: Client sends username, email, and password in JSON.
  • Server actions:
    1. Validates input, creates user in database (stores user id, username, email, and password hash).
    2. Creates short-lived access token in JWT format (contains user id, issued date, and expiration date).
    3. Creates long-lived refresh token as a UUID string and stores it in database (stores user id and refresh token).
  • Response: Server returns access token and refresh token in JSON.

POST /login/:

  • Request: Client sends username and password in JSON.
  • Server actions:
    1. Validates input, checks if credentials are valid by checking database.
    2. If credentials are valid, creates short-lived access token and long-lived refresh token as mentioned previously.
  • Response: Same as /register/, returns access token and refresh token in JSON.

POST /logout/:

  • Request: Client sends refresh token in Authorization header as Bearer token.
  • Server actions:
    1. Validates refresh token by checking refresh token database.
    2. Removes the refresh token from the database.
      Note: This leaves the access token valid, but since it will be short-lived (1 hour or so, I think it should be fine).
  • Response: Returns whether the logout request was successfully processed in JSON.

POST /password/change/:

  • Request: Client sends access token in Authorization header as Bearer token, and also sends old password and new password in JSON through HTTPS.
  • Server actions:
    1. Decodes access token to retrieve the user, and checks the user's old password with the database.
    2. Sets password hash of user in the database to new password's hash.
    3. Removes all refresh tokens associated with user in the refresh token database to essentially log out existing sessions (leaves short-lived access tokens valid).
  • Response: Returns whether the password change request was successfully processed in JSON.

Questions:

  1. Is this approach secure? Specifically:
    • Is sending the username and password through JSON safe if done over HTTPS? How would I prevent unauthorized domains from making calls to this endpoint? Furthermore, how would I prevent programmatic logins?
    • Should the refresh tokens be hashed before storing them in the database, or am I just being paranoid?
  2. If the client were a web browser, how would I securely store the refresh token on the client?
    • One idea I have for storing the refresh token is: when the user logs in, in addition to sending the refresh token to the client, the server stores the token in an HttpOnly cookie with a secure flag. Authorization will still be done through the Authorization header, but when the client initially loads up, it can send a GET request to an endpoint that checks if the cookie contains a valid refresh token, and if so, return it to the user in JSON. In other words, the only time the cookie will actually be used is to return the refresh token inside the cookie to the client. Is this approach secure? I think it will prevent CSRF as there are no side effects when requesting the refresh token from the cookie, but is there another way an attacker could intercept the refresh token (assuming HTTPS)?
  • 2
    Why not use Open ID Connect instead of trying to implement your own logon security mechanism? – Erik Eidt Jun 30 '17 at 23:59
  • I don't want to have to handle an additional server to act as the OpenID connect provider. Also, I would rather have full control over the authentication flow so I can easily pinpoint bugs and security vulnerabilities. – Kootling Jul 1 '17 at 19:33
  • 2
    I don't think that requires an extra server if you just use it. Also, doing your own implementation with your own protocol is has its own security risks maybe more than using the emerging standard approach, but who knows for sure? – Erik Eidt Jul 1 '17 at 19:42
2

Is this approach secure? Specifically:

  • Is sending the username and password through JSON safe if done over HTTPS?

Yes. Headers, request params and request body are encrypted during the communication.

Once on the server-side, do not log the request body :-)

  • How would I prevent unauthorised domains from making calls to this endpoint?

You can not. Basically, once the API is on the WWW, it's automatically exposed to all sort of malice. The best you can do is to be prepared and to be aware of the threats. At least about those that concern you. Take a look here.

A possible approach to the problem could be implementing (or contracting) an API Manager.

On-premise API Managers can reduce the attack surface because all the endpoints behind the AM are not necessarily public.

You could achieve the same result with some products in the cloud, but they are absurdly expensive for the mainstream.

Anyways, the API Management endpoints will remain exposed to attacks.

  • Furthermore, how would I prevent programmatic logins?

If by programmatic logins you mean attacks by brute force, a threshold (max number of allowed requests per second) and a black list should be enough to deter the attacker's insistence. For further information, take a look here.

Many of the API Managers provide out of the box API Rate Limit configurations and Whitelists.

If you are familiar with the Google API Console, then you can guess what an API Manager can do.

  • Should the refresh tokens be hashed before storing them in the database, or am I just being paranoid?

Whether the refresh token is a plain UUID or anything else, I don't like to expose this sort of implementation detail. So I would suggest to hash it. To me, the more opaque are the implementation details of the security layer, the better.

Regarding the JWT security, take a look here.

  • If the client were a web browser, how would I securely store the refresh token on the client?

You might be interested in JSON Web Token (JWT) - Storage on client side.

  • Regarding programmatic logins, I want to prevent someone from making their own front-end to the API (although as you stated this is practically impossible). Also, the refresh tokens (which are UUIDs, not JWTs) are currently stored as plaintext in the database, so I should definitely hash them, right? Regarding client-side token storage, I want to store the refresh tokens and persist them across sessions, so sessionStorage won't work. Also, localStorage and document.cookie seem insecure since they can be accessed by JavaScript. Does my approach make sense, or is it potentially insecure? – Kootling Jun 30 '17 at 21:25
  • 1. Yes, I would store a hashed token instead of the plain text. Just for making the content of the table opaque to other processes. 2. If I have to choose between localStorage and cookies, I choose cookies. Nevertheless, I would do first a little research about its vulnerabilities. Also, consider add to the security a mechanim to invalidate tokens manually. – Laiv Jun 30 '17 at 21:35
  • By "cookies," do you mean document.cookie or an HttpOnly cookie with a secure flag, similar to the approach I mentioned above? – Kootling Jun 30 '17 at 21:40
  • HttpOnly and secure flag don't prevent you from XST and XSRF. But It make the cookie secure to XSS attacks, which I consider a basic one. So Yes, HttpOnly and secure flag. Just remember that you wont have access to the cookie from Javascript – Laiv Jun 30 '17 at 21:50
  • Anyways, I encourage you to checkout the differents OWASP projects related to Web applications, API, REST and JWT. You will find far more info than the one I can provide you here. – Laiv Jun 30 '17 at 21:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.