fastapi-oauth2-cookie 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_oauth2_cookie-0.0.1/.github/workflows/publish.yml +31 -0
- fastapi_oauth2_cookie-0.0.1/.gitignore +70 -0
- fastapi_oauth2_cookie-0.0.1/PKG-INFO +134 -0
- fastapi_oauth2_cookie-0.0.1/README.md +123 -0
- fastapi_oauth2_cookie-0.0.1/fastapi_oauth2_cookie/__init__.py +4 -0
- fastapi_oauth2_cookie-0.0.1/fastapi_oauth2_cookie/auth_cookie_manager.py +66 -0
- fastapi_oauth2_cookie-0.0.1/fastapi_oauth2_cookie/oauth2_password_cookie.py +59 -0
- fastapi_oauth2_cookie-0.0.1/fastapi_oauth2_cookie/py.typed +0 -0
- fastapi_oauth2_cookie-0.0.1/pyproject.toml +39 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment:
|
|
11
|
+
name: pypi
|
|
12
|
+
url: https://pypi.org/p/fastapi-oauth2-cookie
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
|
|
27
|
+
- name: Build package
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Publish package to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
|
|
6
|
+
# Virtual environments
|
|
7
|
+
env/
|
|
8
|
+
venv/
|
|
9
|
+
.venv/
|
|
10
|
+
env3.*
|
|
11
|
+
*venv*
|
|
12
|
+
|
|
13
|
+
# Packaging / builds
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
.eggs/
|
|
18
|
+
|
|
19
|
+
# Testing / coverage
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.pyre/
|
|
23
|
+
.pytype/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
.hypothesis/
|
|
26
|
+
htmlcov/
|
|
27
|
+
.coverage*
|
|
28
|
+
coverage.xml
|
|
29
|
+
|
|
30
|
+
# Documentation builds
|
|
31
|
+
docs/_build/
|
|
32
|
+
site/
|
|
33
|
+
site_build/
|
|
34
|
+
docs_build/
|
|
35
|
+
|
|
36
|
+
# Jupyter
|
|
37
|
+
.ipynb_checkpoints/
|
|
38
|
+
|
|
39
|
+
# IDEs
|
|
40
|
+
.idea/
|
|
41
|
+
.vscode/
|
|
42
|
+
|
|
43
|
+
# Environment / secrets
|
|
44
|
+
.env
|
|
45
|
+
.env.*
|
|
46
|
+
!.env.example
|
|
47
|
+
|
|
48
|
+
# Local files
|
|
49
|
+
test.db
|
|
50
|
+
log.txt
|
|
51
|
+
.cache/
|
|
52
|
+
|
|
53
|
+
# Deployment / tooling
|
|
54
|
+
.netlify/
|
|
55
|
+
.codspeed/
|
|
56
|
+
|
|
57
|
+
# Archives / generated files
|
|
58
|
+
docs.zip
|
|
59
|
+
archive.zip
|
|
60
|
+
|
|
61
|
+
# Vim / editor temporary files
|
|
62
|
+
*~
|
|
63
|
+
*.swp
|
|
64
|
+
*.swo
|
|
65
|
+
|
|
66
|
+
# macOS
|
|
67
|
+
.DS_Store
|
|
68
|
+
|
|
69
|
+
# Emacs
|
|
70
|
+
.projectile
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fastapi-oauth2-cookie
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A lightweight FastAPI library for OAuth2 via HttpOnly Cookies and CSRF headers.
|
|
5
|
+
Project-URL: Homepage, https://github.com/D1918/fastapi-oauth2-cookie.git
|
|
6
|
+
Author-email: Denis Lyovin <DOS1918@proton.me>
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: fastapi>=0.95.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# FastAPI OAuth2 Cookie Authentication
|
|
13
|
+
|
|
14
|
+
A lightweight, production-ready FastAPI library for handling OAuth2 authentication using **HttpOnly Cookies** and **CSRF headers**.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
* 🔒 **HttpOnly Cookies**: Keeps JWT access and refresh tokens out of `localStorage` to prevent XSS attacks.
|
|
19
|
+
* ⚡️ **Swagger UI Compatible**: Extends FastAPI's native `OAuth2` class to keep your OpenAPI docs working.
|
|
20
|
+
* 🛡️ **CSRF Defense**: Native support for extracting and requiring CSRF tokens via headers (`X-CSRF-TOKEN`).
|
|
21
|
+
* 🪶 **Zero Overhead**: No third-party dependencies outside of FastAPI.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install fastapi-oauth2-cookie
|
|
27
|
+
# or using uv:
|
|
28
|
+
uv add fastapi-oauth2-cookie
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
Here is everything you need to set up login, logout, and a protected route.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from typing import Annotated
|
|
38
|
+
from fastapi import Depends, FastAPI, HTTPException, Response, status
|
|
39
|
+
from fastapi.security import OAuth2PasswordRequestForm
|
|
40
|
+
from fastapi_oauth2_cookie import AuthCookieManager, OAuth2PasswordCookie, Tokens
|
|
41
|
+
|
|
42
|
+
app = FastAPI()
|
|
43
|
+
|
|
44
|
+
cookie_manager = AuthCookieManager(secure=False) # set True in production
|
|
45
|
+
|
|
46
|
+
oauth2_scheme = OAuth2PasswordCookie(
|
|
47
|
+
tokenUrl="token",
|
|
48
|
+
access_cookie_name=cookie_manager.access_cookie_name,
|
|
49
|
+
require_csrf=False # set to True in production to avoid CSRF attacks
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
FAKE_USERS_DB = {
|
|
53
|
+
"johndoe": {
|
|
54
|
+
"username": "johndoe",
|
|
55
|
+
"hashed_password": "fakehashedsecretpassword",
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def fake_hash_password(password: str):
|
|
60
|
+
return "fakehashed" + password
|
|
61
|
+
|
|
62
|
+
def fake_decode_token(token: str):
|
|
63
|
+
return FAKE_USERS_DB.get(token)
|
|
64
|
+
|
|
65
|
+
async def get_current_user(tokens: Tokens = Depends(oauth2_scheme)):
|
|
66
|
+
user = fake_decode_token(tokens.access_token)
|
|
67
|
+
if not user:
|
|
68
|
+
raise HTTPException(
|
|
69
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
70
|
+
detail="Invalid authentication credentials",
|
|
71
|
+
)
|
|
72
|
+
return user
|
|
73
|
+
|
|
74
|
+
@app.post("/token")
|
|
75
|
+
async def login(
|
|
76
|
+
response: Response,
|
|
77
|
+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
|
78
|
+
):
|
|
79
|
+
user = FAKE_USERS_DB.get(form_data.username)
|
|
80
|
+
if not user or fake_hash_password(form_data.password) != user["hashed_password"]:
|
|
81
|
+
raise HTTPException(
|
|
82
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
83
|
+
detail="Incorrect username or password"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# 1. Set the HttpOnly cookie for the browser
|
|
87
|
+
cookie_manager.set_auth_cookies(response, access_token=user["username"])
|
|
88
|
+
|
|
89
|
+
# 2. Return standard OAuth2 JSON so Swagger UI works correctly
|
|
90
|
+
return {"access_token": user["username"], "token_type": "bearer"}
|
|
91
|
+
|
|
92
|
+
@app.get("/users/me")
|
|
93
|
+
async def read_users_me(current_user: dict = Depends(get_current_user)):
|
|
94
|
+
return {"username": current_user["username"]}
|
|
95
|
+
|
|
96
|
+
@app.post("/logout")
|
|
97
|
+
def logout(response: Response):
|
|
98
|
+
cookie_manager.clear_auth_cookies(response)
|
|
99
|
+
return {"message": "Successfully logged out"}
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Core API
|
|
104
|
+
|
|
105
|
+
### `AuthCookieManager`
|
|
106
|
+
|
|
107
|
+
Handles the configuration and application of cookies
|
|
108
|
+
|
|
109
|
+
* `set_auth_cookies(response, access_token, refresh_token=None, ...)`: Attaches `HttpOnly` cookies to the response.
|
|
110
|
+
* `clear_auth_cookies(response)`: Clears the authentication cookies.
|
|
111
|
+
|
|
112
|
+
### `OAuth2PasswordCookie`
|
|
113
|
+
|
|
114
|
+
The FastAPI dependency you inject into your routes.
|
|
115
|
+
|
|
116
|
+
* Extracts the token from the configured `access_cookie_name`.
|
|
117
|
+
* Optionally enforces and extracts a CSRF token from the `csrf_header_name` (defaults to `X-CSRF-TOKEN`).
|
|
118
|
+
* Returns a named tuple `Tokens`: `(access_token, csrf_token)`. Raises `401 Unauthorized` if tokens are missing or invalid.
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
### Run dev server
|
|
123
|
+
```bash
|
|
124
|
+
uv run fastapi dev dev/main.py
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Run tests
|
|
128
|
+
```bash
|
|
129
|
+
uv run pytest
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# FastAPI OAuth2 Cookie Authentication
|
|
2
|
+
|
|
3
|
+
A lightweight, production-ready FastAPI library for handling OAuth2 authentication using **HttpOnly Cookies** and **CSRF headers**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* 🔒 **HttpOnly Cookies**: Keeps JWT access and refresh tokens out of `localStorage` to prevent XSS attacks.
|
|
8
|
+
* ⚡️ **Swagger UI Compatible**: Extends FastAPI's native `OAuth2` class to keep your OpenAPI docs working.
|
|
9
|
+
* 🛡️ **CSRF Defense**: Native support for extracting and requiring CSRF tokens via headers (`X-CSRF-TOKEN`).
|
|
10
|
+
* 🪶 **Zero Overhead**: No third-party dependencies outside of FastAPI.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install fastapi-oauth2-cookie
|
|
16
|
+
# or using uv:
|
|
17
|
+
uv add fastapi-oauth2-cookie
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
Here is everything you need to set up login, logout, and a protected route.
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from typing import Annotated
|
|
27
|
+
from fastapi import Depends, FastAPI, HTTPException, Response, status
|
|
28
|
+
from fastapi.security import OAuth2PasswordRequestForm
|
|
29
|
+
from fastapi_oauth2_cookie import AuthCookieManager, OAuth2PasswordCookie, Tokens
|
|
30
|
+
|
|
31
|
+
app = FastAPI()
|
|
32
|
+
|
|
33
|
+
cookie_manager = AuthCookieManager(secure=False) # set True in production
|
|
34
|
+
|
|
35
|
+
oauth2_scheme = OAuth2PasswordCookie(
|
|
36
|
+
tokenUrl="token",
|
|
37
|
+
access_cookie_name=cookie_manager.access_cookie_name,
|
|
38
|
+
require_csrf=False # set to True in production to avoid CSRF attacks
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
FAKE_USERS_DB = {
|
|
42
|
+
"johndoe": {
|
|
43
|
+
"username": "johndoe",
|
|
44
|
+
"hashed_password": "fakehashedsecretpassword",
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def fake_hash_password(password: str):
|
|
49
|
+
return "fakehashed" + password
|
|
50
|
+
|
|
51
|
+
def fake_decode_token(token: str):
|
|
52
|
+
return FAKE_USERS_DB.get(token)
|
|
53
|
+
|
|
54
|
+
async def get_current_user(tokens: Tokens = Depends(oauth2_scheme)):
|
|
55
|
+
user = fake_decode_token(tokens.access_token)
|
|
56
|
+
if not user:
|
|
57
|
+
raise HTTPException(
|
|
58
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
59
|
+
detail="Invalid authentication credentials",
|
|
60
|
+
)
|
|
61
|
+
return user
|
|
62
|
+
|
|
63
|
+
@app.post("/token")
|
|
64
|
+
async def login(
|
|
65
|
+
response: Response,
|
|
66
|
+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
|
67
|
+
):
|
|
68
|
+
user = FAKE_USERS_DB.get(form_data.username)
|
|
69
|
+
if not user or fake_hash_password(form_data.password) != user["hashed_password"]:
|
|
70
|
+
raise HTTPException(
|
|
71
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
72
|
+
detail="Incorrect username or password"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# 1. Set the HttpOnly cookie for the browser
|
|
76
|
+
cookie_manager.set_auth_cookies(response, access_token=user["username"])
|
|
77
|
+
|
|
78
|
+
# 2. Return standard OAuth2 JSON so Swagger UI works correctly
|
|
79
|
+
return {"access_token": user["username"], "token_type": "bearer"}
|
|
80
|
+
|
|
81
|
+
@app.get("/users/me")
|
|
82
|
+
async def read_users_me(current_user: dict = Depends(get_current_user)):
|
|
83
|
+
return {"username": current_user["username"]}
|
|
84
|
+
|
|
85
|
+
@app.post("/logout")
|
|
86
|
+
def logout(response: Response):
|
|
87
|
+
cookie_manager.clear_auth_cookies(response)
|
|
88
|
+
return {"message": "Successfully logged out"}
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Core API
|
|
93
|
+
|
|
94
|
+
### `AuthCookieManager`
|
|
95
|
+
|
|
96
|
+
Handles the configuration and application of cookies
|
|
97
|
+
|
|
98
|
+
* `set_auth_cookies(response, access_token, refresh_token=None, ...)`: Attaches `HttpOnly` cookies to the response.
|
|
99
|
+
* `clear_auth_cookies(response)`: Clears the authentication cookies.
|
|
100
|
+
|
|
101
|
+
### `OAuth2PasswordCookie`
|
|
102
|
+
|
|
103
|
+
The FastAPI dependency you inject into your routes.
|
|
104
|
+
|
|
105
|
+
* Extracts the token from the configured `access_cookie_name`.
|
|
106
|
+
* Optionally enforces and extracts a CSRF token from the `csrf_header_name` (defaults to `X-CSRF-TOKEN`).
|
|
107
|
+
* Returns a named tuple `Tokens`: `(access_token, csrf_token)`. Raises `401 Unauthorized` if tokens are missing or invalid.
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
### Run dev server
|
|
112
|
+
```bash
|
|
113
|
+
uv run fastapi dev dev/main.py
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Run tests
|
|
117
|
+
```bash
|
|
118
|
+
uv run pytest
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from typing import Literal, Optional
|
|
2
|
+
from fastapi import Response
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AuthCookieManager:
|
|
6
|
+
"""
|
|
7
|
+
Basic auth cookie management
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
access_cookie_name: str = "ACCESS-TOKEN",
|
|
13
|
+
refresh_cookie_name: str = "REFRESH-TOKEN",
|
|
14
|
+
refresh_path: str = "/refresh-token",
|
|
15
|
+
secure: bool = True,
|
|
16
|
+
samesite: Literal["lax", "strict", "none"] = "lax",
|
|
17
|
+
):
|
|
18
|
+
self.access_cookie_name: str = access_cookie_name
|
|
19
|
+
self.refresh_cookie_name: str = refresh_cookie_name
|
|
20
|
+
self.refresh_path = refresh_path
|
|
21
|
+
self.secure: bool = secure
|
|
22
|
+
self.samesite: Literal["lax", "strict", "none"] = samesite
|
|
23
|
+
|
|
24
|
+
def set_auth_cookies(
|
|
25
|
+
self,
|
|
26
|
+
response: Response,
|
|
27
|
+
access_token: str,
|
|
28
|
+
refresh_token: Optional[str] = None,
|
|
29
|
+
max_age_access: int = 900,
|
|
30
|
+
max_age_refresh: int = 604800,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Sets auth cookies using the manager's global configuration."""
|
|
33
|
+
response.set_cookie(
|
|
34
|
+
key=self.access_cookie_name,
|
|
35
|
+
value=access_token,
|
|
36
|
+
httponly=True,
|
|
37
|
+
secure=self.secure,
|
|
38
|
+
samesite=self.samesite,
|
|
39
|
+
max_age=max_age_access,
|
|
40
|
+
)
|
|
41
|
+
if refresh_token:
|
|
42
|
+
response.set_cookie(
|
|
43
|
+
key=self.refresh_cookie_name,
|
|
44
|
+
value=refresh_token,
|
|
45
|
+
path=self.refresh_path,
|
|
46
|
+
httponly=True,
|
|
47
|
+
secure=self.secure,
|
|
48
|
+
samesite=self.samesite,
|
|
49
|
+
max_age=max_age_refresh,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def clear_auth_cookies(self, response: Response) -> None:
|
|
53
|
+
"""Clears auth cookies using the manager's global configuration."""
|
|
54
|
+
response.delete_cookie(
|
|
55
|
+
key=self.access_cookie_name,
|
|
56
|
+
secure=self.secure,
|
|
57
|
+
samesite=self.samesite,
|
|
58
|
+
httponly=True,
|
|
59
|
+
)
|
|
60
|
+
response.delete_cookie(
|
|
61
|
+
key=self.refresh_cookie_name,
|
|
62
|
+
secure=self.secure,
|
|
63
|
+
samesite=self.samesite,
|
|
64
|
+
httponly=True,
|
|
65
|
+
path=self.refresh_path,
|
|
66
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Dict, NamedTuple, Optional
|
|
2
|
+
from fastapi import HTTPException, Request, status
|
|
3
|
+
from fastapi.openapi.models import OAuthFlowPassword
|
|
4
|
+
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
|
5
|
+
from fastapi.security import OAuth2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tokens(NamedTuple):
|
|
9
|
+
access_token: str
|
|
10
|
+
csrf_token: Optional[str]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OAuth2PasswordCookie(OAuth2):
|
|
14
|
+
"""
|
|
15
|
+
OAuth2 security scheme that extracts the access token from an HttpOnly cookie
|
|
16
|
+
and the CSRF token from a custom header.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
tokenUrl: str,
|
|
22
|
+
scheme_name: Optional[str] = None,
|
|
23
|
+
scopes: Optional[Dict[str, str]] = None,
|
|
24
|
+
auto_error: bool = True,
|
|
25
|
+
access_cookie_name: str = "ACCESS-TOKEN",
|
|
26
|
+
csrf_header_name: str = "X-CSRF-TOKEN",
|
|
27
|
+
require_csrf: bool = True,
|
|
28
|
+
):
|
|
29
|
+
if not scopes:
|
|
30
|
+
scopes = {}
|
|
31
|
+
|
|
32
|
+
flows = OAuthFlowsModel(
|
|
33
|
+
password=OAuthFlowPassword(tokenUrl=tokenUrl, scopes=scopes)
|
|
34
|
+
)
|
|
35
|
+
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
|
|
36
|
+
|
|
37
|
+
self.access_cookie_name = access_cookie_name
|
|
38
|
+
self.csrf_header_name = csrf_header_name
|
|
39
|
+
self.require_csrf = require_csrf
|
|
40
|
+
|
|
41
|
+
async def __call__( # type: ignore[override]
|
|
42
|
+
self, request: Request
|
|
43
|
+
) -> Optional[Tokens]:
|
|
44
|
+
access_token = request.cookies.get(self.access_cookie_name)
|
|
45
|
+
csrf_token = (
|
|
46
|
+
request.headers.get(self.csrf_header_name) if self.require_csrf else None
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if not access_token or (self.require_csrf and not csrf_token):
|
|
50
|
+
if self.auto_error:
|
|
51
|
+
raise HTTPException(
|
|
52
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
53
|
+
detail="Not authenticated",
|
|
54
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
return Tokens(access_token=access_token, csrf_token=csrf_token)
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fastapi-oauth2-cookie"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "A lightweight FastAPI library for OAuth2 via HttpOnly Cookies and CSRF headers."
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Denis Lyovin", email = "DOS1918@proton.me" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"fastapi>=0.95.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=9.1.1",
|
|
22
|
+
"httpx2>=2.12.0",
|
|
23
|
+
"fastapi[standard]>=0.141.1",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/D1918/fastapi-oauth2-cookie.git"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.sdist]
|
|
30
|
+
exclude = [
|
|
31
|
+
"1.md",
|
|
32
|
+
"uv.lock",
|
|
33
|
+
".gitignore",
|
|
34
|
+
"tests/",
|
|
35
|
+
"dev/"
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["fastapi_oauth2_cookie"]
|