L1L2 gen1Gen2 kialakítása
This commit is contained in:
@@ -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)}"
|
||||
)
|
||||
Reference in New Issue
Block a user