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