-
-
Notifications
You must be signed in to change notification settings - Fork 247
Added a fourth lookup within the json body #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| JWT in JSON Body | ||
| ================ | ||
|
|
||
| You can also pass the token as an attribute in the body of an `application/json` request. | ||
| However, since the body is meaningless in a `GET` request, this is mostly useful for | ||
| protecting routes that only accept `POST`, `PATCH`, or `DELETE` methods. | ||
|
|
||
| That is to say, the `GET` method will become essentially unauthorized in any protected route | ||
| if you only use this lookup method. | ||
|
|
||
| If you decide to use JWTs in the request body, here is an example of how it might look: | ||
|
|
||
| .. literalinclude:: ../examples/jwt_in_json.py |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| from flask import Flask, jsonify, request | ||
|
|
||
| from flask_jwt_extended import ( | ||
| JWTManager, jwt_required, create_access_token, | ||
| ) | ||
|
|
||
| app = Flask(__name__) | ||
|
|
||
| # IMPORTANT: Body is meaningless in GET requests, so using json | ||
| # as the only lookup method means that the GET method will become | ||
| # unauthorized in any protected route, as there's no body to look for. | ||
|
|
||
| app.config['JWT_TOKEN_LOCATION'] = ['json'] | ||
| app.config['JWT_SECRET_KEY'] = 'super-secret' # Change this! | ||
|
|
||
| jwt = JWTManager(app) | ||
|
|
||
|
|
||
| @app.route('/login', methods=['POST']) | ||
| def login(): | ||
| username = request.json.get('username', None) | ||
| password = request.json.get('password', None) | ||
| if username != 'test' or password != 'test': | ||
| return jsonify({"msg": "Bad username or password"}), 401 | ||
|
|
||
| access_token = create_access_token(identity=username) | ||
| return jsonify(access_token=access_token) | ||
|
|
||
|
|
||
| # The default attribute name where the JWT is looked for is `access_token`, | ||
| # and can be changed with the JWT_JSON_KEY option. | ||
| # Notice how the route is unreachable with GET requests. | ||
| @app.route('/protected', methods=['GET', 'POST']) | ||
| @jwt_required | ||
| def protected(): | ||
| return jsonify(foo='bar') | ||
|
|
||
| if __name__ == '__main__': | ||
| app.run() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import pytest | ||
| from flask import Flask, jsonify | ||
|
|
||
| from flask_jwt_extended import JWTManager, jwt_required, jwt_refresh_token_required, create_access_token, create_refresh_token | ||
| from tests.utils import get_jwt_manager | ||
|
|
||
|
|
||
| @pytest.fixture(scope='function') | ||
| def app(): | ||
| app = Flask(__name__) | ||
| app.config['JWT_SECRET_KEY'] = 'foobarbaz' | ||
| app.config['JWT_TOKEN_LOCATION'] = 'json' | ||
| JWTManager(app) | ||
|
|
||
| @app.route('/protected', methods=['POST']) | ||
| @jwt_required | ||
| def access_protected(): | ||
| return jsonify(foo='bar') | ||
|
|
||
| @app.route('/refresh', methods=['POST']) | ||
| @jwt_refresh_token_required | ||
| def refresh_protected(): | ||
| return jsonify(foo='bar') | ||
|
|
||
| return app | ||
|
|
||
|
|
||
| def test_content_type(app): | ||
| test_client = app.test_client() | ||
|
|
||
| with app.test_request_context(): | ||
| access_token = create_access_token('username') | ||
| refresh_token = create_refresh_token('username') | ||
|
|
||
| data = {'access_token': access_token} | ||
| response = test_client.post('/protected', data=data) | ||
|
|
||
| assert response.status_code == 401 | ||
| assert response.get_json() == {'msg': 'Invalid content-type. Must be application/json.'} | ||
|
|
||
| data = {'refresh_token': refresh_token} | ||
| response = test_client.post('/refresh', data=data) | ||
|
|
||
| assert response.status_code == 401 | ||
| assert response.get_json() == {'msg': 'Invalid content-type. Must be application/json.'} | ||
|
|
||
|
|
||
| def test_custom_body_key(app): | ||
| app.config['JWT_JSON_KEY'] = 'Foo' | ||
| app.config['JWT_REFRESH_JSON_KEY'] = 'Bar' | ||
| test_client = app.test_client() | ||
|
|
||
| with app.test_request_context(): | ||
| access_token = create_access_token('username') | ||
| refresh_token = create_refresh_token('username') | ||
|
|
||
| # Ensure 'default' keys no longer work | ||
| data = {'access_token': access_token} | ||
| response = test_client.post('/protected', json=data) | ||
| assert response.status_code == 401 | ||
| assert response.get_json() == {'msg': 'Missing "Foo" key in json data.'} | ||
|
|
||
|
|
||
| data = {'refresh_token': refresh_token} | ||
| response = test_client.post('/refresh', json=data) | ||
| assert response.status_code == 401 | ||
| assert response.get_json() == {'msg': 'Missing "Bar" key in json data.'} | ||
|
|
||
| # Ensure new keys do work | ||
| data = {'Foo': access_token} | ||
| response = test_client.post('/protected', json=data) | ||
| assert response.status_code == 200 | ||
| assert response.get_json() == {'foo': 'bar'} | ||
|
|
||
| data = {'Bar': refresh_token} | ||
| response = test_client.post('/refresh', json=data) | ||
| assert response.status_code == 200 | ||
| assert response.get_json() == {'foo': 'bar'} | ||
|
|
||
|
|
||
| def test_missing_keys(app): | ||
| test_client = app.test_client() | ||
| jwtM = get_jwt_manager(app) | ||
| headers = {'content-type': 'application/json'} | ||
|
|
||
| # Ensure 'default' no json response | ||
| response = test_client.post('/protected', headers=headers) | ||
| assert response.status_code == 401 | ||
| assert response.get_json() == {'msg': 'Missing "access_token" key in json data.'} | ||
|
|
||
| # Test custom no json response | ||
| @jwtM.unauthorized_loader | ||
| def custom_response(err_str): | ||
| return jsonify(foo='bar'), 201 | ||
|
|
||
| response = test_client.post('/protected', headers=headers) | ||
| assert response.status_code == 201 | ||
| assert response.get_json() == {'foo': "bar"} | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we add one more unit test in this file as well? https://github.com/vimalloc/flask-jwt-extended/blob/master/tests/test_multiple_token_locations.py The |
||
|
|
||
| def test_defaults(app): | ||
| test_client = app.test_client() | ||
|
|
||
| with app.test_request_context(): | ||
| access_token = create_access_token('username') | ||
| refresh_token = create_refresh_token('username') | ||
|
|
||
| data = {'access_token': access_token} | ||
| response = test_client.post('/protected', json=data) | ||
| assert response.status_code == 200 | ||
| assert response.get_json() == {'foo': 'bar'} | ||
|
|
||
| data = {'refresh_token': refresh_token} | ||
| response = test_client.post('/refresh', json=data) | ||
| assert response.status_code == 200 | ||
| assert response.get_json() == {'foo': 'bar'} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add one more unit test to this file, this working with the default json keys?