Files
service-finder/backend/app/api/auth.py.old

133 lines
4.2 KiB
Python
Executable File

from datetime import timedelta
from typing import Dict, Any
from fastapi import APIRouter, HTTPException
from app.core.config import settings
from app.core.security import create_token, decode_token
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/login")
def login(payload: Dict[str, Any]):
"""
payload:
{
"org_id": "<uuid>",
"login": "<username or email>",
"password": "<plain>"
}
"""
from app.db.session import get_conn
conn = get_conn()
try:
cur = conn.cursor()
cur.execute("BEGIN;")
org_id = (payload.get("org_id") or "").strip()
login_id = (payload.get("login") or "").strip()
password = payload.get("password") or ""
if not org_id or not login_id or not password:
raise HTTPException(status_code=400, detail="org_id, login, password required")
# RLS miatt kötelező: org kontextus beállítás
cur.execute("SELECT set_config('app.tenant_org_id', %s, false);", (org_id,))
# account + credential
cur.execute(
"""
SELECT
a.account_id::text,
a.org_id::text,
a.username::text,
a.email::text,
c.password_hash,
c.is_active
FROM app.account a
JOIN app.account_credential c ON c.account_id = a.account_id
WHERE a.org_id = %s::uuid
AND (a.username = %s::citext OR a.email = %s::citext)
AND c.is_active = true
LIMIT 1;
""",
(org_id, login_id, login_id),
)
row = cur.fetchone()
if not row:
raise HTTPException(status_code=401, detail="Invalid credentials")
account_id, org_id_db, username, email, password_hash, cred_active = row
# Jelszó ellenőrzés pgcrypto-val: crypt(plain, stored_hash) = stored_hash
cur.execute("SELECT crypt(%s, %s) = %s;", (password, password_hash, password_hash))
ok = cur.fetchone()[0]
if not ok:
raise HTTPException(status_code=401, detail="Invalid credentials")
# MVP: role később membershipből; most fixen tenant_admin
role_code = "tenant_admin"
is_platform_admin = False
access = create_token(
{
"sub": account_id,
"org_id": org_id_db,
"role": role_code,
"is_platform_admin": is_platform_admin,
"type": "access",
},
settings.JWT_SECRET,
timedelta(minutes=settings.JWT_ACCESS_MINUTES),
)
refresh = create_token(
{
"sub": account_id,
"org_id": org_id_db,
"role": role_code,
"is_platform_admin": is_platform_admin,
"type": "refresh",
},
settings.JWT_SECRET,
timedelta(days=settings.JWT_REFRESH_DAYS),
)
conn.commit()
return {"access_token": access, "refresh_token": refresh, "token_type": "bearer"}
except HTTPException:
conn.rollback()
raise
except Exception as e:
conn.rollback()
raise HTTPException(status_code=500, detail=str(e))
finally:
conn.close()
@router.post("/refresh")
def refresh_token(payload: Dict[str, Any]):
token = payload.get("refresh_token") or ""
if not token:
raise HTTPException(status_code=400, detail="refresh_token required")
try:
claims = decode_token(token, settings.JWT_SECRET)
if claims.get("type") != "refresh":
raise HTTPException(status_code=401, detail="Invalid refresh token type")
access = create_token(
{
"sub": claims.get("sub"),
"org_id": claims.get("org_id"),
"role": claims.get("role"),
"is_platform_admin": claims.get("is_platform_admin", False),
"type": "access",
},
settings.JWT_SECRET,
timedelta(minutes=settings.JWT_ACCESS_MINUTES),
)
return {"access_token": access, "token_type": "bearer"}
except Exception:
raise HTTPException(status_code=401, detail="Invalid or expired refresh token")