L1L2 gen1Gen2 kialakítása

This commit is contained in:
Roo
2026-07-24 09:56:21 +00:00
parent 4594de6c11
commit 33c4d793db
49 changed files with 4108 additions and 127 deletions

View File

@@ -6,6 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.db.session import get_db
from app.services.auth_service import AuthService
from app.services.social_auth_service import SocialAuthService
from app.core.security import create_tokens, DEFAULT_RANK_MAP
from app.core.config import settings
from app.services.config_service import config
@@ -15,6 +16,10 @@ from app.models.identity import User, Device, UserDeviceLink # JAVÍTVA: Device-
from app.core.translation_helper import t # Translation helper
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime, timezone
import httpx
import logging
logger = logging.getLogger(__name__)
router = APIRouter()
@@ -379,4 +384,153 @@ async def restore_account_verify(
status_code=status.HTTP_400_BAD_REQUEST,
detail=result.get("message", "Érvénytelen vagy lejárt kód.")
)
return result
return result
# ── GOOGLE OAUTH ──
class GoogleAuthRequest(BaseModel):
"""Google token from the frontend after successful Google Sign-In.
The frontend sends either an id_token (from Google Sign-In / One Tap)
or an access_token (from Google OAuth2 token client).
"""
id_token: Optional[str] = Field(None, description="Google ID token (JWT) from Google Sign-In")
access_token: Optional[str] = Field(None, description="Google OAuth2 access token from token client")
referred_by_code: Optional[str] = Field(None, description="Optional referral code from registration form")
@router.post("/google", response_model=Token)
async def google_auth(
request: GoogleAuthRequest,
response: Response,
db: AsyncSession = Depends(get_db),
):
"""
Google OAuth bejelentkezés / regisztráció.
A frontend Google Sign-In gombjára kattintva a felhasználó Google tokent kap.
Ezt a tokent küldi el a backendnek, amely:
1. Ellenőrzi a token érvényességét a Google nyilvános kulcsaival
2. Kinyeri az email-t, nevet és Google ID-t
3. Ha a felhasználó már létezik (Login): visszaadja a JWT tokent
4. Ha nem létezik (Register): létrehozza az új felhasználót a Google adataival
5. Opcionális referral_code tárolása, ha a regisztrációs űrlapon megadták
Supports both id_token (from Google Sign-In) and access_token (from OAuth2 token client).
"""
try:
# 1. Determine which token was provided and build verification URL
if request.id_token:
google_verify_url = f"https://oauth2.googleapis.com/tokeninfo?id_token={request.id_token}"
elif request.access_token:
google_verify_url = f"https://oauth2.googleapis.com/tokeninfo?access_token={request.access_token}"
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Hiányzó Google token. Adjon meg id_token vagy access_token értéket."
)
# 2. Verify Google token
async with httpx.AsyncClient() as client:
google_resp = await client.get(google_verify_url)
if google_resp.status_code != 200:
logger.error(f"Google token verification failed: {google_resp.text}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Érvénytelen Google token."
)
google_data = google_resp.json()
# 3. Validate audience (client ID) — only for id_token
# For access_token, the 'aud' field may not be present; validate by 'azp' or skip
if request.id_token:
if google_data.get("aud") != settings.GOOGLE_CLIENT_ID:
logger.error(f"Google token audience mismatch: {google_data.get('aud')}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Érvénytelen Google token (audience mismatch)."
)
else:
# For access_token, check azp (authorized party) matches our client ID
if google_data.get("azp") != settings.GOOGLE_CLIENT_ID:
logger.error(f"Google token azp mismatch: {google_data.get('azp')}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Érvénytelen Google token (azp mismatch)."
)
# 4. Extract user info
google_id = google_data.get("sub")
email = google_data.get("email", "")
first_name = google_data.get("given_name", "")
last_name = google_data.get("family_name", "")
if not google_id or not email:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Hiányzó Google felhasználói adatok."
)
# 5. Get or create user via SocialAuthService
user = await SocialAuthService.get_or_create_social_user(
db=db,
provider="google",
social_id=google_id,
email=email,
first_name=first_name,
last_name=last_name,
referred_by_code=request.referred_by_code
)
if not user:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Hiba a felhasználó létrehozása során."
)
# 6. Generate JWT tokens (same logic as login)
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default=DEFAULT_RANK_MAP)
role_name = user.role.value if hasattr(user.role, 'value') else str(user.role)
role_key = role_name.upper()
token_data = {
"sub": str(user.id),
"role": role_name,
"rank": ranks.get(role_key, 10),
"scope_level": user.scope_level or "individual",
"scope_id": str(user.scope_id) if user.scope_id else str(user.id)
}
access, refresh = create_tokens(data=token_data, remember_me=False)
max_age_days = await config.get_setting(db, "auth_refresh_default_days", default=1)
max_age_sec = int(max_age_days) * 24 * 60 * 60
response.set_cookie(
key="refresh_token",
value=refresh,
httponly=True,
secure=True,
samesite="lax",
domain=settings.COOKIE_DOMAIN,
max_age=max_age_sec
)
return {
"access_token": access,
"refresh_token": refresh,
"token_type": "bearer",
"is_active": user.is_active
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Google auth error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Google hitelesítési hiba: {str(e)}"
)