feat: add JWT creation, decoding and cookie helpers
This commit is contained in:
parent
f93793a1d8
commit
fb7284b117
47
app/core/auth.py
Normal file
47
app/core/auth.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import Request, Response
|
||||||
|
from jose import JWTError, jwt
|
||||||
|
|
||||||
|
from app.core.config import get_settings
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
|
COOKIE_NAME = "access_token"
|
||||||
|
ALGORITHM = "HS256"
|
||||||
|
TOKEN_EXPIRE_HOURS = 8
|
||||||
|
|
||||||
|
|
||||||
|
def create_access_token(username: str, is_admin: bool) -> str:
|
||||||
|
expire = datetime.now(timezone.utc) + timedelta(hours=TOKEN_EXPIRE_HOURS)
|
||||||
|
return jwt.encode(
|
||||||
|
{"sub": username, "is_admin": is_admin, "exp": expire},
|
||||||
|
settings.SECRET_KEY,
|
||||||
|
algorithm=ALGORITHM,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def decode_token(token: str) -> Optional[dict]:
|
||||||
|
try:
|
||||||
|
return jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
except JWTError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_token_from_request(request: Request) -> Optional[str]:
|
||||||
|
return request.cookies.get(COOKIE_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
def set_auth_cookie(response: Response, token: str) -> None:
|
||||||
|
response.set_cookie(
|
||||||
|
key=COOKIE_NAME,
|
||||||
|
value=token,
|
||||||
|
httponly=True,
|
||||||
|
samesite="lax",
|
||||||
|
secure=settings.APP_ENV == "production",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_auth_cookie(response: Response) -> None:
|
||||||
|
response.delete_cookie(key=COOKIE_NAME, httponly=True, samesite="lax")
|
||||||
27
tests/test_core_auth.py
Normal file
27
tests/test_core_auth.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from app.core.auth import COOKIE_NAME, create_access_token, decode_token
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_and_decode_token():
|
||||||
|
token = create_access_token(username="alice", is_admin=False)
|
||||||
|
payload = decode_token(token)
|
||||||
|
assert payload is not None
|
||||||
|
assert payload["sub"] == "alice"
|
||||||
|
assert payload["is_admin"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_claim():
|
||||||
|
token = create_access_token(username="admin", is_admin=True)
|
||||||
|
assert decode_token(token)["is_admin"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_invalid_token():
|
||||||
|
assert decode_token("not.a.valid.token") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_tampered_token():
|
||||||
|
token = create_access_token(username="alice", is_admin=False)
|
||||||
|
assert decode_token(token[:-4] + "xxxx") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_cookie_name():
|
||||||
|
assert COOKIE_NAME == "access_token"
|
||||||
Loading…
x
Reference in New Issue
Block a user