Rate-limit logins, constant-time creds, pin CORS, secure cookie

Both login endpoints now share verify_admin_credentials (hmac
compare_digest, SEC-02) and a 5/min/IP sliding-window rate limit that
returns 429 with Retry-After (SEC-03). CORS is pinned to the prod
domain and localhost dev origins instead of '*' (SEC-04). The session
cookie's Secure flag is driven by COOKIE_SECURE instead of hardcoded
false (SEC-08).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-06-09 19:13:19 -06:00
parent 15ea1ef4c2
commit b4123703ef
3 changed files with 79 additions and 25 deletions

View File

@@ -1,22 +1,20 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, Request
from fastapi.security import OAuth2PasswordRequestForm
from app.auth import create_access_token, get_current_user, get_current_user_cookie_or_bearer
from app.config import settings
from app.auth import (
check_login_rate_limit,
create_access_token,
get_current_user_cookie_or_bearer,
verify_admin_credentials,
)
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
if (
form_data.username != settings.ADMIN_USERNAME
or form_data.password != settings.ADMIN_PASSWORD
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
)
def login(request: Request, form_data: OAuth2PasswordRequestForm = Depends()):
check_login_rate_limit(request)
verify_admin_credentials(form_data.username, form_data.password)
token = create_access_token(form_data.username)
return {"access_token": token, "token_type": "bearer"}