Skip to content

Commit c902112

Browse files
Merge pull request #10 from IndominusByte/docs
add documentation
2 parents f5995ad + 8e54499 commit c902112

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1585
-1116
lines changed

.github/workflows/build-docs.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Build Docs
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
deploy:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v2
10+
- uses: actions/setup-python@v2
11+
with:
12+
python-version: 3.x
13+
- run: pip install mkdocs-material
14+
- run: pip install markdown-include
15+
- run: mkdocs gh-deploy --force

.github/workflows/tests.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: [3.6, 3.7, 3.8]
11+
fail-fast: false
12+
13+
steps:
14+
- uses: actions/checkout@v2
15+
- name: Set up Python
16+
uses: actions/setup-python@v1
17+
with:
18+
python-version: ${{ matrix.python-version }}
19+
- name: Install Flit
20+
run: pip install flit
21+
- name: Install Dependencies
22+
run: flit install --symlink
23+
- name: Test
24+
run: bash scripts/tests.sh
25+
- name: Coveralls
26+
env:
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28+
run: |
29+
coveralls

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var/
3030

3131
# coverage
3232
.coverage
33+
htmlcov/
3334

3435
# testing py
3536
test.py

.travis.yml

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

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## 0.3.0
2+
* **(Deprecated)** environment variable support
3+
* Change name function **load_end()** -> **load_config()**
4+
* Change name function **get_jwt_identity()** -> **get_jwt_subject()**
5+
* Change name identity claims to standard claims sub *(Thanks to @rassie for suggestion)*
6+
* Additional headers in claims
7+
* Get additional headers claims from request or parsing token directly
8+
* Leeway exp claim decode token
9+
* Dynamic token expires time
10+
* Change name **blacklist** -> **denylist**
11+
* Denylist custom check refresh and access tokens
12+
* Issuer claim
13+
* Audience claim
14+
* Jwt decode algorithms
15+
* Dynamic algorithm create token
16+
* Token multiple location
17+
* Support RSA encryption *(Thanks to @jet10000 for make issues)*
18+
* Custom header name and type
19+
* Custom error message key and status code
20+
* JWT in cookies *(Thanks to @m4nuC for make issues)*
21+
* Add Additional claims
22+
* Add Documentation *(#9 by @paulussimanjuntak)*
23+
24+
## 0.2.0
25+
26+
* Call create_token and get_jti function must be from dependency injection
27+
* Improve blacklist loader
28+
* Can load env from pydantic
29+
* Add docs on readme how to use without dependency injection and example on multiple files
30+
* Fix raise jwt exception PR #1 by @ironslob
31+
32+
## 0.1.0
33+
34+
* Initial release.

README.md

Lines changed: 22 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,224 +1,40 @@
1-
# fastapi-jwt-auth
1+
<h1 align="left" style="margin-bottom: 20px; font-weight: 500; font-size: 50px; color: black;">
2+
FastAPI JWT Auth
3+
</h1>
24

3-
[![Build Status](https://travis-ci.org/IndominusByte/fastapi-jwt-auth.svg?branch=master)](https://travis-ci.org/IndominusByte/fastapi-jwt-auth)
5+
![Tests](https://github.com/IndominusByte/fastapi-jwt-auth/workflows/Tests/badge.svg)
46
[![Coverage Status](https://coveralls.io/repos/github/IndominusByte/fastapi-jwt-auth/badge.svg?branch=master)](https://coveralls.io/github/IndominusByte/fastapi-jwt-auth?branch=master)
57
[![PyPI version](https://badge.fury.io/py/fastapi-jwt-auth.svg)](https://badge.fury.io/py/fastapi-jwt-auth)
6-
[![Downloads](https://pepy.tech/badge/fastapi-jwt-auth/month)](https://pepy.tech/project/fastapi-jwt-auth/month)
7-
[![Downloads](https://pepy.tech/badge/fastapi-jwt-auth)](https://pepy.tech/project/fastapi-jwt-auth)
8+
[![Downloads](https://static.pepy.tech/personalized-badge/fastapi-jwt-auth?period=total&units=international_system&left_color=grey&right_color=brightgreen&left_text=Downloads)](https://pepy.tech/project/fastapi-jwt-auth)
89

9-
## Features
10-
FastAPI extension that provides JWT Auth support (secure, easy to use and lightweight), if you were familiar with flask-jwt-extended this extension suitable for you because this extension inspired by flask-jwt-extended.
11-
- Access token and refresh token
12-
- Token freshness will only allow fresh tokens to access endpoint
13-
- Token revoking/blacklisting
14-
- Custom token revoking
15-
16-
## Installation
17-
```bash
18-
pip install fastapi-jwt-auth
19-
```
20-
21-
## Usage
22-
### Setting `AUTHJWT_SECRET_KEY` in environment variable
23-
- For Linux, macOS, Windows Bash
24-
```bash
25-
export AUTHJWT_SECRET_KEY=secretkey
26-
```
27-
- For Windows PowerShell
28-
```bash
29-
$Env:AUTHJWT_SECRET_KEY = "secretkey"
30-
```
31-
### Create it
32-
- Create a file `basic.py` with:
33-
```python
34-
from fastapi import FastAPI, Depends, HTTPException
35-
from fastapi_jwt_auth import AuthJWT
36-
from pydantic import BaseModel, Field
37-
38-
app = FastAPI()
10+
---
3911

40-
class User(BaseModel):
41-
username: str = Field(...,min_length=1)
42-
password: str = Field(...,min_length=1)
12+
**Documentation**: <a href="https://indominusbyte.github.io/fastapi-jwt-auth" target="_blank">https://indominusbyte.github.io/fastapi-jwt-auth</a>
4313

44-
# Provide a method to create access tokens. The create_access_token()
45-
# function is used to actually generate the token, and you can return
46-
# it to the caller however you choose.
47-
@app.post('/login',status_code=200)
48-
def login(user: User, Authorize: AuthJWT = Depends()):
49-
if user.username != 'test' or user.password != 'test':
50-
raise HTTPException(status_code=401,detail='Bad username or password')
14+
**Source Code**: <a href="https://github.com/IndominusByte/fastapi-jwt-auth" target="_blank">https://github.com/IndominusByte/fastapi-jwt-auth</a>
5115

52-
# identity must be between string or integer
53-
access_token = Authorize.create_access_token(identity=user.username)
54-
return {"access_token": access_token}
16+
---
5517

56-
@app.get('/protected',status_code=200)
57-
def protected(Authorize: AuthJWT = Depends()):
58-
# Protect an endpoint with jwt_required, which requires a valid access token
59-
# in the request to access.
60-
Authorize.jwt_required()
18+
## Features
19+
FastAPI extension that provides JWT Auth support (secure, easy to use and lightweight), if you were familiar with flask-jwt-extended this extension suitable for you, cause this extension inspired by flask-jwt-extended 😀
6120

62-
# Access the identity of the current user with get_jwt_identity
63-
current_user = Authorize.get_jwt_identity()
64-
return {"logged_in_as": current_user}
21+
- Access tokens and refresh tokens
22+
- Freshness Tokens
23+
- Revoking Tokens
24+
- Support for adding custom claims to JSON Web Tokens
25+
- Storing tokens in cookies and CSRF protection
6526

66-
```
67-
### Run it
68-
Run the server with:
69-
```console
70-
$ uvicorn basic:app --host 0.0.0.0
27+
## Installation
28+
The easiest way to start working with this extension with pip
7129

72-
INFO: Started server process [4235]
73-
INFO: Waiting for application startup.
74-
INFO: Application startup complete.
75-
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
76-
```
77-
### Access it
78-
To access a jwt_required protected url, all we have to do is send in the JWT with the request. By default, this is done with an authorization header that looks like:
7930
```bash
80-
Authorization: Bearer <access_token>
81-
```
82-
We can see this in action using CURL:
83-
```console
84-
$ curl http://localhost:8000/protected
85-
86-
{"detail":"Missing Authorization Header"}
87-
88-
$ curl -H "Content-Type: application/json" -X POST \
89-
-d '{"username":"test","password":"test"}' http://localhost:8000/login
90-
91-
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTczMzMxMzMsIm5iZiI6MTU5NzMzMzEzMywianRpIjoiNDczY2ExM2ItOWI1My00NDczLWJjZTctMWZiOWMzNTlmZmI0IiwiZXhwIjoxNTk3MzM0MDMzLCJpZGVudGl0eSI6InRlc3QiLCJ0eXBlIjoiYWNjZXNzIiwiZnJlc2giOmZhbHNlfQ.42CusQo6nsLxOk6bBUP1vnVX-REx4ZYBYYIjYChWf0c"
92-
93-
$ export TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTczMzMxMzMsIm5iZiI6MTU5NzMzMzEzMywianRpIjoiNDczY2ExM2ItOWI1My00NDczLWJjZTctMWZiOWMzNTlmZmI0IiwiZXhwIjoxNTk3MzM0MDMzLCJpZGVudGl0eSI6InRlc3QiLCJ0eXBlIjoiYWNjZXNzIiwiZnJlc2giOmZhbHNlfQ.42CusQo6nsLxOk6bBUP1vnVX-REx4ZYBYYIjYChWf0c
94-
95-
$ curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/protected
96-
97-
{"logged_in_as":"test"}
98-
```
99-
## Extract Token
100-
Access all URL to see what the result
101-
```python
102-
from fastapi import FastAPI, Depends, HTTPException
103-
from fastapi_jwt_auth import AuthJWT
104-
from pydantic import BaseModel, Field
105-
106-
app = FastAPI()
107-
108-
class User(BaseModel):
109-
username: str = Field(...,min_length=1)
110-
password: str = Field(...,min_length=1)
111-
112-
@app.post('/login',status_code=200)
113-
def login(user: User, Authorize: AuthJWT = Depends()):
114-
if user.username != 'test' or user.password != 'test':
115-
raise HTTPException(status_code=401,detail='Bad username or password')
116-
117-
access_token = Authorize.create_access_token(identity=user.username)
118-
return access_token
119-
120-
# Returns the JTI (unique identifier) of an encoded JWT
121-
@app.get('/get-jti',status_code=200)
122-
def get_jti(Authorize: AuthJWT = Depends()):
123-
access_token = Authorize.create_access_token(identity='test')
124-
return Authorize.get_jti(encoded_token=access_token)
125-
126-
# this will return the identity of the JWT that is accessing this endpoint.
127-
# If no JWT is present, `None` is returned instead.
128-
@app.get('/get-jwt-identity',status_code=200)
129-
def get_jwt_identity(Authorize: AuthJWT = Depends()):
130-
Authorize.jwt_optional()
131-
132-
current_user = Authorize.get_jwt_identity()
133-
return {"logged_in_as": current_user}
134-
135-
# this will return the python dictionary which has all
136-
# of the claims of the JWT that is accessing the endpoint.
137-
# If no JWT is currently present, return None instead
138-
@app.get('/get-raw-jwt',status_code=200)
139-
def get_raw_jwt(Authorize: AuthJWT = Depends()):
140-
Authorize.jwt_optional()
141-
142-
token = Authorize.get_raw_jwt()
143-
return {"token": token}
31+
pip install fastapi-jwt-auth
14432
```
14533

146-
## Configuration Options (env)
147-
- `AUTHJWT_ACCESS_TOKEN_EXPIRES`<br/>
148-
How long an access token should live before it expires. If you not define in env variable
149-
default value is `15 minutes`. Or you can custom with value `int` (seconds), example
150-
`AUTHJWT_ACCESS_TOKEN_EXPIRES=300` its mean access token expired in 5 minute
151-
152-
- `AUTHJWT_REFRESH_TOKEN_EXPIRES`<br/>
153-
How long a refresh token should live before it expires. If you not define in env variable
154-
default value is `30 days`. Or you can custom with value `int` (seconds), example
155-
`AUTHJWT_REFRESH_TOKEN_EXPIRES=86400` its mean refresh token expired in 1 day
156-
157-
- `AUTHJWT_BLACKLIST_ENABLED`<br/>
158-
Enable/disable token revoking. Default value is None, for enable blacklist token: `AUTHJWT_BLACKLIST_ENABLED=true`
159-
160-
- `AUTHJWT_SECRET_KEY`<br/>
161-
The secret key needed for symmetric based signing algorithms, such as HS*. If this is not set `raise RuntimeError`.
162-
163-
- `AUTHJWT_ALGORITHM`<br/>
164-
Which algorithms are allowed to decode a JWT. Default value is `HS256`
165-
166-
## Configuration (pydantic or list[tuple])
167-
You can convert and validate type data from dotenv through pydantic (BaseSettings)
168-
```python
169-
from fastapi_jwt_auth import AuthJWT
170-
from pydantic import BaseSettings
171-
from datetime import timedelta
172-
from typing import Literal
173-
174-
# dotenv file parsing requires python-dotenv to be installed
175-
# This can be done with either pip install python-dotenv
176-
class Settings(BaseSettings):
177-
authjwt_access_token_expires: timedelta = timedelta(minutes=15)
178-
authjwt_refresh_token_expires: timedelta = timedelta(days=30)
179-
# literal type only available for python 3.8
180-
authjwt_blacklist_enabled: Literal['true','false']
181-
authjwt_secret_key: str
182-
authjwt_algorithm: str = 'HS256'
183-
184-
class Config:
185-
env_file = '.env'
186-
env_file_encoding = 'utf-8'
187-
188-
189-
@AuthJWT.load_env
190-
def get_settings():
191-
return Settings()
192-
# or you can just parse a list of tuple
193-
# return [
194-
# ("authjwt_access_token_expires",timedelta(minutes=2)),
195-
# ("authjwt_refresh_token_expires",timedelta(days=5)),
196-
# ("authjwt_blacklist_enabled","false"),
197-
# ("authjwt_secret_key","testing"),
198-
# ("authjwt_algorithm","HS256")
199-
# ]
200-
201-
202-
print(AuthJWT._access_token_expires)
203-
print(AuthJWT._refresh_token_expires)
204-
print(AuthJWT._blacklist_enabled)
205-
print(AuthJWT._secret_key)
206-
print(AuthJWT._algorithm)
34+
If you want to use asymmetric (public/private) key signing algorithms, include the <b>asymmetric</b> extra requirements.
35+
```bash
36+
pip install 'fastapi-jwt-auth[asymmetric]'
20737
```
20838

209-
## Examples
210-
Examples are available on [examples](/examples) folder.
211-
There are:
212-
- [Basic](/examples/basic.py)
213-
- [Token Optional](/examples/optional_protected_endpoints.py)
214-
- [Refresh Token](/examples/refresh_tokens.py)
215-
- [Token Fresh](/examples/token_freshness.py)
216-
- [Blacklist Token](/examples/blacklist.py)
217-
- [Blacklist Token Use Redis](/examples/blacklist_redis.py)
218-
219-
Optional:
220-
- [Use AuthJWT Without Dependency Injection](/examples/without_dependency.py)
221-
- [On Mutiple Files](/examples/multiple_files)
222-
22339
## License
22440
This project is licensed under the terms of the MIT license.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
You may want to store additional information in the access token or refresh token and you could later access in the protected views. This can be done easily by parsing additional information *(dictionary python)* to parameter **user_claims** in function **create_access_token()** or **create_refresh_token()**, and the data can be accessed later in a protected endpoint with the **get_raw_jwt()** function.
2+
3+
Storing data in the tokens can be good for performance. If you store data in the tokens, you won't need to look it up from disk next time you need it in a protected endpoint. However, you should take care of what data you put in the tokens. Any data in the tokens can be trivially viewed by anyone who can read the tokens.
4+
5+
**Note**: *Do not store sensitive information in the tokens!*
6+
7+
```python hl_lines="34-35 44"
8+
{!../examples/additional_claims.py!}
9+
```

docs/advanced-usage/bigger-app.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Because *fastapi-jwt-auth* configure your setting via class state that applies across all instances of the class. You need to make sure to call **load_config**(callback) above from your endpoint. Thanks to `FastAPI` when you make endpoint from `APIRouter` it will actually work as if everything was the same single app.
2+
3+
So you only need to define **load_config**(callback) where `Fastapi` instance created or you can import it where you included all the router.
4+
5+
## An example file structure
6+
7+
Let's say you have a file structure like this:
8+
9+
```
10+
.
11+
├── multiple_files
12+
│ ├── __init__.py
13+
│ ├── app.py
14+
│ └── routers
15+
│ ├── __init__.py
16+
│ ├── items.py
17+
│ └── users.py
18+
```
19+
20+
Here an example of `app.py`
21+
22+
```python
23+
{!../examples/multiple_files/app.py!}
24+
```
25+
26+
Here an example of `users.py`
27+
28+
```python
29+
{!../examples/multiple_files/routers/users.py!}
30+
```
31+
32+
Here an example of `items.py`
33+
34+
```python
35+
{!../examples/multiple_files/routers/items.py!}
36+
```

0 commit comments

Comments
 (0)