from fastapi import APIRouter, Depends, HTTPException, status, Request from fastapi.security import OAuth2PasswordRequestForm from fastapi.responses import RedirectResponse from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from authlib.integrations.starlette_client import OAuth 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.schemas.auth import ( UserLiteRegister, Token, PasswordResetRequest, UserKYCComplete, PasswordResetConfirm ) from app.api.deps import get_current_user from app.models.identity import User router = APIRouter() # --- GOOGLE OAUTH KONFIGURÁCIÓ --- oauth = OAuth() oauth.register( name='google', client_id=settings.GOOGLE_CLIENT_ID, client_secret=settings.GOOGLE_CLIENT_SECRET, server_metadata_url='https://accounts.google.com/.well-known/openid-configuration', client_kwargs={'scope': 'openid email profile'} ) # --- SOCIAL AUTH ENDPOINTS --- @router.get("/login/google") async def login_google(request: Request): """ Step 1: Átirányítás a Google bejelentkező oldalára. """ redirect_uri = settings.GOOGLE_CALLBACK_URL return await oauth.google.authorize_redirect(request, redirect_uri) @router.get("/callback/google") async def auth_google(request: Request, db: AsyncSession = Depends(get_db)): """ Step 2: Google visszahívás lekezelése + Dupla Token generálás. """ try: token = await oauth.google.authorize_access_token(request) user_info = token.get('userinfo') except Exception: raise HTTPException(status_code=400, detail="Google hitelesítési hiba.") if not user_info: raise HTTPException(status_code=400, detail="Nincs adat a Google-től.") # Step 1: Technikai user létrehozása/keresése (inaktív, nincs mappa) user = await SocialAuthService.get_or_create_social_user( db, provider="google", social_id=user_info['sub'], email=user_info['email'], first_name=user_info.get('given_name'), last_name=user_info.get('family_name') ) # Dinamikus token generálás 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) user_rank = ranks.get(role_name, 10) token_data = { "sub": str(user.id), "role": role_name, "rank": user_rank, "scope_level": user.scope_level or "individual", "scope_id": user.scope_id or str(user.id), "region": user.region_code } access, refresh = create_tokens(data=token_data) # Visszatérés a frontendre mindkét tokennel response_url = f"{settings.FRONTEND_BASE_URL}/auth/callback?access={access}&refresh={refresh}" return RedirectResponse(url=response_url) # --- STANDARD AUTH ENDPOINTS --- @router.post("/register-lite", response_model=Token, status_code=status.HTTP_201_CREATED) async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)): """Step 1: Manuális regisztráció (inaktív, nincs mappa).""" stmt = select(User).where(User.email == user_in.email) if (await db.execute(stmt)).scalar_one_or_none(): raise HTTPException(status_code=400, detail="Email már regisztrálva.") user = await AuthService.register_lite(db, user_in) 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) token_data = { "sub": str(user.id), "role": role_name, "rank": ranks.get(role_name, 10), "scope_level": "individual", "scope_id": str(user.id), "region": user.region_code } access, refresh = create_tokens(data=token_data) return { "access_token": access, "refresh_token": refresh, "token_type": "bearer", "is_active": user.is_active } @router.post("/login", response_model=Token) async def login(db: AsyncSession = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()): """Hagyományos belépés + Dupla Token.""" user = await AuthService.authenticate(db, form_data.username, form_data.password) if not user: raise HTTPException(status_code=401, detail="Hibás adatok.") 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) user_rank = ranks.get(role_name, 10) token_data = { "sub": str(user.id), "role": role_name, "rank": user_rank, "scope_level": user.scope_level or "individual", "scope_id": user.scope_id or str(user.id), "region": user.region_code } access, refresh = create_tokens(data=token_data) return { "access_token": access, "refresh_token": refresh, "token_type": "bearer", "is_active": user.is_active } @router.get("/verify-email") async def verify_email(token: str, db: AsyncSession = Depends(get_db)): if not await AuthService.verify_email(db, token): raise HTTPException(status_code=400, detail="Érvénytelen token.") return {"message": "Email megerősítve!"} @router.post("/complete-kyc") async def complete_kyc( kyc_in: UserKYCComplete, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Step 2: KYC Aktiválás. Itt használjuk a get_current_user-t (nem active), mert a user még inaktív. """ user = await AuthService.complete_kyc(db, current_user.id, kyc_in) if not user: raise HTTPException(status_code=404, detail="User nem található.") return {"status": "success", "message": "Fiók aktiválva."} @router.post("/forgot-password") async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)): result = await AuthService.initiate_password_reset(db, req.email) if result == "cooldown": raise HTTPException(status_code=429, detail="Túl sok kérés.") return {"message": "Visszaállító link kiküldve."} @router.post("/reset-password") async def reset_password(req: PasswordResetConfirm, db: AsyncSession = Depends(get_db)): if req.password != req.password_confirm: raise HTTPException(status_code=400, detail="Nem egyeznek a jelszavak.") if not await AuthService.reset_password(db, req.email, req.token, req.password): raise HTTPException(status_code=400, detail="Sikertelen frissítés.") return {"message": "Jelszó frissítve!"}