Uses a simple in-memory storage for User records, no database/password hashing etc. This was made only to demonstrate the sign in/out actions with JWTs.
All endpoints expect content type JSON.
Protected endpoints require HTTP authorization header in following format:
Authorization: JWT [jwt_encoded_string]
Example:
Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjajN1eGJscnYwMDAwaHJ6eXlpZzM0ejJtIiwiaWF0IjoxNDk3MzE5NTY3LCJleHAiOjE0OTc5MjQzNjd9.3hjshab6VdWV9K_Qt_HJbhiWJdZ_oJjzPl0-vFDhwIo
All JWT tokens are made up of a header, payload, and signature. The signature is uniquely generated by hashing the combined base64-encoded header, payload, and server secret. This means the server can always guarantee it was the issuer of the token.
The server generates with the following payload:
{
iat: unixtime (issued at),
exp: unixtime (expires at),
sub: string (user id)
}
These payload objects are stored in a tokens
field on the User model when the token is generated. Every Sign In generates a new token and is deleted when a Sign Out occurs.
All protected endpoints expect a valid token to exist in the Authorization header described above. The server validates the token by performing two steps:
- Verify signature - this will fail if it's a malformed string or doesn't match the server secret
- Fetching the user record matching the
sub
field, and then ensuring a token exists intokens
with a matchingiat
The second step ensures that a valid signed JWT token won't be authorized when a user signed out from that device prior to expiration.
TODO: If there was a database, you would want to periodically prune tokens that have expired to account for devices that lost the locally stored token and never send a Sign Out.
POST /api/auth
No Authorization Header Required
Request Body:
{
username: string (req),
password: string (req)
}
Responses:
400 - { message: 'Fields `username`, `password` required }
401 - {}
201 - {
username: string,
token: jwt_encoded_string
}
It is expected that the client will store the username
and token
in session or local storage.
This endpoint checks if:
- The token is valid and matches server secret
- The token's iat (issued at) matches with a token stored in User record
DELETE /api/auth
Authorization Header Required
Request Body: {}
Responses:
400 - { message: Authorization header required }
400 - { message: Authorization is malformed or invalid }
200 - {}
This endpoint creates a user and performs a sign in action to generate and immediately return a token.
POST /api/users
No Authorization header required
Request Body: {
username: string,
password: string
}
Responses:
400 - { message: 'Fields `username`, `password` required }
422 - { message: 'Username already taken' }
201 - {
user: object
token: jwt_encoded_string
}
GET /api/users/me
Authorization Header Required
Responses:
401 - {}
200 - {
id: string,
username: string,
tokens: array [decoded jwt payloads]
}