YetAnotherToDoList/server/auth/README.md

223 lines
5.5 KiB
Markdown
Raw Normal View History

# Authentication
Authentication in this project works in three steps:
1. first you [login](#login) and retrieve a [refresh token](#refresh-token)
2. send your [refresh token](#refresh-token) and retrieve a short-lived
[access token](#access-token)
3. send your [access token](#access-token) with every api request to access
protected recourses
See the [example](#example) for the most basic authentication flow. Note that
the script requests a new refresh-token every time, which should be avoided.
## Login
Make a request to `/auth/login` with your user credentials (username/user Id &
password) as json inside the `Authorization` http header field:
```bash
Authorization: {"userId":"<userId>","password":"<password>"}
```
The order of the `json` fields does not matter. Note the use of `"` for valid
`json`.
### Usage
If your credentials are valid, you will get a [refresh token](#refresh-token) as
a response. Store it somewhere save and use it to request new
[access tokens](#access-token).
### Fields
Password and either Username or userId are required.
#### Password
Your password must only contain letters and numbers.
#### Username
Your username must only contain letters and numbers.
#### User Id
Your userId.
## Refresh Token
A json object containing a selector, token and expiry date used to request
[access tokens](#access-token)
```json
{ "selector": "<string>", "token": "<string>", "expiryDate": "<int>" }
```
### Obtaining
See [login](#login).
### Usage
Used to [request an access token](#obtaining-1).
### Fields
All fields are required.
#### selector
The base64 encoded URL-save representation of a random `byte[9]` array used to
retrieved the associated token-hash from the database.
#### token
The base64 encoded URL-save representation of a `byte[33]` array used to compare
against a hash stored in the database.
#### expiryDate
An integer representing the UNIX timestamp at which the associated refresh token
becomes invalid.
Currently hardcoded to **10 days** inside
[/database/crypto_helpers.go](../../database/crypto_helpers.go):
`const refreshTokenLifetime = "+10 day"`.
## Access Token
A JWT (JSON Web Token, [learn more](https://jwt.io/)) consisting of three base64
encoded URL-save parts separated by dots `.`.
Decoded & formatted:
```json
{
"alg": "HS256",
"typ": "JWT"
}
.
{
"userId": "<int>",
"isAdmin": <bool>,
"isUserCreator": <bool>,
"expiryDate": <int>
}
.
<signature>
```
### Obtaining
Make a request to `/auth` with a [refresh token](#refresh-token) inside the
`Authorization` http header:
```json
Authorization: Refresh {"selector":"<string>","token":"<string>","expiryDate":<int>}
```
Returns:
```
<base64 encoded header>.<base64 encoded payload>.<base64 encoded signature>
```
### Usage
When included in the `Authorization` http header, it gives you access based on
your user role.
```
Authorization: Bearer <base64 encoded header>.<base64 encoded payload>.<base64 encoded signature>
```
### Fields
All fields are required.
#### Header
##### alg
The algorithm used to create the signature. Currently only `HS256` is supported.
##### type
The **IANA Media Type**
([learn more](https://www.iana.org/assignments/media-types/media-types.xhtml))
of the payload. This field is always set to `"jwt"`.
#### Payload
##### userId
The userId associated with the access token.
##### isAdmin
A boolean indicating if the user has admin rights.
##### isUserCreator
A boolean indicating if the user has user creation rights.
##### expiryDate
An integer representing the UNIX timestamp at which the associated refresh token
becomes invalid.
Currently hardcoded to **10 minutes** inside
[database/crypto_helpers.go](../../database/crypto_helpers.go):
`const accessTokenLifetime = time.Minute * 10`.
#### Signature
A `SHA256` hash of the encoded header and payload. The value of
`database.secret` from the [config file](../../.YetAnotherToDoList.yaml) is used
as a key (salt).
## Why
We use access tokens with a lifespan of 10 min and an in-memory db to blacklist
revoked tokens. So if for e.g. a user changes it's password, we would add the
userId and the time of the change to the blacklist, invalidating all tokens of
that user that have been issued before.
After a server restart, all tokens will become invalid as well, since we can not
be sure which ones were revoked (this could be mitigated in the future by making
the blacklist persist during restarts).
If a token has expired (either by a server restart or after 10 min), a new token
is requested with a [refresh token](#refresh-token) that is stored in a
database.
### Pros:
- less DB lookups in general
### Cons:
- increased load on database after server restart since all active clients need
a new access token.
## Example
```bash
#!/bin/env bash
USERNAME="admin"
PASSWORD="temporaryPassword"
# login (you can replace `userName` with `userId` since the username might change)
REFRESH_TOKEN=$(curl -ksH "Authorization: Login {\"userName\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" https://localhost:4241/auth/login)
# request access token (every 10 minutes or after server restart)
ACCESS_TOKEN=$(curl -ksH "Authorization: Refresh $REFRESH_TOKEN" https://localhost:4241/auth)
# a request to a fictional protected resource
QUERY_RESULT=$(curl -ksH "Authorization: Bearer $ACCESS_TOKEN" https://localhost:4241/protected)
# "convert" the header & access token to a json string to use in client applications (e.g. GraphiQL)
echo -e "Use this as header for e.g. GraphiQL:\n{\"Authorization\":\"Bearer $ACCESS_TOKEN\"}"
```