mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 10:28:48 +02:00
Add cookie-based SPA auth and update container plumbing
Backend now exposes /api/auth/login + /api/auth/logout setting an httpOnly ws_token cookie, and get_current_user accepts either the cookie (SPA) or a Bearer token (n8n/CLI). AuthContext probes the cookie via /api/v1/auth/me. Dockerfiles and compose files updated for the new agent service deps and CopilotKit dev sidecar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import hashlib
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi import Cookie, Depends, Header, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
from sqlmodel import Session, select
|
||||
@@ -22,8 +24,8 @@ def hash_token(token: str) -> str:
|
||||
return hashlib.sha256(token.encode()).hexdigest()
|
||||
|
||||
|
||||
def get_current_user(token: str = Depends(oauth2_scheme)) -> str:
|
||||
# Try JWT first
|
||||
def _validate_token(token: str) -> str:
|
||||
"""Validate JWT and return subject, or raise 401."""
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
@@ -51,3 +53,26 @@ def get_current_user(token: str = Depends(oauth2_scheme)) -> str:
|
||||
return f"api:{api_token.name}"
|
||||
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
|
||||
def get_current_user_cookie_or_bearer(
|
||||
authorization: Optional[str] = Header(default=None),
|
||||
ws_token: Optional[str] = Cookie(default=None),
|
||||
) -> str:
|
||||
"""Accepts httpOnly cookie (SPA) or Bearer token (API clients / n8n)."""
|
||||
token: Optional[str] = None
|
||||
if authorization and authorization.lower().startswith("bearer "):
|
||||
token = authorization.split(" ", 1)[1].strip()
|
||||
elif ws_token:
|
||||
token = ws_token
|
||||
if not token:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
||||
return _validate_token(token)
|
||||
|
||||
|
||||
def get_current_user(
|
||||
authorization: Optional[str] = Header(default=None),
|
||||
ws_token: Optional[str] = Cookie(default=None),
|
||||
) -> str:
|
||||
"""SPA cookie or Bearer token. Single dependency for all v1 endpoints."""
|
||||
return get_current_user_cookie_or_bearer(authorization, ws_token)
|
||||
|
||||
Reference in New Issue
Block a user