app-MAIL-temp/app/api/views/auth_mfa.py

76 lines
2.1 KiB
Python
Raw Normal View History

2020-01-20 15:00:56 +01:00
import pyotp
from flask import jsonify, request
2020-07-04 10:39:38 +02:00
from flask_login import login_user
2020-04-25 11:30:09 +02:00
from itsdangerous import Signer
2020-01-20 15:00:56 +01:00
from app.api.base import api_bp
from app.config import FLASK_SECRET
from app.db import Session
from app.email_utils import send_invalid_totp_login_email
2022-03-21 14:23:35 +01:00
from app.extensions import limiter
2020-02-05 12:05:26 +01:00
from app.log import LOG
2020-01-20 15:00:56 +01:00
from app.models import User, ApiKey
@api_bp.route("/auth/mfa", methods=["POST"])
2022-03-21 14:23:35 +01:00
@limiter.limit("10/minute")
2020-01-20 15:00:56 +01:00
def auth_mfa():
"""
Validate the OTP Token
Input:
mfa_token: OTP token that user enters
mfa_key: MFA key obtained in previous auth request, e.g. /api/auth/login
device: the device name, used to create an ApiKey associated with this device
Output:
200 and user info containing:
{
name: "John Wick",
2020-06-09 17:20:37 +02:00
api_key: "a long string",
email: "user email"
2020-01-20 15:00:56 +01:00
}
"""
data = request.get_json()
if not data:
return jsonify(error="request body cannot be empty"), 400
mfa_token = data.get("mfa_token")
mfa_key = data.get("mfa_key")
device = data.get("device")
s = Signer(FLASK_SECRET)
try:
user_id = int(s.unsign(mfa_key))
except Exception:
2020-01-20 15:00:56 +01:00
return jsonify(error="Invalid mfa_key"), 400
user = User.get(user_id)
if not user:
return jsonify(error="Invalid mfa_key"), 400
elif not user.enable_otp:
return (
jsonify(error="This endpoint should only be used by user who enables MFA"),
400,
)
totp = pyotp.TOTP(user.otp_secret)
if not totp.verify(mfa_token, valid_window=2):
send_invalid_totp_login_email(user, "TOTP")
2020-01-20 15:00:56 +01:00
return jsonify(error="Wrong TOTP Token"), 400
ret = {"name": user.name or "", "email": user.email}
2020-01-20 15:00:56 +01:00
2020-02-05 12:05:26 +01:00
api_key = ApiKey.get_by(user_id=user.id, name=device)
if not api_key:
LOG.d("create new api key for %s and %s", user, device)
api_key = ApiKey.create(user.id, device)
Session.commit()
2020-02-05 12:05:26 +01:00
2020-01-20 15:00:56 +01:00
ret["api_key"] = api_key.code
2020-07-04 10:39:38 +02:00
# so user is logged in automatically on the web
login_user(user)
2020-01-20 15:00:56 +01:00
return jsonify(**ret), 200