Skip to content

Commit

Permalink
Merge pull request flavors#165 from flavors/feature/refresh-and-cooki…
Browse files Browse the repository at this point in the history
…es-improvements

Refresh and cookies improvements
  • Loading branch information
mongkok authored Feb 3, 2020
2 parents 06441be + 95b04cb commit 95ac4af
Show file tree
Hide file tree
Showing 37 changed files with 926 additions and 273 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cache: pip
matrix:
fast_finish: true
include:
- python: 3.7
- python: 3.8
env: TOXENV=flake8

# Django 1.11
Expand Down
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ test:
@pytest tests

coverage:
@pytest\
--verbose\
--cov graphql_jwt\
--cov-config .coveragerc\
--cov-report term\
@pytest \
--verbose \
--cov graphql_jwt \
--cov-config .coveragerc \
--cov-report term \
--cov-report xml

test-all:
Expand Down
43 changes: 43 additions & 0 deletions docs/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,49 @@ When a token is requested and ``jwt_cookie`` decorator is set, the response will

If the ``jwt_cookie`` decorator is set, consider adding `CSRF middleware <https://docs.djangoproject.com/es/2.1/ref/csrf/>`_ ``'django.middleware.csrf.CsrfViewMiddleware'`` to provide protection against `Cross Site Request Forgeries <https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)>`_.

A cookie-based authentication does not require sending the tokens as a mutation input argument.

Delete Cookies
~~~~~~~~~~~~~~

In order to prevent XSS (cross-site scripting) attacks, cookies have the ``HttpOnly`` flag set, so you cannot delete them on the client-side. This package includes some mutations to delete the cookies on the server-side.

Add mutations to the root schema::

import graphene
import graphql_jwt


class Mutation(graphene.ObjectType):
delete_token_cookie = graphql_jwt.DeleteJSONWebTokenCookie.Field()

# Long running refresh tokens
delete_refresh_token_cookie = \
graphql_jwt.refresh_token.DeleteRefreshTokenCookie.Field()


schema = graphene.Schema(mutation=Mutation)


* ``deleteTokenCookie`` to delete the ``JWT`` cookie:

::

mutation {
deleteTokenCookie {
deleted
}
}

* ``deleteRefreshTokenCookie`` to delete ``JWT-refresh-token`` cookie for :doc:`long running refresh tokens<refresh_token>`.

::

mutation {
deleteRefreshTokenCookie {
deleted
}
}

Per-argument
------------
Expand Down
1 change: 1 addition & 0 deletions docs/customizing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Authenticate the user and obtain a **JSON Web Token** and the *user id*::
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
payload
user {
id
}
Expand Down
2 changes: 2 additions & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Queries
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
payload
refreshExpiresIn
}
}

Expand Down
16 changes: 10 additions & 6 deletions docs/refresh_token.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Refresh token
This package supports two refresh methods:

* `Single token refresh <#single-token-refresh>`__ (by default)
* `Long running refresh tokens <#long-running-refresh-tokens>`__ (`django-graphql-jwt` ≥ v0.1.14)
* `Long running refresh tokens <#long-running-refresh-tokens>`__

Single token refresh
--------------------
Expand All @@ -20,7 +20,7 @@ Settings
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7),
}

It means that you need to refresh every 5 mins and even you keep on refreshing token every 5 mins, you will still be logout in 7 days after the first token has been issued.
It means that you need to refresh every 5 mins (``payload.exp``) and even you keep on refreshing token every 5 mins, you will still be logout in 7 days after the first token has been issued (``refreshExpiresIn``).

Queries
~~~~~~~
Expand All @@ -33,6 +33,7 @@ Queries
refreshToken(token: $token) {
token
payload
refreshExpiresIn
}
}

Expand All @@ -50,7 +51,7 @@ Queries

::

exp = orig_iat + JWT_EXPIRATION_DELTA
exp = orig_iat + JWT_EXPIRATION_DELTA (payload.exp)
refreshToken (t): exp = t + JWT_EXPIRATION_DELTA

2. Signature expiration (login is required)
Expand All @@ -65,7 +66,7 @@ Queries

::

when: t = orig_iat + JWT_REFRESH_EXPIRATION_DELTA
when: t = orig_iat + JWT_REFRESH_EXPIRATION_DELTA (refreshExpiresIn)
refreshToken (t): error!

Long running refresh tokens
Expand Down Expand Up @@ -93,7 +94,7 @@ Settings
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7),
}

It means that you need to refresh every 5 mins and you need to replace your refresh token in 7 days after it has been issued.
It means that you need to refresh every 5 mins (``payload.exp``) and you need to replace your refresh token in 7 days after it has been issued (``refreshExpiresIn``).

Schema
~~~~~~
Expand Down Expand Up @@ -122,7 +123,9 @@ Queries
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
payload
refreshToken
refreshExpiresIn
}
}

Expand All @@ -134,8 +137,9 @@ Queries
mutation RefreshToken($refreshToken: String!) {
refreshToken(refreshToken: $refreshToken) {
token
refreshToken
payload
refreshToken
refreshExpiresIn
}
}

Expand Down
36 changes: 35 additions & 1 deletion docs/relay.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ Add mutations to the root schema::
token_auth = graphql_jwt.relay.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.relay.Verify.Field()
refresh_token = graphql_jwt.relay.Refresh.Field()
delete_token_cookie = graphql_jwt.relay.DeleteJSONWebTokenCookie.Field()

# Long running refresh tokens
revoke_token = graphql_jwt.relay.Revoke.Field()

delete_refresh_token_cookie = \
graphql_jwt.refresh_token.relay.DeleteRefreshTokenCookie.Field()


schema = graphene.Schema(mutation=Mutation)

Expand All @@ -37,6 +41,8 @@ Relay mutations only accepts one argument named *input*.
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(input: {username: $username, password: $password}) {
token
payload
refreshExpiresIn
}
}

Expand All @@ -62,6 +68,7 @@ Single token refresh
refreshToken(input: {token: $token}) {
token
payload
refreshExpiresIn
}
}

Expand All @@ -76,8 +83,9 @@ Long running refresh tokens
mutation RefreshToken($refreshToken: String!) {
refreshToken(input: {refreshToken: $refreshToken}) {
token
refreshToken
payload
refreshToken
refreshExpiresIn
}
}

Expand All @@ -92,6 +100,30 @@ Long running refresh tokens
}


Cookies
~~~~~~~

* ``deleteTokenCookie`` to delete the ``JWT`` cookie:

::

mutation {
deleteTokenCookie(input: {}) {
deleted
}
}

* ``deleteRefreshTokenCookie`` to delete ``JWT-refresh-token`` cookie for :doc:`long running refresh tokens<refresh_token>`.

::

mutation {
deleteRefreshTokenCookie(input: {}) {
deleted
}
}


Customizing
-----------

Expand All @@ -117,6 +149,8 @@ Authenticate the user and obtain a **JSON Web Token** and the *user id*::
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(input: {username: $username, password: $password}) {
token
payload
refreshExpiresIn
user {
id
}
Expand Down
48 changes: 46 additions & 2 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,19 @@ JWT_REFRESH_TOKEN_MODEL
JWT_REFRESH_TOKEN_N_BYTES
~~~~~~~~~~~~~~~~~~~~~~~~~

Refresh token number of bytes
Long running refresh token number of bytes

Default: ``20``


JWT_REUSE_REFRESH_TOKENS
~~~~~~~~~~~~~~~~~~~~~~~~

Reuse the long running refreshed token instead of generating a new one

Default: ``False``


JWT_REFRESH_EXPIRED_HANDLER
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -248,6 +256,7 @@ JWT_COOKIE_NAME

Default: ``'JWT'``


JWT_REFRESH_TOKEN_COOKIE_NAME
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -259,7 +268,42 @@ JWT_REFRESH_TOKEN_COOKIE_NAME
JWT_COOKIE_SECURE
~~~~~~~~~~~~~~~~~

Whether to use a secure cookie for the JWT cookie. If this is set to True, the cookie will be marked as "secure", which means browsers may ensure that the cookie is only sent under an HTTPS connection.
Whether to use a secure cookie for the JWT cookie. If this is set to True, the cookie will be marked as "secure", which means browsers may ensure that the cookie is only sent under an HTTPS connection

Default: ``False``


JWT_COOKIE_PATH
~~~~~~~~~~~~~~~~~

Document location for the cookie

Default: ``'/'``


JWT_COOKIE_DOMAIN
~~~~~~~~~~~~~~~~~

Use domain if you want to set a cross-domain cookie

Default: ``None``


JWT_HIDE_TOKEN_FIELDS
~~~~~~~~~~~~~~~~~~~~~

For cookie-based authentications, remove the token fields from the GraphQL schema in order to prevent XSS exploitation

Default: ``False``


CSRF
----

JWT_CSRF_ROTATION
~~~~~~~~~~~~~~~~~

Rotate CSRF tokens each time a token or refresh token is issued

Default: ``False``

Expand Down
3 changes: 2 additions & 1 deletion docs/signals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ Sent when a long running refresh token has been rotated.
Arguments sent with this signal:
- sender: The class of the refresh_token that just rotated.
- request: The current HttpRequest instance.
- refresh_token: The RefreshToken instance that just rotated.
- refresh_token: The old RefreshToken instance that just rotated.
- refresh_token_issued: The new RefreshToken instance issued.


refresh_token_revoked
Expand Down
5 changes: 4 additions & 1 deletion graphql_jwt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from . import relay
from .mutations import (
JSONWebTokenMutation, ObtainJSONWebToken, Refresh, Revoke, Verify,
DeleteJSONWebTokenCookie, DeleteRefreshTokenCookie, JSONWebTokenMutation,
ObtainJSONWebToken, Refresh, Revoke, Verify,
)

__all__ = [
Expand All @@ -10,6 +11,8 @@
'Verify',
'Refresh',
'Revoke',
'DeleteJSONWebTokenCookie',
'DeleteRefreshTokenCookie',
]

__version__ = '0.3.0'
Loading

0 comments on commit 95ac4af

Please sign in to comment.