Skip to content

Commit 5244635

Browse files
author
Landon Gilbert-Bland
committed
Finish documentation for jwt locations
1 parent b41d50c commit 5244635

File tree

4 files changed

+51
-132
lines changed

4 files changed

+51
-132
lines changed

docs/options.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ These are only applicable if ``JWT_TOKEN_LOCATION`` is set to use json data.
130130
================================= =========================================
131131

132132

133-
Cross Site Request Forgery Options:
133+
.. _Cross Site Request Forgery Options:
134+
135+
Cross Site Request Forgery Options
134136
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135137
These are only applicable if ``JWT_TOKEN_LOCATION`` is set to use cookies and
136138
``JWT_COOKIE_CSRF_PROTECT`` is True.

docs/token_locations.rst

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
JWT Locations
22
=============
3-
JWTs can be passed sent in many different ways to a protected route. These
4-
available ways the tokens can be passed in can be controlled globally via
5-
the `app.config["JWT_TOKEN_LOCATION']` option, or overridden on a per route
6-
basis via the `locations` argument in :func:`~flask_jwt_extended.jwt_required`.
7-
Lets see how working with different locations look like in javascript:
3+
JWTs can be sent in with a request in many different ways. You can control which
4+
ways you want to accept JWTs in your Flask application via the `JWT_TOKEN_LOCATION`
5+
:ref:`configuration option<Configuration Options>`. You can also override that global
6+
configuration on a per route basis via the `locations` argument in
7+
:func:`~flask_jwt_extended.jwt_required`.
88

99
.. literalinclude:: ../examples/jwt_locations.py
1010

11+
Lets take a look at how you could utilize all of these locations using some
12+
javascript in a web browser.
13+
1114
Headers
1215
~~~~~~~
1316
Working JWTs via headers is a pretty simple process. All you need to do is store
@@ -44,30 +47,40 @@ Cookies
4447
Cookies are a fantastic way of handling JWTs if you are using a web browser.
4548
They offer some nice benefits compared to the headers approach:
4649

47-
* They can be set to send only if you are on an HTTPS connection. This prevents a
48-
JWT from accidentally being leaked by being sent over an unsecure connection.
50+
* They can be configured to send only over HTTPS. This prevents a JWT from
51+
accidentally being sent, and possibly compromised, over an unsecure connection.
4952
* They are stored in an http-only cookie, which prevents XSS attacks from being
5053
able to steal the underlying JWT.
51-
* You flask application can implicitly refresh JWTs that are close to expiring,
54+
* You Flask application can implicitly refresh JWTs that are close to expiring,
5255
which simplifies the logic of keeping active users logged in. More on this in
5356
the next section!
5457

5558
Of course, when using cookies you also need to do some additional work to prevent
56-
Cross Site Request Forgery (CSRF) attacks. In this extension we do this by utilizing
57-
the double submit verification method. The basic idea behind this is that we are
58-
going to save two cookies when logging in. The first cookie contains the access
59-
token, and encoded in the access token is double submit token. This cookie is
60-
set as http-only, so javascript cannot access the cookie to decode the double
61-
submit token. The second cookie we save contains only the same double submit
62-
token, but this time in a cookie that is readable by javascript. Whenever a
63-
request is made, it needs to include an `X-CSRF-TOKEN` header, with the value
64-
of the double submit token. If the value in this header does not match the value
65-
stored in the access token, the request is kicked out as invalid. This prevents
66-
any CSRF attacks, because although they can implictitly send in the JWT as part
67-
of the request, they have no way to also include the double submit token.
59+
Cross Site Request Forgery (CSRF) attacks. In this extension we handle this via
60+
something called double submit verification.
61+
62+
The basic idea behind double submit verification is that a JWT coming from a
63+
cookie will only be considered valid if a special double submit token is also
64+
present in the request, and that double submit token must not be something that
65+
is automatically sent by a web browser (ie it cannot be another cookie).
66+
67+
By default, we accomplish this by setting two cookies when someone logging in.
68+
The first cookie contains the JWT, and encoded in that JWT is the double submit
69+
token. This cookie is set as http-only, so that it cannot be access via javascript
70+
(this is what prevents XSS attacks from being able to steal the JWT). The second
71+
cookie we set contains only the same double submit token, but this time in a
72+
cookie that is readable by javascript. Whenever a request is made, it needs to
73+
include an `X-CSRF-TOKEN` header, with the value of the double submit token.
74+
If the value in this header does not match the value stored in the JWT, the
75+
request is kicked out as invalid.
76+
77+
Because the double submit token needs to be present as a header (which wont be
78+
automatically sent on a request), and some malicious javascript running on a
79+
different domain will not be able to read the cookie containing the double submit
80+
token on your website, we have successfully thwarted any CSRF attacks.
6881

6982
This does mean that whenever you are making a request, you need to manually
70-
include the double submit token header, otherwise your requests will be kicked
83+
include the `X-CSRF-TOKEN` header, otherwise your requests will be kicked
7184
out as invalid too. Lets look at how to do that:
7285

7386
.. code-block :: javascript
@@ -98,6 +111,11 @@ out as invalid too. Lets look at how to do that:
98111
return result;
99112
}
100113
114+
Note that there are additional CSRF options, such as looking for the double
115+
submit token in a form, changing cookie paths, etc, that can be used to
116+
tailor things to the needs of your application. See
117+
:ref:`Cross Site Request Forgery Options` for details.
118+
101119

102120
Query String
103121
~~~~~~~~~~~~~
@@ -122,7 +140,8 @@ in this extension.
122140
}
123141
124142
async function makeRequestWithJWT() {
125-
const response = await fetch('/protected', {method: 'post'});
143+
const jwt = localStorage.getItem('jwt')
144+
const response = await fetch(`/protected?jwt=${jwt}`, {method: 'post'});
126145
const result = await response.json();
127146
return result;
128147
}
@@ -164,7 +183,3 @@ we include the option for it regardless.
164183
const result = await response.json();
165184
return result;
166185
}
167-
168-
169-
Overwriting Locations On a Per Route Basis
170-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

examples/csrf_protection_with_cookies.py

Lines changed: 0 additions & 104 deletions
This file was deleted.

examples/jwt_locations.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
app = Flask(__name__)
1111

12-
# Here you can globally configure all the way you want to allow JWTs to
12+
# Here you can globally configure all the ways you want to allow JWTs to
1313
# be sent to your web application. By default, this will be only headers.
1414
app.config["JWT_TOKEN_LOCATION"] = ["headers", "cookies", "json", "query_string"]
1515

@@ -50,5 +50,11 @@ def protected():
5050
return jsonify(foo="bar")
5151

5252

53+
@app.route("/only_headers")
54+
@jwt_required(locations=["headers"])
55+
def only_headers():
56+
return jsonify(foo="baz")
57+
58+
5359
if __name__ == "__main__":
5460
app.run()

0 commit comments

Comments
 (0)