feature/auth #4
@@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
from core.security import (
|
from core.security import (
|
||||||
AuthContext,
|
AuthContext,
|
||||||
@@ -32,9 +33,9 @@ async def login_handler(payload: LoginInput):
|
|||||||
|
|
||||||
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def logout_handler(
|
async def logout_handler(
|
||||||
auth_context: AuthContext = Depends(get_auth_context),
|
auth_context: Annotated[AuthContext, Depends(get_auth_context)],
|
||||||
) -> Response:
|
) -> Response:
|
||||||
expires_at = datetime.fromtimestamp(auth_context.payload.exp, tz=timezone.utc)
|
expires_at = datetime.fromtimestamp(auth_context.payload.exp, tz=UTC)
|
||||||
if expires_at > datetime.now(timezone.utc):
|
if expires_at > datetime.now(UTC):
|
||||||
await revoke_token(jti=auth_context.payload.jti, expires_at=expires_at)
|
await revoke_token(jti=auth_context.payload.jti, expires_at=expires_at)
|
||||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import UTC, datetime, timedelta
|
||||||
|
from typing import Annotated
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from authlib.jose import jwt # type: ignore
|
from authlib.jose import jwt # type: ignore
|
||||||
@@ -43,7 +44,7 @@ def verify_password(plain_password: str, password_hash: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def create_access_token(*, subject: str) -> str:
|
def create_access_token(*, subject: str) -> str:
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(UTC)
|
||||||
expires_delta = timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
|
expires_delta = timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
|
||||||
expires_at = int((now + expires_delta).timestamp())
|
expires_at = int((now + expires_delta).timestamp())
|
||||||
|
|
||||||
@@ -72,7 +73,10 @@ def decode_access_token(token: str) -> AccessTokenPayload:
|
|||||||
|
|
||||||
|
|
||||||
async def get_auth_context(
|
async def get_auth_context(
|
||||||
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
|
credentials: Annotated[
|
||||||
|
HTTPAuthorizationCredentials | None,
|
||||||
|
Depends(bearer_scheme),
|
||||||
|
],
|
||||||
) -> AuthContext:
|
) -> AuthContext:
|
||||||
if credentials is None or credentials.scheme.lower() != "bearer":
|
if credentials is None or credentials.scheme.lower() != "bearer":
|
||||||
raise token_auth_error()
|
raise token_auth_error()
|
||||||
@@ -94,6 +98,6 @@ async def get_auth_context(
|
|||||||
|
|
||||||
|
|
||||||
async def require_authenticated_user(
|
async def require_authenticated_user(
|
||||||
auth_context: AuthContext = Depends(get_auth_context),
|
auth_context: Annotated[AuthContext, Depends(get_auth_context)],
|
||||||
) -> AuthUserDocument:
|
) -> AuthUserDocument:
|
||||||
return auth_context.user
|
return auth_context.user
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,6 @@
|
|||||||
from datetime import datetime, timezone
|
from collections.abc import Mapping
|
||||||
from typing import Any, Mapping
|
from datetime import UTC, datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from core.mongodb import get_database
|
from core.mongodb import get_database
|
||||||
from models.auth import AuthUserDocument
|
from models.auth import AuthUserDocument
|
||||||
@@ -10,7 +11,7 @@ REVOKED_COLLECTION_NAME = "revoked_tokens"
|
|||||||
|
|
||||||
|
|
||||||
def utcnow() -> datetime:
|
def utcnow() -> datetime:
|
||||||
return datetime.now(timezone.utc)
|
return datetime.now(UTC)
|
||||||
|
|
||||||
|
|
||||||
async def users_collection() -> AsyncIOMotorCollection[dict[str, Any]]:
|
async def users_collection() -> AsyncIOMotorCollection[dict[str, Any]]:
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,6 @@
|
|||||||
from datetime import datetime, timezone
|
from collections.abc import Mapping
|
||||||
from typing import Any, Mapping
|
from datetime import UTC, datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from core.mongodb import get_database
|
from core.mongodb import get_database
|
||||||
from models.proxy import ProxyDocument, ProxyUpdateInput
|
from models.proxy import ProxyDocument, ProxyUpdateInput
|
||||||
@@ -12,7 +13,7 @@ COLLECTION_NAME = "proxies"
|
|||||||
|
|
||||||
|
|
||||||
def _utcnow() -> datetime:
|
def _utcnow() -> datetime:
|
||||||
return datetime.now(timezone.utc)
|
return datetime.now(UTC)
|
||||||
|
|
||||||
|
|
||||||
async def _collection() -> AsyncIOMotorCollection[dict[str, Any]]:
|
async def _collection() -> AsyncIOMotorCollection[dict[str, Any]]:
|
||||||
|
|||||||
+5
-1
@@ -12,8 +12,12 @@ dependencies = [
|
|||||||
"redis>=7.4.0",
|
"redis>=7.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py314"
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["E", "F", "I"]
|
select = ["E", "F", "I", "B", "UP"]
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
quote-style = "double"
|
quote-style = "double"
|
||||||
|
|||||||
Reference in New Issue
Block a user