feat(auth): implement authentication endpoints and JWT handling
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from api.auth.router import router
|
||||
|
||||
__all__ = ["router"]
|
||||
@@ -0,0 +1,40 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from core.security import (
|
||||
AuthContext,
|
||||
create_access_token,
|
||||
get_auth_context,
|
||||
verify_password,
|
||||
)
|
||||
from db.auth import get_user_by_username, revoke_token
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from models.auth import LoginInput, TokenResponse
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/auth",
|
||||
tags=["Auth"],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/login", response_model=TokenResponse)
|
||||
async def login_handler(payload: LoginInput):
|
||||
user = await get_user_by_username(payload.username)
|
||||
if user is None or not verify_password(payload.password, user.password_hash):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
token = create_access_token(subject=user.username)
|
||||
return TokenResponse(access_token=token)
|
||||
|
||||
|
||||
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def logout_handler(
|
||||
auth_context: AuthContext = Depends(get_auth_context),
|
||||
) -> Response:
|
||||
expires_at = datetime.fromtimestamp(auth_context.payload.exp, tz=timezone.utc)
|
||||
if expires_at > datetime.now(timezone.utc):
|
||||
await revoke_token(jti=auth_context.payload.jti, expires_at=expires_at)
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
+19
-7
@@ -1,17 +1,20 @@
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from core.security import require_authenticated_user
|
||||
from db.proxy import (
|
||||
create_proxy,
|
||||
delete_proxy,
|
||||
get_proxy_by_port,
|
||||
list_proxies as list_proxy_documents,
|
||||
update_proxy,
|
||||
)
|
||||
from db.proxy import (
|
||||
list_proxies as list_proxy_documents,
|
||||
)
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from models.proxy import ProxyCreateInput, ProxyUpdateInput
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/proxy",
|
||||
tags=["Proxy"]
|
||||
tags=["Proxy"],
|
||||
dependencies=[Depends(require_authenticated_user)],
|
||||
)
|
||||
|
||||
|
||||
@@ -24,7 +27,10 @@ async def list_proxies_handler():
|
||||
async def get_proxy(proxy_port: int):
|
||||
proxy = await get_proxy_by_port(proxy_port)
|
||||
if proxy is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Proxy not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Proxy not found",
|
||||
)
|
||||
return proxy
|
||||
|
||||
|
||||
@@ -49,7 +55,10 @@ async def create_proxy_handler(payload: ProxyCreateInput):
|
||||
async def edit_proxy(proxy_port: int, payload: ProxyUpdateInput):
|
||||
updated_proxy = await update_proxy(proxy_port, payload)
|
||||
if updated_proxy is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Proxy not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Proxy not found",
|
||||
)
|
||||
return updated_proxy
|
||||
|
||||
|
||||
@@ -57,4 +66,7 @@ async def edit_proxy(proxy_port: int, payload: ProxyUpdateInput):
|
||||
async def remove_proxy(proxy_port: int):
|
||||
deleted = await delete_proxy(proxy_port)
|
||||
if not deleted:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Proxy not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Proxy not found",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user