|
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> |
2 | 4 |
|
3 | | -[](https://travis-ci.org/IndominusByte/fastapi-jwt-auth) |
| 5 | + |
4 | 6 | [](https://coveralls.io/github/IndominusByte/fastapi-jwt-auth?branch=master) |
5 | 7 | [](https://badge.fury.io/py/fastapi-jwt-auth) |
6 | | -[](https://pepy.tech/project/fastapi-jwt-auth/month) |
7 | | -[](https://pepy.tech/project/fastapi-jwt-auth) |
| 8 | +[](https://pepy.tech/project/fastapi-jwt-auth) |
8 | 9 |
|
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 | +--- |
39 | 11 |
|
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> |
43 | 13 |
|
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> |
51 | 15 |
|
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 | +--- |
55 | 17 |
|
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 😀 |
61 | 20 |
|
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 |
65 | 26 |
|
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 |
71 | 29 |
|
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: |
79 | 30 | ```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 |
144 | 32 | ``` |
145 | 33 |
|
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]' |
207 | 37 | ``` |
208 | 38 |
|
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 | | - |
223 | 39 | ## License |
224 | 40 | This project is licensed under the terms of the MIT license. |
0 commit comments