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:
- Validates input, creates user in database (stores user id, username, email, and password hash).
- Creates short-lived access token in JWT format (contains user id, issued date, and expiration date).
- 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:
- Validates input, checks if credentials are valid by checking database.
- 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 asBearer
token. - Server actions:
- Validates refresh token by checking refresh token database.
- 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 asBearer
token, and also sends old password and new password in JSON through HTTPS. - Server actions:
- Decodes access token to retrieve the user, and checks the user's old password with the database.
- Sets password hash of user in the database to new password's hash.
- 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:
- 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?
- 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 asecure
flag. Authorization will still be done through theAuthorization
header, but when the client initially loads up, it can send aGET
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)?
- 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