Skip to content

Commit edb0607

Browse files
committed
moving more github docs to read the docs
1 parent 45d98b2 commit edb0607

10 files changed

+399
-6
lines changed

docs/__init__.py

Whitespace-only changes.

docs/add_custom_data_claims.rst

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Adding Custom Data (Claims) to the Access Token
2+
===============================================
3+
4+
You may want to store additional information in the access token. Perhaps you want to save the access roles this user has so you can access them in the view functions (without having to make a database call each time). This can be done with the user_claims_loader decorator, and accessed later with the 'get_jwt_claims()' method (in a protected endpoint).
5+
6+
7+
.. code-block:: python
8+
9+
from flask import Flask, jsonify, request
10+
from flask_jwt_extended import JWTManager, jwt_required, create_access_token, \
11+
get_jwt_claims
12+
13+
app = Flask(__name__)
14+
app.secret_key = 'super-secret' # Change this!
15+
jwt = JWTManager(app)
16+
17+
18+
# Using the user_claims_loader, we can specify a method that will be called
19+
# when creating access tokens, and add these claims to the said token. This
20+
# method is passed the identity of who the token is being created for, and
21+
# must return data that is json serializable
22+
@jwt.user_claims_loader
23+
def add_claims_to_access_token(identity):
24+
return {
25+
'hello': identity,
26+
'foo': ['bar', 'baz']
27+
}
28+
29+
30+
@app.route('/login', methods=['POST'])
31+
def login():
32+
username = request.json.get('username', None)
33+
password = request.json.get('password', None)
34+
if username != 'test' and password != 'test':
35+
return jsonify({"msg": "Bad username or password"}), 401
36+
37+
ret = {'access_token': create_access_token(username)}
38+
return jsonify(ret), 200
39+
40+
41+
# In a protected view, get the claims you added to the jwt with the
42+
# get_jwt_claims() method
43+
@app.route('/protected', methods=['GET'])
44+
@jwt_required
45+
def protected():
46+
claims = get_jwt_claims()
47+
return jsonify({
48+
'hello_is': claims['hello'],
49+
'foo_is': claims['foo']
50+
}), 200
51+
52+
if __name__ == '__main__':
53+
app.run()
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
Blacklist and Token Revoking
2+
============================
3+
4+
This supports optional blacklisting and token revoking out of the box. This will allow you to revoke a specific token so a user can no longer access your endpoints. In order to revoke a token, we need some storage where we can save a list of all the tokens we have created, as well as if they have been revoked or not. In order to make the underlying storage as agnostic as possible, we use simplekv to provide assess to a variety of backends.
5+
6+
In production, it is important to use a backend that can have some sort of persistent storage, so we don't 'forget' that we revoked a token if the flask process is restarted. We also need something that can be safely used by the multiple thread and processes running your application. At present we believe redis is a good fit for this. It has the added benefit of removing expired tokens from the store automatically, so it wont blow up into something huge.
7+
8+
We also have choose what tokens we want to check against the blacklist. We could check all tokens (refresh and access), or only the refresh tokens. There are pros and cons to either way (extra overhead on jwt_required endpoints vs someone being able to use an access token freely until it expires). In this example, we are going to only check refresh tokens, and set the access tokes to a small expires time to help minimize damage that could be done with a stolen access token.
9+
10+
.. code-block:: python
11+
12+
from datetime import timedelta
13+
14+
15+
import simplekv
16+
import simplekv.memory
17+
from flask import Flask, request, jsonify
18+
19+
from flask_jwt_extended import JWTManager, jwt_required, \
20+
get_jwt_identity, revoke_token, unrevoke_token, \
21+
get_stored_tokens, get_all_stored_tokens, create_access_token, \
22+
create_refresh_token, jwt_refresh_token_required
23+
24+
# Setup flask
25+
app = Flask(__name__)
26+
app.secret_key = 'super-secret'
27+
28+
# Configure access token expires time
29+
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=5)
30+
31+
# Enable and configure the JWT blacklist / token revoke. We are using an in
32+
# memory store for this example. In production, you should use something
33+
# persistant (such as redis, memcached, sqlalchemy). See here for options:
34+
# http://pythonhosted.org/simplekv/
35+
app.config['JWT_BLACKLIST_ENABLED'] = True
36+
app.config['JWT_BLACKLIST_STORE'] = simplekv.memory.DictStore()
37+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'refresh'
38+
39+
jwt = JWTManager(app)
40+
41+
42+
@app.route('/login', methods=['POST'])
43+
def login():
44+
username = request.json.get('username', None)
45+
password = request.json.get('password', None)
46+
if username != 'test' and password != 'test':
47+
return jsonify({"msg": "Bad username or password"}), 401
48+
49+
ret = {
50+
'access_token': create_access_token(identity=username),
51+
'refresh_token': create_refresh_token(identity=username)
52+
}
53+
return jsonify(ret), 200
54+
55+
56+
@app.route('/refresh', methods=['POST'])
57+
@jwt_refresh_token_required
58+
def refresh():
59+
current_user = get_jwt_identity()
60+
ret = {
61+
'access_token': create_access_token(identity=current_user)
62+
}
63+
return jsonify(ret), 200
64+
65+
66+
# Endpoint for listing tokens that have the same identity as you
67+
@app.route('/auth/tokens', methods=['GET'])
68+
@jwt_required
69+
def list_identity_tokens():
70+
username = get_jwt_identity()
71+
return jsonify(get_stored_tokens(username)), 200
72+
73+
74+
# Endpoint for listing all tokens. In your app, you should either not expose
75+
# this endpoint, or put some addition security on top of it so only trusted users,
76+
# (administrators, etc) can access it
77+
@app.route('/auth/all-tokens')
78+
def list_all_tokens():
79+
return jsonify(get_all_stored_tokens()), 200
80+
81+
82+
# Endpoint for allowing users to revoke their tokens
83+
@app.route('/auth/tokens/revoke/<string:jti>', methods=['PUT'])
84+
@jwt_required
85+
def change_jwt_revoke_state(jti):
86+
username = jwt_get_identity()
87+
try:
88+
token_data = get_stored_token(jti)
89+
if token_data['token']['identity'] != username:
90+
raise KeyError
91+
revoke_token(jti)
92+
return jsonify({"msg": "Token successfully revoked"}), 200
93+
except KeyError:
94+
return jsonify({'msg': 'Token not found'}), 404
95+
96+
97+
# Endpoint for allowing users to unrevoke their tokens
98+
@app.route('/auth/tokens/unrevoke/<string:jti>', methods=['PUT'])
99+
@jwt_required
100+
def change_jwt_unrevoke_state(jti):
101+
username = jwt_get_identity()
102+
try:
103+
token_data = get_stored_token(jti)
104+
if token_data['token']['identity'] != username:
105+
raise KeyError
106+
unrevoke_token(jti)
107+
return jsonify({"msg": "Token successfully unrevoked"}), 200
108+
except KeyError:
109+
return jsonify({'msg': 'Token not found'}), 404
110+
111+
112+
@app.route('/protected', methods=['GET'])
113+
@jwt_required
114+
def protected():
115+
return jsonify({'hello': 'world'})
116+
117+
if __name__ == '__main__':
118+
app.run()

docs/changing_default_behavior.rst

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
Changing Default Behaviors
2+
==========================
3+
4+
5+
We provide what we think are sensible behaviors when attempting to access a protected endpoint. If the access token is not valid for any reason (missing, expired, tampered with, etc) we will return json in the format of {'msg': 'why accessing endpoint failed'} along with an appropriate http status code (generally 401 or 422). However, you may want to customize what you returned in these situations. We can do that with the jwt_manager _loader functions.
6+
7+
8+
.. code-block:: python
9+
10+
from flask import Flask, jsonify, request
11+
from flask_jwt_extended import JWTManager, jwt_required, create_access_token
12+
13+
app = Flask(__name__)
14+
app.secret_key = 'super-secret' # Change this!
15+
jwt = JWTManager(app)
16+
17+
18+
# Use the expired_token_loader to call this function whenever an expired but
19+
# otherwise valid access token tries to access an endpoint
20+
@jwt.expired_token_loader
21+
def my_expired_token_callback():
22+
return jsonify({
23+
'status': 401,
24+
'sub_status': 101,
25+
'msg': 'The token has expired'
26+
}), 200
27+
28+
29+
@app.route('/login', methods=['POST'])
30+
def login():
31+
username = request.json.get('username', None)
32+
password = request.json.get('password', None)
33+
if username != 'test' and password != 'test':
34+
return jsonify({"msg": "Bad username or password"}), 401
35+
36+
ret = {'access_token': create_access_token(username)}
37+
return jsonify(ret), 200
38+
39+
40+
@app.route('/protected', methods=['GET'])
41+
@jwt_required
42+
def protected():
43+
return jsonify({'hello': 'world'}), 200
44+
45+
if __name__ == '__main__':
46+
app.run()
47+
48+
49+
50+
************************************
51+
Loader functions are:
52+
************************************
53+
54+
.. automodule:: jwt_manager.py
55+
:members:
56+
57+
.. literalinclude:: ../flask_jwt_extended/jwt_manager.py
58+
:language: python
59+
:emphasize-lines: 60-122
60+
:linenos:

docs/conf.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
#
33
import sphinx_rtd_theme
4-
4+
import sys, os
55
# flask-jwt-extended documentation build configuration file, created by
66
# sphinx-quickstart on Thu Oct 6 13:07:36 2016.
77
#
@@ -18,9 +18,14 @@
1818
# add these directories to sys.path here. If the directory is relative to the
1919
# documentation root, use os.path.abspath to make it absolute, like shown here.
2020
#
21-
# import os
22-
# import sys
21+
22+
2323
# sys.path.insert(0, os.path.abspath('.'))
24+
sys.path.insert(0, os.path.abspath('../../..'))
25+
sys.path.insert(0, os.path.abspath('../flask_jwt_extended/'))
26+
# sys.path.insert(0, os.path.abspath('../../flask-jwt-extended/flask_jwt_extended/'))
27+
28+
print sys.path
2429

2530
# -- General configuration ------------------------------------------------
2631

@@ -31,8 +36,13 @@
3136
# Add any Sphinx extension module names here, as strings. They can be
3237
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3338
# ones.
34-
extensions = []
35-
39+
extensions = [
40+
'sphinx.ext.autodoc',
41+
'sphinx.ext.intersphinx',
42+
'sphinx.ext.ifconfig',
43+
'sphinx.ext.viewcode',
44+
'sphinx.ext.autosummary'
45+
]
3646
# Add any paths that contain templates here, relative to this directory.
3747
templates_path = ['_templates']
3848

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Welcome to flask-jwt-extended's documentation!
1818
changing_default_behavior
1919
options
2020
blacklist_and_token_revoking
21-
testing_and_code_coverage
21+
testing_and_coverage
2222

2323

2424

docs/options.rst

Whitespace-only changes.

docs/refresh_tokens.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
Refresh Tokens
2+
==============
3+
4+
Flask-JWT-Extended supports refresh tokens out of the box. These are longer lived token which cannot access a jwt_required protected endpoint, but can be used to create new access tokens once an old access token has expired. By setting the access tokens to a shorter lifetime (see Options below), and utilizing fresh tokens for critical views (see Fresh Tokens below) we can help reduce the damage done if an access token is stolen. Here is an example of how you might use them in your application:
5+
6+
7+
.. code-block:: python
8+
9+
from flask import Flask, jsonify, request
10+
from flask_jwt_extended import JWTManager, jwt_required, create_access_token, \
11+
jwt_refresh_token_required, create_refresh_token, get_jwt_identity
12+
13+
app = Flask(__name__)
14+
app.secret_key = 'super-secret' # Change this!
15+
jwt = JWTManager(app)
16+
17+
18+
@app.route('/login', methods=['POST'])
19+
def login():
20+
username = request.json.get('username', None)
21+
password = request.json.get('password', None)
22+
if username != 'test' and password != 'test':
23+
return jsonify({"msg": "Bad username or password"}), 401
24+
25+
# Use create_access_token() and create_refresh_token() to create our
26+
# access and refresh tokens
27+
ret = {
28+
'access_token': create_access_token(identity=username),
29+
'refresh_token': create_refresh_token(identity=username)
30+
}
31+
return jsonify(ret), 200
32+
33+
34+
# The jwt_refresh_token_required decorator insures a valid refresh token is
35+
# present in the request before calling this endpoint. We can use the
36+
# get_jwt_identity() function to get the identity of the refresh toke, and use
37+
# the create_access_token() function again to make a new access token for this
38+
# identity.
39+
@app.route('/refresh', methods=['POST'])
40+
@jwt_refresh_token_required
41+
def refresh():
42+
current_user = get_jwt_identity()
43+
ret = {
44+
'access_token': create_access_token(identity=current_user)
45+
}
46+
return jsonify(ret), 200
47+
48+
49+
@app.route('/protected', methods=['GET'])
50+
@jwt_required
51+
def protected():
52+
username = get_jwt_identity()
53+
return jsonify({'hello': 'from {}'.format(username)}), 200
54+
55+
if __name__ == '__main__':
56+
app.run()

docs/testing_and_coverage.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Testing and Code Coverage
2+
=========================
3+
4+
We run all the unit tests with tox. This will test against python2.7, and 3.5 (although not tested, python3.3 and 3.4 should also be fully supported). This will also print out a code coverage report.
5+
6+
.. code-block:: bash
7+
8+
$ tox

0 commit comments

Comments
 (0)