def auth_payload(user, device) -> dict: ret = { "name": user.name or "", "email": user.email, "mfa_enabled": user.enable_otp } # do not give api_key, user can only obtain api_key after OTP verification if user.enable_otp: s = Signer(FLASK_SECRET) ret["mfa_key"] = s.sign(str(user.id)) ret["api_key"] = None else: 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) db.session.commit() ret["mfa_key"] = None ret["api_key"] = api_key.code # so user is automatically logged in on the web login_user(user) return ret
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", api_key: "a long string", email: "user email" } """ 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: 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): return jsonify(error="Wrong TOTP Token"), 400 ret = {"name": user.name or "", "email": user.email} 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) db.session.commit() ret["api_key"] = api_key.code # so user is logged in automatically on the web login_user(user) return jsonify(**ret), 200
def test_create_delete_api_key(flask_client): user = login(flask_client) Session.commit() # create api_key create_r = flask_client.post( url_for("dashboard.api_key"), data={ "form-name": "create", "name": "for test" }, follow_redirects=True, ) assert create_r.status_code == 200 api_key = ApiKey.get_by(user_id=user.id) assert ApiKey.count() == 1 assert api_key.name == "for test" # delete api_key delete_r = flask_client.post( url_for("dashboard.api_key"), data={ "form-name": "delete", "api-key-id": api_key.id }, follow_redirects=True, ) assert delete_r.status_code == 200 assert ApiKey.count() == 0
def auth_login(): """ Authenticate user Input: email password device: to create an ApiKey associated with this device Output: 200 and user info containing: { name: "John Wick", mfa_enabled: true, mfa_key: "a long string", api_key: "a long string" } """ data = request.get_json() if not data: return jsonify(error="request body cannot be empty"), 400 email = data.get("email") password = data.get("password") device = data.get("device") user = User.filter_by(email=email).first() if not user or not user.check_password(password): return jsonify(error="Email or password incorrect"), 400 elif not user.activated: return jsonify(error="Account not activated"), 400 ret = { "name": user.name, "mfa_enabled": user.enable_otp, } # do not give api_key, user can only obtain api_key after OTP verification if user.enable_otp: s = Signer(FLASK_SECRET) ret["mfa_key"] = s.sign(str(user.id)) ret["api_key"] = None else: 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) db.session.commit() ret["mfa_key"] = None ret["api_key"] = api_key.code return jsonify(**ret), 200
def decorated(*args, **kwargs): api_code = request.headers.get("Authentication") api_key = ApiKey.get_by(code=api_code) if not api_key: return jsonify(error="Wrong api key"), 401 # Update api key stats api_key.last_used = arrow.now() api_key.times += 1 db.session.commit() g.user = api_key.user return f(*args, **kwargs)
def decorated(*args, **kwargs): api_code = request.headers.get("Authentication") api_key = ApiKey.get_by(code=api_code) if not api_key: # if user is authenticated, the request is authorized if current_user.is_authenticated: g.user = current_user else: return jsonify(error="Wrong api key"), 401 else: # Update api key stats api_key.last_used = arrow.now() api_key.times += 1 Session.commit() g.user = api_key.user return f(*args, **kwargs)