Skip to content

Commit

Permalink
Fixed a minor grammatical error, nobody is getting insurance money fr…
Browse files Browse the repository at this point in the history
…om this:)
  • Loading branch information
tgross35 committed Jan 8, 2022
1 parent a6ab61d commit faadc95
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 83 deletions.
47 changes: 29 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,73 @@
# Flask-JWT-Extended

### Features

Flask-JWT-Extended not only adds support for using JSON Web Tokens (JWT) to Flask for protecting routes,
but also many helpful (and **optional**) features built in to make working with JSON Web Tokens
but also many helpful (and **optional**) features built in to make working with JSON Web Tokens
easier. These include:

* Adding custom claims to JSON Web Tokens
* Automatic user loading (`current_user`).
* Custom claims validation on received tokens
* [Refresh tokens](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/)
* First class support for fresh tokens for making sensitive changes.
* Token revoking/blocklisting
* Storing tokens in cookies and CSRF protection
- Adding custom claims to JSON Web Tokens
- Automatic user loading (`current_user`).
- Custom claims validation on received tokens
- [Refresh tokens](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/)
- First class support for fresh tokens for making sensitive changes.
- Token revoking/blocklisting
- Storing tokens in cookies and CSRF protection

### Usage

[View the documentation online](https://flask-jwt-extended.readthedocs.io/en/stable/)

### Upgrading from 3.x.x to 4.0.0

[View the changes](https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/)

### Changelog

You can view the changelog [here](https://github.com/vimalloc/flask-jwt-extended/releases).
This project follows [semantic versioning](https://semver.org/).

### Chatting

Come chat with the community or ask questions at https://discord.gg/EJBsbFd

### Contributing

Before making any changes, make sure to install the development requirements
and setup the git hooks which will automatically lint and format your changes.

```bash
pip install -r requirements.txt
pre-commit install
```

We require 100% code coverage in our unit tests. You can run the tests locally
with `tox` which insures that all tests pass, tests provide complete code coverage,
with `tox` which ensures that all tests pass, tests provide complete code coverage,
documentation builds, and style guide are adhered to

```bash
tox
```

A subset of checks can also be ran by adding an argument to tox. The available
arguments are:
* py36, py37, py38, py39, pypy3
* Run unit tests on the given python version
* coverage
* Run a code coverage check
* docs
* Insure documentation builds and there are no broken links
* style
* Insure style guide is adhered to

- py36, py37, py38, py39, pypy3
- Run unit tests on the given python version
- coverage
- Run a code coverage check
- docs
- Ensure documentation builds and there are no broken links
- style
- Ensure style guide is adhered to

```bash
tox -e py38
```

We also require features to be well documented. You can generate a local copy
We also require features to be well documented. You can generate a local copy
of the documentation by going to the `docs` directory and running:

```bash
make clean && make html && open _build/html/index.html
```
7 changes: 3 additions & 4 deletions flask_jwt_extended/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from typing import Any

import jwt
from flask import _request_ctx_stack
from flask import Response
from flask import Response, _request_ctx_stack
from werkzeug.local import LocalProxy

from flask_jwt_extended.config import config
Expand Down Expand Up @@ -105,11 +104,11 @@ def decode_token(
) -> dict:
"""
Returns the decoded token (python dict) from an encoded JWT. This does all
the checks to insure that the decoded token is valid before returning it.
the checks to ensure that the decoded token is valid before returning it.
This will not fire the user loader callbacks, save the token for access
in protected endpoints, checked if a token is revoked, etc. This is puerly
used to insure that a JWT is valid.
used to ensure that a JWT is valid.
:param encoded_token:
The encoded JWT to decode.
Expand Down
12 changes: 4 additions & 8 deletions tests/test_asymmetric_crypto.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import pytest
from flask import Flask
from flask import jsonify

from flask_jwt_extended import create_access_token
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from flask import Flask, jsonify
from flask_jwt_extended import JWTManager, create_access_token, jwt_required

RSA_PRIVATE = """
-----BEGIN RSA PRIVATE KEY-----
Expand Down Expand Up @@ -57,13 +53,13 @@ def test_asymmetric_cropto(app):
app.config["JWT_ALGORITHM"] = "RS256"
rs256_token = create_access_token("username")

# Insure the symmetric token does not work now
# Ensure the symmetric token does not work now
access_headers = {"Authorization": "Bearer {}".format(hs256_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 422
assert response.get_json() == {"msg": "The specified alg value is not allowed"}

# Insure the asymmetric token does work
# Ensure the asymmetric token does work
access_headers = {"Authorization": "Bearer {}".format(rs256_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 200
Expand Down
31 changes: 15 additions & 16 deletions tests/test_cookies.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import pytest
from flask import Flask
from flask import jsonify
from flask import request

from flask_jwt_extended import create_access_token
from flask_jwt_extended import create_refresh_token
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import set_refresh_cookies
from flask_jwt_extended import unset_access_cookies
from flask_jwt_extended import unset_jwt_cookies
from flask_jwt_extended import unset_refresh_cookies
from flask import Flask, jsonify, request
from flask_jwt_extended import (
JWTManager,
create_access_token,
create_refresh_token,
jwt_required,
set_access_cookies,
set_refresh_cookies,
unset_access_cookies,
unset_jwt_cookies,
unset_refresh_cookies,
)


def _get_cookie_from_response(response, cookie_name):
Expand Down Expand Up @@ -301,17 +300,17 @@ def test_custom_csrf_methods(app, options):
response = test_client.get(auth_url)
csrf_token = _get_cookie_from_response(response, csrf_cookie_name)[csrf_cookie_name]

# Insure we can now do posts without csrf
# Ensure we can now do posts without csrf
response = test_client.post(post_url)
assert response.status_code == 200
assert response.get_json() == {"foo": "bar"}

# Insure GET requests now fail without csrf
# Ensure GET requests now fail without csrf
response = test_client.get(get_url)
assert response.status_code == 401
assert response.get_json() == {"msg": "Missing CSRF token"}

# Insure GET requests now succeed with csrf
# Ensure GET requests now succeed with csrf
csrf_headers = {"X-CSRF-TOKEN": csrf_token}
response = test_client.get(get_url, headers=csrf_headers)
assert response.status_code == 200
Expand Down
43 changes: 22 additions & 21 deletions tests/test_decode_tokens.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
from datetime import datetime
from datetime import timedelta
from datetime import timezone
from datetime import datetime, timedelta, timezone

import pytest
from dateutil.relativedelta import relativedelta
from flask import Flask
from jwt import DecodeError
from jwt import ExpiredSignatureError
from jwt import ImmatureSignatureError
from jwt import InvalidAudienceError
from jwt import InvalidIssuerError
from jwt import InvalidSignatureError
from jwt import MissingRequiredClaimError

from flask_jwt_extended import create_access_token
from flask_jwt_extended import create_refresh_token
from flask_jwt_extended import decode_token
from flask_jwt_extended import get_jti
from flask_jwt_extended import get_unverified_jwt_headers
from flask_jwt_extended import JWTManager
from flask_jwt_extended import (
JWTManager,
create_access_token,
create_refresh_token,
decode_token,
get_jti,
get_unverified_jwt_headers,
)
from flask_jwt_extended.config import config
from flask_jwt_extended.exceptions import JWTDecodeError
from tests.utils import encode_token
from tests.utils import get_jwt_manager
from jwt import (
DecodeError,
ExpiredSignatureError,
ImmatureSignatureError,
InvalidAudienceError,
InvalidIssuerError,
InvalidSignatureError,
MissingRequiredClaimError,
)

from tests.utils import encode_token, get_jwt_manager


@pytest.fixture(scope="function")
Expand Down Expand Up @@ -172,13 +173,13 @@ def test_nbf_token_in_future(app):
def test_alternate_identity_claim(app, default_access_token):
app.config["JWT_IDENTITY_CLAIM"] = "banana"

# Insure decoding fails if the claim isn't there
# Ensure decoding fails if the claim isn't there
token = encode_token(app, default_access_token)
with pytest.raises(JWTDecodeError):
with app.test_request_context():
decode_token(token)

# Insure the claim exists in the decoded jwt
# Ensure the claim exists in the decoded jwt
del default_access_token["sub"]
default_access_token["banana"] = "username"
token = encode_token(app, default_access_token)
Expand Down
21 changes: 9 additions & 12 deletions tests/test_headers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import pytest
from flask import Flask
from flask import jsonify
from flask import Flask, jsonify
from flask_jwt_extended import JWTManager, create_access_token, jwt_required

from flask_jwt_extended import create_access_token
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from tests.utils import get_jwt_manager


Expand Down Expand Up @@ -76,13 +73,13 @@ def test_custom_header_name(app):
with app.test_request_context():
access_token = create_access_token("username")

# Insure 'default' headers no longer work
# Ensure 'default' headers no longer work
access_headers = {"Authorization": "Bearer {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 401
assert response.get_json() == {"msg": "Missing Foo Header"}

# Insure new headers do work
# Ensure new headers do work
access_headers = {"Foo": "Bearer {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 200
Expand All @@ -108,7 +105,7 @@ def test_custom_header_type(app):
with app.test_request_context():
access_token = create_access_token("username")

# Insure 'default' headers no longer work
# Ensure 'default' headers no longer work
access_headers = {"Authorization": "Bearer {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
error_msg = (
Expand All @@ -118,7 +115,7 @@ def test_custom_header_type(app):
assert response.status_code == 401
assert response.get_json() == {"msg": error_msg}

# Insure new headers do work
# Ensure new headers do work
access_headers = {"Authorization": "JWT {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 200
Expand All @@ -136,14 +133,14 @@ def test_custom_header_type(app):
assert response.status_code == 200
assert response.get_json() == {"foo": "bar"}

# Insure new headers without a type also work
# Ensure new headers without a type also work
app.config["JWT_HEADER_TYPE"] = ""
access_headers = {"Authorization": access_token}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 200
assert response.get_json() == {"foo": "bar"}

# Insure header with too many parts fails
# Ensure header with too many parts fails
app.config["JWT_HEADER_TYPE"] = ""
access_headers = {"Authorization": "Bearer {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
Expand All @@ -156,7 +153,7 @@ def test_missing_headers(app):
test_client = app.test_client()
jwtM = get_jwt_manager(app)

# Insure 'default' no headers response
# Ensure 'default' no headers response
response = test_client.get("/protected", headers=None)
assert response.status_code == 401
assert response.get_json() == {"msg": "Missing Authorization Header"}
Expand Down
8 changes: 4 additions & 4 deletions tests/test_query_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ def test_custom_query_paramater(app):
with app.test_request_context():
access_token = create_access_token("username")

# Insure 'default' query paramaters no longer work
# Ensure 'default' query paramaters no longer work
url = "/protected?jwt={}".format(access_token)
response = test_client.get(url)
assert response.status_code == 401
assert response.get_json() == {"msg": "Missing 'foo' query paramater"}

# Insure new query_string does work
# Ensure new query_string does work
url = "/protected?foo={}".format(access_token)
response = test_client.get(url)
assert response.status_code == 200
Expand All @@ -86,12 +86,12 @@ def test_missing_query_paramater(app):
with app.test_request_context():
access_token = create_access_token("username")

# Insure no query paramaters doesn't give a response
# Ensure no query paramaters doesn't give a response
response = test_client.get("/protected")
assert response.status_code == 401
assert response.get_json() == {"msg": "Missing 'jwt' query paramater"}

# Insure headers don't work
# Ensure headers don't work
access_headers = {"Authorization": "Bearer {}".format(access_token)}
response = test_client.get("/protected", headers=access_headers)
assert response.status_code == 401
Expand Down

0 comments on commit faadc95

Please sign in to comment.