Backend finished
Co-authored-by: Álvaro <alvaro6gv@users.noreply.github.com>
This commit is contained in:
121
backend/app/routes/auth.py
Normal file
121
backend/app/routes/auth.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from app.db.database import get_db
|
||||
from app.models.user import UserModel
|
||||
from app.schemas.user import UserLogin, User2FA, Token2FA
|
||||
from app.core.security import verify_password, generate_jwt, verify_jwt
|
||||
from datetime import timedelta
|
||||
import hashlib
|
||||
import pyotp
|
||||
from app.core.config import settings
|
||||
import qrcode
|
||||
import io
|
||||
import base64
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
def get_digest_function(algorithm: str):
|
||||
algo = algorithm.upper()
|
||||
if algo == "SHA256":
|
||||
return hashlib.sha256
|
||||
elif algo == "SHA512":
|
||||
return hashlib.sha512
|
||||
else:
|
||||
return hashlib.sha1
|
||||
|
||||
@router.post("/login")
|
||||
def login(user: UserLogin, db: Session = Depends(get_db)):
|
||||
existing_user = db.query(UserModel).filter(UserModel.user_name == user.user_name).first()
|
||||
if not existing_user:
|
||||
raise HTTPException(status_code=404, detail="El usuario no existe")
|
||||
|
||||
if not verify_password(user.password, existing_user.password):
|
||||
raise HTTPException(status_code=401, detail="Credenciales inválidas")
|
||||
|
||||
pre_auth_token = generate_jwt(
|
||||
data={"user_id": existing_user.user_id, "scope": "pre_auth"},
|
||||
expires=timedelta(minutes=2)
|
||||
)
|
||||
|
||||
return {"pre_auth_token": pre_auth_token}
|
||||
|
||||
@router.post("/2fa")
|
||||
def two_factor_auth(user_2fa: User2FA, db: Session = Depends(get_db)):
|
||||
token = verify_jwt(user_2fa.pre_auth_token)
|
||||
user_id = token.get("user_id")
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=401, detail="Token inválido o expirado")
|
||||
|
||||
user = db.query(UserModel).filter(UserModel.user_id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="Usuario no encontrado")
|
||||
|
||||
digest_func = get_digest_function(settings.TOTP_ALGORITHM)
|
||||
totp = pyotp.TOTP(
|
||||
s=user.totp_secret,
|
||||
digits=settings.TOTP_DIGITS,
|
||||
digest=digest_func,
|
||||
interval=settings.TOTP_INTERVAL,
|
||||
issuer=settings.TOTP_ISSUER,
|
||||
name=f"{settings.TOTP_NAME_PREFIX}:{user.user_name}"
|
||||
)
|
||||
|
||||
if not totp.verify(user_2fa.totp_code):
|
||||
raise HTTPException(status_code=401, detail="Código 2FA inválido")
|
||||
|
||||
expires = timedelta(seconds=settings.EXPIRATION)
|
||||
token = generate_jwt(
|
||||
data={"user_id": user.user_id, "user_name": user.user_name, "scope": "2fa_authenticated"},
|
||||
expires=expires
|
||||
)
|
||||
|
||||
return {
|
||||
"access_token": token,
|
||||
"token_type": "bearer",
|
||||
"message": "Autenticación 2FA exitosa"
|
||||
}
|
||||
|
||||
@router.post("/enable-2fa")
|
||||
def enable_2fa(token2fa: Token2FA, db: Session = Depends(get_db)):
|
||||
token = verify_jwt(token2fa.token)
|
||||
user_id = token.get("user_id")
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=401, detail="Token inválido o expirado")
|
||||
|
||||
user = db.query(UserModel).filter(UserModel.user_id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="Usuario no encontrado")
|
||||
|
||||
secret = pyotp.random_base32()
|
||||
user.totp_secret = secret
|
||||
db.commit()
|
||||
|
||||
digest_func = get_digest_function(settings.TOTP_ALGORITHM)
|
||||
|
||||
totp = pyotp.TOTP(
|
||||
s=secret,
|
||||
digits=settings.TOTP_DIGITS,
|
||||
digest=digest_func,
|
||||
interval=settings.TOTP_INTERVAL,
|
||||
issuer=settings.TOTP_ISSUER,
|
||||
name=f"{settings.TOTP_NAME_PREFIX}:{user.user_name}"
|
||||
)
|
||||
|
||||
otpauth_url = totp.provisioning_uri(
|
||||
name=f"{settings.TOTP_NAME_PREFIX}:{user.user_name}",
|
||||
issuer_name=settings.TOTP_ISSUER
|
||||
)
|
||||
|
||||
qr = qrcode.make(otpauth_url)
|
||||
buf = io.BytesIO()
|
||||
qr.save(buf, format="PNG")
|
||||
img_base64 = base64.b64encode(buf.getvalue()).decode("utf-8")
|
||||
|
||||
return {
|
||||
"user_id": user.user_id,
|
||||
"user_name": user.user_name,
|
||||
"totp_secret": secret,
|
||||
"otpauth_url": otpauth_url,
|
||||
"qr_base64": img_base64,
|
||||
"message": "2FA habilitado correctamente. Escanea el QR o introduce el código en tu app de autenticación."
|
||||
}
|
||||
Reference in New Issue
Block a user