Skip to content

Commit 8a3b50e

Browse files
authored
feat: add jwt demo (#19)
* docs: add jwt demo(inherited from @yance-dev) * fix: lint * fix: update pytest to fix err `TypeError: required field "lineno" missing from alias`
1 parent b0ae545 commit 8a3b50e

File tree

14 files changed

+298
-46
lines changed

14 files changed

+298
-46
lines changed

.github/workflows/coverage.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
on: ["push", "pull_request"]
1+
on: [ "push", "pull_request" ]
22

33
name: Test Coveralls
44

@@ -9,25 +9,25 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111

12-
- uses: actions/checkout@v1
12+
- uses: actions/checkout@v1
1313

14-
- uses: actions/setup-python@v2
15-
with:
16-
python-version: '3.9'
17-
architecture: 'x64'
14+
- uses: actions/setup-python@v2
15+
with:
16+
python-version: '3.9'
17+
architecture: 'x64'
1818

1919

20-
- name: Install Dependency
21-
run: |
22-
python -m pip install -r dev-requirements.txt
23-
python -m pip install coveralls
20+
- name: Install Dependency
21+
run: |
22+
python -m pip install -r dev-requirements.txt
23+
python -m pip install coveralls
2424
25-
- name: Run Coverage
26-
run: |
27-
python -m pytest --cov=fastapi_authz tests/
28-
coveralls --service=github
29-
env:
30-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25+
- name: Run Coverage
26+
run: |
27+
python -m pytest --cov=fastapi_authz tests/
28+
coveralls --service=github
29+
env:
30+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3131

3232
# - name: Coveralls
3333
# uses: coverallsapp/github-action@master

.github/workflows/release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ jobs:
1313
uses: actions/checkout@v2
1414
with:
1515
fetch-depth: 0
16-
16+
1717
- uses: actions/setup-node@v2
1818
with:
1919
node-version: '16'
2020

2121
- name: Setup
2222
run: npm install -g semantic-release @semantic-release/github @semantic-release/changelog @semantic-release/commit-analyzer @semantic-release/git @semantic-release/release-notes-generator semantic-release-pypi
23-
23+
2424
- name: Set up python
2525
uses: actions/setup-python@v2
2626
with:
2727
python-version: 3.9
28-
28+
2929
- name: Install setuptools
3030
run: python -m pip install --upgrade setuptools wheel twine
31-
31+
3232
- name: Release
3333
env:
3434
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.releaserc.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@
1111
"changelogFile": "CHANGELOG.md",
1212
"changelogTitle": "# Semantic Versioning Changelog"
1313
}
14-
],
15-
[
14+
],
15+
[
1616
"@semantic-release/git",
1717
{
1818
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
19-
"assets": ["CHANGELOG.md", "setup.py", "setup.cfg"]
19+
"assets": [
20+
"CHANGELOG.md",
21+
"setup.py",
22+
"setup.cfg"
23+
]
2024
}
2125
]
2226
]

CHANGELOG.md

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,34 @@
22

33
# [0.1.0](https://github.com/pycasbin/fastapi-authz/compare/v0.0.5...v0.1.0) (2021-12-01)
44

5-
65
### Features
76

8-
* add release config ([ca64044](https://github.com/pycasbin/fastapi-authz/commit/ca64044342088f36fb9822376487260a2ac2c6a1))
9-
* always allow OPTIONS ([#16](https://github.com/pycasbin/fastapi-authz/issues/16)) ([6576796](https://github.com/pycasbin/fastapi-authz/commit/65767963ce26d8a63115ecbc87e76f812abd430f))
7+
* add release
8+
config ([ca64044](https://github.com/pycasbin/fastapi-authz/commit/ca64044342088f36fb9822376487260a2ac2c6a1))
9+
* always allow
10+
OPTIONS ([#16](https://github.com/pycasbin/fastapi-authz/issues/16)) ([6576796](https://github.com/pycasbin/fastapi-authz/commit/65767963ce26d8a63115ecbc87e76f812abd430f))
1011

1112
# [0.1.0](https://github.com/pycasbin/fastapi-authz/compare/v0.0.1...v0.1.0) (2021-03-11)
1213

13-
1414
### Bug Fixes
1515

1616
* fix package ([6fc0a68](https://github.com/pycasbin/fastapi-authz/commit/6fc0a68e9a6620c0e67f355d82894b09aa5da1ef))
17-
* fix package build ([6ec4f6a](https://github.com/pycasbin/fastapi-authz/commit/6ec4f6a58e205bf70913b21cf7102282f4435939))
18-
* fix package install ([98f7a34](https://github.com/pycasbin/fastapi-authz/commit/98f7a34c2ee70b39a25f11d64746078253fb03ba))
17+
* fix package
18+
build ([6ec4f6a](https://github.com/pycasbin/fastapi-authz/commit/6ec4f6a58e205bf70913b21cf7102282f4435939))
19+
* fix package
20+
install ([98f7a34](https://github.com/pycasbin/fastapi-authz/commit/98f7a34c2ee70b39a25f11d64746078253fb03ba))
1921
* typo ([5081da4](https://github.com/pycasbin/fastapi-authz/commit/5081da46b99d1b8d1746f683d675bde17566d659))
2022
* typo ([fe5800f](https://github.com/pycasbin/fastapi-authz/commit/fe5800f0f5af1b13a45721b24cfbdc48beabc8a2))
2123

22-
2324
### Features
2425

25-
* add readme and demo ([191c6f1](https://github.com/pycasbin/fastapi-authz/commit/191c6f1caa812f288e8e8ecebad74762ca3b1866))
26-
* add release config ([ca64044](https://github.com/pycasbin/fastapi-authz/commit/ca64044342088f36fb9822376487260a2ac2c6a1))
26+
* add readme and
27+
demo ([191c6f1](https://github.com/pycasbin/fastapi-authz/commit/191c6f1caa812f288e8e8ecebad74762ca3b1866))
28+
* add release
29+
config ([ca64044](https://github.com/pycasbin/fastapi-authz/commit/ca64044342088f36fb9822376487260a2ac2c6a1))
2730

2831
# [0.1.0](https://github.com/pycasbin/fastapi-authz/compare/v0.0.3...v0.1.0) (2021-03-04)
2932

30-
3133
### Bug Fixes
3234

3335
* ci ([bd53180](https://github.com/pycasbin/fastapi-authz/commit/bd53180565fb55907b60666fb438add7cb18e4fb))
@@ -36,14 +38,18 @@
3638
* ci ([df1b2cd](https://github.com/pycasbin/fastapi-authz/commit/df1b2cd7ee05ed058d009070ae2e9150c794c41b))
3739
* ci ([ada48e9](https://github.com/pycasbin/fastapi-authz/commit/ada48e95c38494d59eb5c93698eb939201d1a038))
3840
* ci ([eec5cf0](https://github.com/pycasbin/fastapi-authz/commit/eec5cf0f292044be21b264a85e391545dbf1d200))
39-
* fix package build ([d15f244](https://github.com/pycasbin/fastapi-authz/commit/d15f244ab257e334b9204c140a4114c84f298f73))
40-
* fix package build ([e190697](https://github.com/pycasbin/fastapi-authz/commit/e190697f2bc0678ee1762bb1b31c46cf4f7bd539))
41-
* fix package install ([61a24a8](https://github.com/pycasbin/fastapi-authz/commit/61a24a816e823001e44a35a41fae5ecf86205697))
42-
* fix package install ([32e85a1](https://github.com/pycasbin/fastapi-authz/commit/32e85a15475290a3128274d5fa03e82f6198edf2))
41+
* fix package
42+
build ([d15f244](https://github.com/pycasbin/fastapi-authz/commit/d15f244ab257e334b9204c140a4114c84f298f73))
43+
* fix package
44+
build ([e190697](https://github.com/pycasbin/fastapi-authz/commit/e190697f2bc0678ee1762bb1b31c46cf4f7bd539))
45+
* fix package
46+
install ([61a24a8](https://github.com/pycasbin/fastapi-authz/commit/61a24a816e823001e44a35a41fae5ecf86205697))
47+
* fix package
48+
install ([32e85a1](https://github.com/pycasbin/fastapi-authz/commit/32e85a15475290a3128274d5fa03e82f6198edf2))
4349
* typo ([51367ce](https://github.com/pycasbin/fastapi-authz/commit/51367ce9f189a218bfa270dbf389fda0cfed19f2))
4450
* typo ([843ba85](https://github.com/pycasbin/fastapi-authz/commit/843ba852a455e00ca942897363e59ee3ec1f0698))
4551

46-
4752
### Features
4853

49-
* add release config ([61dde88](https://github.com/pycasbin/fastapi-authz/commit/61dde885034f4ae5d32956141f8e70549088aac9))
54+
* add release
55+
config ([61dde88](https://github.com/pycasbin/fastapi-authz/commit/61dde885034f4ae5d32956141f8e70549088aac9))

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ It used the casbin config from `examples` folder, and you can find this demo in
113113

114114
You can also view the unit tests to understand this middleware.
115115

116+
Besides, there is another example for `CasbinMiddleware` which is designed to work with JWT authentication. You can find
117+
it in `demo/jwt_test.py`.
118+
116119
## Development
117120

118121
### Run unit tests

demo/jwt_test.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from datetime import datetime, timedelta
2+
from typing import Optional, Tuple, Union
3+
4+
import casbin
5+
import jwt
6+
import uvicorn
7+
from fastapi import FastAPI
8+
from starlette.authentication import (
9+
AuthenticationBackend, AuthenticationError, BaseUser, AuthCredentials)
10+
from starlette.middleware.authentication import AuthenticationMiddleware
11+
12+
from fastapi_authz import CasbinMiddleware
13+
14+
JWT_SECRET_KEY = "secret"
15+
app = FastAPI()
16+
17+
18+
class JWTUser(BaseUser):
19+
def __init__(self, username: str, token: str, payload: dict) -> None:
20+
self.username = username
21+
self.token = token
22+
self.payload = payload
23+
24+
@property
25+
def is_authenticated(self) -> bool:
26+
return True
27+
28+
@property
29+
def display_name(self) -> str:
30+
return self.username
31+
32+
33+
class JWTAuthenticationBackend(AuthenticationBackend):
34+
35+
def __init__(self,
36+
secret_key: str,
37+
algorithm: str = 'HS256',
38+
prefix: str = 'Bearer',
39+
username_field: str = 'username',
40+
audience: Optional[str] = None,
41+
options: Optional[dict] = None) -> None:
42+
self.secret_key = secret_key
43+
self.algorithm = algorithm
44+
self.prefix = prefix
45+
self.username_field = username_field
46+
self.audience = audience
47+
self.options = options or dict()
48+
49+
@classmethod
50+
def get_token_from_header(cls, authorization: str, prefix: str) -> str:
51+
"""Parses the Authorization header and returns only the token"""
52+
try:
53+
scheme, token = authorization.split()
54+
except ValueError as e:
55+
raise AuthenticationError('Could not separate Authorization scheme and token') from e
56+
57+
if scheme.lower() != prefix.lower():
58+
raise AuthenticationError(f'Authorization scheme {scheme} is not supported')
59+
return token
60+
61+
async def authenticate(self, request) -> Union[None, Tuple[AuthCredentials, BaseUser]]:
62+
if "Authorization" not in request.headers:
63+
return None
64+
65+
auth = request.headers["Authorization"]
66+
token = self.get_token_from_header(authorization=auth, prefix=self.prefix)
67+
try:
68+
payload = jwt.decode(token, key=self.secret_key, algorithms=self.algorithm, audience=self.audience,
69+
options=self.options)
70+
except jwt.InvalidTokenError as e:
71+
raise AuthenticationError(str(e)) from e
72+
return AuthCredentials(["authenticated"]), JWTUser(username=payload[self.username_field], token=token,
73+
payload=payload)
74+
75+
76+
enforcer = casbin.Enforcer('../examples/rbac_model.conf', '../examples/rbac_policy.csv')
77+
app.add_middleware(CasbinMiddleware, enforcer=enforcer)
78+
79+
app.add_middleware(AuthenticationMiddleware, backend=JWTAuthenticationBackend(secret_key=JWT_SECRET_KEY))
80+
81+
82+
def create_access_token(subject: str, expires_delta: timedelta = None) -> str:
83+
if expires_delta:
84+
expire = datetime.utcnow() + expires_delta
85+
else:
86+
expire = datetime.utcnow() + timedelta(
87+
minutes=60
88+
)
89+
to_encode = {"exp": expire, "username": subject}
90+
return jwt.encode(to_encode, JWT_SECRET_KEY, algorithm="HS256")
91+
92+
93+
@app.get('/')
94+
async def index():
95+
return "If you see this, you have been authenticated."
96+
97+
98+
@app.get('/dataset1/protected')
99+
async def auth_test():
100+
return "You must be alice to see this."
101+
102+
103+
if __name__ == '__main__':
104+
print("alice:", create_access_token("alice", expires_delta=timedelta(minutes=60)))
105+
print("mark:", create_access_token("mark", expires_delta=timedelta(minutes=60)))
106+
uvicorn.run(app, debug=True)

demo/test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import casbin
55
import uvicorn
6-
76
from fastapi import FastAPI
87
from starlette.authentication import AuthenticationBackend, AuthenticationError, SimpleUser, AuthCredentials
98
from starlette.middleware.authentication import AuthenticationMiddleware

dev-requirements.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ uvicorn
88
starlette-auth-toolkit
99
requests
1010
twine
11-
build
11+
build
12+
pyjwt

dev-requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ pluggy==0.13.1
2525
py==1.10.0
2626
pydantic==1.7.3
2727
pygments==2.8.0
28+
pyjwt==2.3.0
2829
pyparsing==2.4.7
2930
pytest-cov==2.11.1
30-
pytest==6.2.2
31+
pytest==7.1.2
3132
pywin32-ctypes==0.2.0
3233
readme-renderer==29.0
3334
requests-toolbelt==0.9.1

fastapi_authz/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .middleware import CasbinMiddleware
1+
from .middleware import CasbinMiddleware

0 commit comments

Comments
 (0)