diff --git a/app/core/jwt.py b/app/core/jwt.py new file mode 100644 index 0000000..801d856 --- /dev/null +++ b/app/core/jwt.py @@ -0,0 +1,38 @@ +from datetime import datetime, timedelta, timezone +from typing import Any +from uuid import uuid4 + +from jose import JWTError, jwt + +from app.config import settings + + +# формирует JWT с claim'ами: +# sub (email или id пользователя), +# iat, +# exp, +# jti (случайный идентификатор), +# typ: "access", +# подписывает HS256 секретом из настроек; +def create_access_token(sub: str, ttl_min: int | None = None) -> str: + now = datetime.now(timezone.utc) + ttl = ttl_min if ttl_min is not None else settings.access_token_ttl_min + payload: dict[str, Any] = { + "sub": sub, + "iat": now, + "exp": now + timedelta(minutes=ttl), + "jti": uuid4().hex, + "typ": "access", + } + return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) + + +# decode_token(token) – декодирует и проверяет подпись/срок, +# при ошибке бросает `ValueError` с понятным сообщением. +def decode_token(token: str) -> dict[str, Any]: + try: + return jwt.decode( + token, settings.jwt_secret, algorithms=[settings.jwt_algorithm] + ) + except JWTError as exc: + raise ValueError(f"Invalid token: {exc}") from exc