Skip to content

Commit

Permalink
docs: Add API docs for refresh token implementation (#6301)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamareebjamal committed Aug 3, 2019
1 parent 3e3e9a0 commit aa4147b
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 3 deletions.
144 changes: 143 additions & 1 deletion docs/api/blueprint/auth/authentication.apib
Expand Up @@ -2,7 +2,7 @@

The API uses JWT Authentication to authenticate users to the server. For authentication, you need to be a registered user. Once you have registered yourself as an user, you can send a request to get the access_token. This access_token you need to then use in Authorization header while sending a request in the following manner: `Authorization: JWT <access_token>`

## JWT Authentication [/auth/session]
## JWT Authentication [/v1/auth/login]

### Authenticate and generate token [POST]

Expand All @@ -24,3 +24,145 @@ The API uses JWT Authentication to authenticate users to the server. For authent
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDk2OTU2ODMwLCJuYmYiOjE0OTY5NTY4MzAsImV4cCI6MTQ5NzA0MzIzMH0.bjl0"
}

### Authenticate with remember me [POST]

If there is a need for user to be logged in for extended period of time (max 1 year), value of `true` must be sent in `remember-me` key. This will issue a refresh token and refresh CSRF token in cookies, which can be used to refresh the access token once it is expired.
**Note**: Doing this will reduce the expiry time of access token from 24 hours to 1.5 hours to increase security

+ Request

+ Headers

Content-Type: application/json

+ Body

{
"email": "email@example.com",
"password": "password",
"remember-me": true
}

+ Response 200 (application/json)

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDk2OTU2ODMwLCJuYmYiOjE0OTY5NTY4MzAsImV4cCI6MTQ5NzA0MzIzMH0.bjl0"
}

### Authenticate with remember me for mobile [POST]

For mobile clients, dealing with cookies is not easy, and traditional problem of XSS and CSRF does not exist for them, so cookies are not required for them. They can send `true` in key `include-in-response`, which will not send cookies with request and send the refresh token in the response JSON

+ Request

+ Headers

Content-Type: application/json

+ Body

{
"email": "email@example.com",
"password": "password",
"remember-me": true,
"include-in-response": true
}

+ Response 200 (application/json)

{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjQ4NTMwNzgsIm5iZiI6MTU2NDg1MzA3OCwianRpIjoiNzVjNTNiNTgtZmYzYy00NTEyLTk2YzktYjQ0NDQ0N2MzMTAwIiwiZXhwIjoxNTY0ODU4NDc4LCJpZGVudGl0eSI6MSwiZnJlc2giOnRydWUsInR5cGUiOiJhY2Nlc3MiLCJjc3JmIjoiZTk1ZmEzYTktNjMyYS00NGFhLWIzMDAtZmQ2NDI0MmE0ODU3In0.7lBrQW3wAWnwc7vWIBLh5p2a0KUzG42VyoHS8qM-OYs",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjQ4NTMwNzgsIm5iZiI6MTU2NDg1MzA3OCw3ODgtMzdlYS00YjE1LWE4YjktMjQ0YzYyN2I3NTljIMzg5MDc4LCJpZGVudGl0eSI6MSwidHlwZSI6InJlZnJlc2giLCJjc3JmIjoiNWZlZTdhYmYtNTIxNS00NzFhLWEwZWYtMDk4NmIzMTA3ODhhIn0.vQog__X9GHmfZSTpo_jtKQ_AsFq9idT0kpR7OLcAcmo"
}

## Re-Authentication [/v1/auth/fresh-login]

Access Tokens generated via refresh tokens are not fresh. To generate fresh tokens, you need to re-authenticate. However, it is not a good idea to use login endpoint for re-authentication since it will recreate refresh tokens, and also there may be mismatch of currently authenticated user and reauthenticating credentials.
This endpoint should be used with current access token in Authorization header to ensure that there is no mismatch. If the credentials are correct and matching with currently authenticated user, a new fresh access token will be generated.

### Generate fresh token [POST]

For mobile clients, dealing with cookies is not easy, and traditional problem of XSS and CSRF does not exist for them, so cookies are not required for them. They can send `true` in key `include-in-response`, which will not send cookies with request and send the refresh token in the response JSON

+ Request

+ Headers

Content-Type: application/json
Authorization: JWT <Auth Key>

+ Body

{
"email": "email@example.com",
"password": "password"
}

+ Response 200 (application/json)

{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjQ4NTQyODIsIm5iZiI6MTU2NDg1NDI4MiwianRpIjoiZGM2MjU3MjMtZjYyMi00YmYzLTgxMGQtYTVmZTljMWNhMDIyIiwiZXhwIjoxNTY0OTQwNjgyLCJpZGVudGl0eSI6MSwiZnJlc2giOnRydWUsInR5cGUiOiJhY2Nlc3MiLCJjc3JmIjoiMDFkNDI2MmYtOGRiZS00MWEwLWI2OWUtODY1M2QzNTRkYTUyIn0.lscegFJqTeqsfpqBNC6t2E2_A38JYqriQh5wixQQOtU"
}

## Token Refresh [/v1/auth/token/refresh]

**Note**: The access token generated by this method is not fresh. Which means it is good for all auth except sensitive APIs like changing password and changing email. This is done to increase security and prevent damage if a refresh token is leaked.
If a fresh token is required, use the fresh-login endpoint above to re-authenticate

### Access Token Refresh for Web [POST]

For web clients, the refresh token is sent as a cookie back to the server, so they don't need to attach it in the request, but they need to attach the value of `csrf_refresh_token` cookie as `X-CSRF-Token` header.
For security purposes, refresh token cookie is `HttpOnly`, so that JS cannot read it, mitigating XSS attacks, but to prevent CSRF attacks, `csrf_refresh_token` is used. So, it is readable by JS.

+ Request

+ Headers

X-CSRF-Token: e0b229be-629a-40b7-a0de-678f5aafd888

+ Response 200 (application/json)

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDk2OTU2ODMwLCJuYmYiOjE0OTY5NTY4MzAsImV4cCI6MTQ5NzA0MzIzMH0.bjl0"
}

### Access Token Refresh for mobile [POST]

For mobile clients, CSRF token is not provided and not needed. They just need to use the refresh token instead of access token in `Authorization` header. So, instead of `JWT <access_token>`, they should use `JWT <refresh_token>` for this endpoint.

+ Request

+ Headers

Authorization: JWT <refresh_token>

+ Response 200 (application/json)

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDk2OTU2ODMwLCJuYmYiOjE0OTY5NTY4MzAsImV4cCI6MTQ5NzA0MzIzMH0.bjl0"
}

## Logout [/v1/auth/logout]

**Optional**: Logout endpoint should only be used for web clients which use refresh tokens. Refresh Tokens and their CSRF tokens are stored in cookies and hence need to be explicitly cleared when logging out.

### Logout [POST]

+ Response 200 (application/json)

{
"success": true
}

## Blacklist Tokens [/v1/auth/blacklist]

Any refresh token that was issued before this request is made will be invalidated and will not be able to be used for creating new access tokens

### Blacklist Tokens [POST]

+ Response 200 (application/json)

{
"success": true
}
17 changes: 15 additions & 2 deletions tests/hook_main.py
Expand Up @@ -70,7 +70,7 @@
stash = {}
api_username = "open_event_test_user@fossasia.org"
api_password = "fossasia"
api_uri = "http://localhost:5000/auth/session"
api_uri = "http://localhost:5000/v1/auth/login"


def obtain_token():
Expand Down Expand Up @@ -130,9 +130,11 @@ def after_each(transaction):

# ------------------------- Authentication -------------------------
@hooks.before("Authentication > JWT Authentication > Authenticate and generate token")
@hooks.before("Authentication > JWT Authentication > Authenticate with remember me")
@hooks.before("Authentication > JWT Authentication > Authenticate with remember me for mobile")
def skip_auth(transaction):
"""
POST /auth/session
POST /v1/auth/login
:param transaction:
:return:
"""
Expand All @@ -144,6 +146,17 @@ def skip_auth(transaction):
print('User Created')


@hooks.before("Authentication > Re-Authentication > Generate fresh token")
@hooks.before("Authentication > Token Refresh > Access Token Refresh for Web")
@hooks.before("Authentication > Token Refresh > Access Token Refresh for mobile")
def skip_token_refresh(transaction):
"""
POST /v1/auth/token/refresh
:param transaction:
:return:
"""
transaction['skip'] = True

# ------------------------- Users -------------------------
@hooks.before("Users > Users Collection > List All Users")
def user_get_list(transaction):
Expand Down

0 comments on commit aa4147b

Please sign in to comment.