Spaces:
Running
Running
File size: 5,606 Bytes
79ef7e1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
import logging
from fastapi import APIRouter, Header, HTTPException, status, Depends
from app.schemas.auth import (
SignupDto,
VerifySignupOtpDto,
LoginDto,
SendPasswordResetOtpDto,
VerifyPasswordResetOtpDto,
ResetPasswordDto,
ResendOtpDto,
)
from app.services.auth_service import auth_service
from app.services.user_service import user_service
from app.services.otp_service import otp_service
from app.guards.jwt_guard import get_current_user
router = APIRouter(tags=["Authentication"])
logger = logging.getLogger(__name__)
# ============================================================
# SIGNUP ENDPOINTS
# ============================================================
@router.post("/signup", status_code=status.HTTP_200_OK)
async def signup(signup_dto: SignupDto):
"""
Step 1: Initiate Signup
Create account and send OTP to email or phone
"""
logger.info("Signup request")
return await auth_service.signup(signup_dto)
@router.post("/verify-signup-otp", status_code=status.HTTP_200_OK)
async def verify_signup_otp(dto: VerifySignupOtpDto):
"""
Step 2: Verify Signup OTP
Verify OTP and complete signup. Returns JWT token.
"""
logger.info(f"Verify signup OTP: {dto.identifier}")
return await auth_service.verify_signup_otp(dto.identifier, dto.code)
# ============================================================
# LOGIN ENDPOINT
# ============================================================
@router.post("/login", status_code=status.HTTP_200_OK)
async def login(login_dto: LoginDto):
"""
User Login
Authenticate with email or phone and password. Returns JWT token.
"""
logger.info(f"Login request: {login_dto.identifier}")
return await auth_service.login(login_dto)
# ============================================================
# PASSWORD RESET ENDPOINTS
# ============================================================
@router.post("/send-password-reset-otp", status_code=status.HTTP_200_OK)
async def send_password_reset_otp(dto: SendPasswordResetOtpDto):
"""
Step 1: Request Password Reset OTP
Send OTP to email or phone for password reset.
Does not reveal if account exists (security).
"""
logger.info(f"Send password reset OTP: {dto.identifier}")
return await auth_service.send_password_reset_otp(dto.identifier)
@router.post("/verify-password-reset-otp", status_code=status.HTTP_200_OK)
async def verify_password_reset_otp(dto: VerifyPasswordResetOtpDto):
"""
Step 2: Verify Password Reset OTP
Verify OTP and get temporary reset token (10 minute validity).
"""
logger.info(f"Verify password reset OTP: {dto.identifier}")
return await auth_service.verify_password_reset_otp(dto.identifier, dto.code)
@router.post("/reset-password", status_code=status.HTTP_200_OK)
async def reset_password(
reset_password_dto: ResetPasswordDto,
x_reset_token: str = Header(None),
):
"""
Step 3: Reset Password
Reset password using reset token from verify-password-reset-otp endpoint.
Pass token in x-reset-token header.
"""
if not x_reset_token:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Reset token is required in x-reset-token header"
)
logger.info(f"Reset password: {reset_password_dto.identifier}")
return await auth_service.reset_password(reset_password_dto, x_reset_token)
# ============================================================
# RESEND OTP ENDPOINT
# ============================================================
@router.post("/resend-otp", status_code=status.HTTP_200_OK)
async def resend_otp(dto: ResendOtpDto):
"""
Resend OTP for Signup or Password Reset
Specify purpose in request body ('signup' or 'password_reset').
"""
logger.info(f"Resend OTP for {dto.purpose}: {dto.identifier}")
is_expired = await otp_service.is_otp_expired(dto.identifier, dto.purpose)
if not is_expired:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="OTP still valid. Please wait before requesting again."
)
await otp_service.send_otp(dto.identifier, dto.purpose)
return {
"success": True,
"message": "New OTP sent to your email/phone",
}
# ============================================================
# USER PROFILE ENDPOINTS
# ============================================================
@router.get("/profile", status_code=status.HTTP_200_OK)
async def get_current_user_profile(current_user: dict = Depends(get_current_user)):
"""
Get Current User Profile
Fetch current logged-in user profile. Only you can view your own profile.
Requires: Bearer token in Authorization header
"""
logger.info(f"Get current user profile: {current_user.get('user_id')}")
return await user_service.get_current_user_profile(current_user.get("user_id"))
# ============================================================
# LOGOUT ENDPOINT
# ============================================================
@router.post("/logout", status_code=status.HTTP_200_OK)
async def logout(current_user: dict = Depends(get_current_user)):
"""
User Logout
Logout current user. Remove JWT token from frontend storage to complete logout.
Requires: Bearer token in Authorization header
"""
logger.info(f"User logged out: {current_user.get('user_id')}")
return {
"success": True,
"message": "Logout successful. Please remove your token from storage to complete logout.",
} |