def webauthn_begin_login(): username = request.form.get('login_username') password = request.form.get('login_password') if not util.validate_username(username): return make_response(jsonify({'fail': 'Invalid username.'}), 401) _, person = auth.getPerson(username) if not person: return make_response(jsonify({'fail': 'User does not exist.'}), 401) if not person.credential_id: return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401) session.pop('challenge', None) session.pop('login_password', None) challenge = util.generate_challenge(32) # We strip the padding from the challenge stored in the session # for the reasons outlined in the comment in webauthn_begin_activate. session['challenge'] = challenge.rstrip('=') session['login_password'] = password webauthn_user = webauthn.WebAuthnUser( person.ukey, person.username, person.display_name, person.icon_url, person.credential_id, person.pub_key, person.sign_count, person.rp_id) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) return jsonify(webauthn_assertion_options.assertion_dict)
def webauthn_begin_assertion(election): email = request.form.get('email') if not len(email): return make_response(jsonify({'fail': 'Invalid email.'}), 401) user = Authority.query.filter(Election.name == election, Authority.email == email).first() if not user: return make_response(jsonify({'fail': 'User does not exist.'}), 401) if not user.webauthn: session['email'] = email return jsonify({'alt-login': '******'}) if not user.credential_id: return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401) if 'challenge' in session: del session['challenge'] challenge = generate_challenge(32) session['challenge'] = challenge webauthn_user = webauthn.WebAuthnUser(user.ukey, user.email, user.name, user.icon_url, user.credential_id, user.pub_key, user.sign_count, user.rp_id) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) return jsonify(webauthn_assertion_options.assertion_dict)
def webauthn_begin_assertion(request): challenge = util.generate_challenge(32) request.session["challenge"] = challenge user = util.get_user(request) username = user.get_username() display_name = user.get_full_name() keys = WebAuthnKey.objects.filter(user=user) webauthn_users = [] for key in keys: webauthn_users.append( webauthn.WebAuthnUser( key.ukey, username, display_name, settings.WEBAUTHN_ICON_URL, key.credential_id, key.public_key, key.sign_count, settings.RELYING_PARTY_ID, )) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_users, challenge) return JsonResponse(webauthn_assertion_options.assertion_dict)
def webauthn_begin_assertion(): jsonData = request.get_json() username = jsonData['username'] if not util.validate_username(username): return make_response(jsonify({'fail': 'Invalid username.'}), 401) user = database.query_db("select * from Users where username=?",[username])[0] if len(user) == 0: return make_response(jsonify({'fail': 'User does not exist.'}), 401) if not user[4]: return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401) challenge = util.generate_challenge(32) # We strip the padding from the challenge stored in the session # for the reasons outlined in the comment in webauthn_begin_activate. toStoreChallenge = challenge.rstrip('=') try: insert = database.insert_db("insert into PublicKeyCredentialCreationOptions VALUES (?,?,?,?,?,?)",[None, None,user[0],None,username,toStoreChallenge]) except: update = database.insert_db("update PublicKeyCredentialCreationOptions SET challenge=? where user_username=?",[toStoreChallenge,username]) webauthn_user = webauthn.WebAuthnUser( user[0], user[1], user[2], user[6], user[4], user[3], user[5], user[7]) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) return jsonify(webauthn_assertion_options.assertion_dict)
def webauthn_begin_assertion(): username = request.form.get('login_username') if not util.validate_username(username): return make_response(jsonify({'fail': 'Invalid username.'}), 401) user = User.query.filter_by(username=username).first() if not user: return make_response(jsonify({'fail': 'User does not exist.'}), 401) if not user.credential_id: return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401) if 'challenge' in session: del session['challenge'] challenge = util.generate_challenge(32) session['challenge'] = challenge webauthn_user = webauthn.WebAuthnUser(user.ukey, user.username, user.display_name, user.icon_url, user.credential_id, user.pub_key, user.sign_count, user.rp_id) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) return jsonify(webauthn_assertion_options.assertion_dict)
def login_challenge(): data = LoginChallengeSchema().load(request.get_json()) email = data["email"] fake_user = User.generate_fake(email=email) user = User.query.filter_by(email=email).first() or fake_user challenge = random_string() webauthn_users = [ webauthn.WebAuthnUser( user_id=authenticator.user.id, username=authenticator.user.email, display_name=authenticator.user.name, icon_url=app.config["WEBAUTHN_ICON_URL"], credential_id=authenticator.credential_id, public_key=authenticator.public_key, sign_count=authenticator.sign_count, rp_id=app.config["WEBAUTHN_RP_ID"], ) for authenticator in user.authenticators ] options = webauthn.WebAuthnAssertionOptions( webauthn_users, challenge ).assertion_dict token = guard.encode_jwt_token( user, is_login=True, email=email, challenge=challenge ) return { "token": token, "options": options, }
def get_assertion_options(user, *, challenge, icon_url, rp_id): """ Returns a dictionary of options for assertion retrieval on the client side. """ options = pywebauthn.WebAuthnAssertionOptions( _get_webauthn_users(user, icon_url=icon_url, rp_id=rp_id), challenge) return options.assertion_dict
def get(self, request, *args, **kwargs): challenge = os.urandom(32) request.session['webauthn_assert'] = webauthn_encode(challenge) data = webauthn.WebAuthnAssertionOptions( [ credential.webauthn_user for credential in request.profile. webauthn_credentials.select_related('user__user') ], challenge, ).assertion_dict return JsonResponse(data, encoder=WebAuthnJSONEncoder)
def login_start(): username = request.json.get('username') if username not in USERS: return jsonify(status='failure', error='User with given username not found') challenge = secrets.token_urlsafe(32) session['challenge'] = challenge user = USERS[username] user_id, username, credential_id, public_key = user['id'], user['username'], user['credential_id'], user['public_key'] webauthn_user = webauthn.WebAuthnUser(user_id=user_id, username=username, display_name=username, icon_url=ICON_URL, credential_id=credential_id, public_key=public_key, sign_count=0, rp_id=RELYING_PARTY_ID) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(webauthn_user, challenge) return jsonify(status='success', options=webauthn_assertion_options.assertion_dict)
def challenge(self, request, otp_device): challenge = generate_challenge() request.session['challenge'] = challenge ukey = base64.urlsafe_b64encode(str(request.user.id).encode('utf-8')) webauthn_user = webauthn.WebAuthnUser( ukey, otp_device.user.username, otp_device.user.username, otp_device.data['icon_url'], otp_device.data['credential_id'], otp_device.data['pub_key'], otp_device.counter, otp_device.data['rp_id']) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) return webauthn_assertion_options.assertion_dict
def webauthn_begin_transfer(): amount = request.form.get('transfer_amount') recipient = request.form.get('transfer_recipient') if not util.validate_transfer_amount(amount): return make_response(jsonify({'fail': 'Invalid transfer amount.'}), 401) # Make the amount into an int type. Type safety assured by `util.validate_transfer_amount` amount = int(amount) # Extract the logged in `g.user` person = g.user.person if not person: return make_response(jsonify({'fail': 'User does not exist.'}), 401) if not person.credential_id: return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401) session.pop('challenge', None) session.pop('transfer_amount', None) session.pop('transfer_recipient', None) session.pop('clientExtensions', None) challenge = util.generate_challenge(32) # We strip the padding from the challenge stored in the session # for the reasons outlined in the comment in webauthn_begin_activate. session['challenge'] = challenge.rstrip('=') session['transfer_amount'] = amount session['transfer_recipient'] = recipient session['clientExtensions'] = { 'txAuthSimple': "Authorize sending {} coins from {} to {}!".format( amount, person.username, recipient) } webauthn_user = webauthn.WebAuthnUser(person.ukey, person.username, person.display_name, person.icon_url, person.credential_id, person.pub_key, person.sign_count, person.rp_id) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge, clientExtensions=session['clientExtensions']) return jsonify(webauthn_assertion_options.assertion_dict)
def webauthn_begin_assertion(request): print("webauthn_begin_assertion") username = request.POST.get('login_username') challenge = generate_challenge(32) user = User.objects.get(username=username) if 'challenge' in request.session: del request.session['challenge'] challenge = generate_challenge(32) print("assertion get challenge") request.session['challenge'] = challenge webauthn_user = webauthn.WebAuthnUser( user.ukey, user.username, user.display_name, user.icon_url, user.credential_id, user.pub_key, user.sign_count, user.rp_id) print("assertion get user") webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) print("go return") return JsonResponse(webauthn_assertion_options.assertion_dict)
def get_context_data(self, **kwargs): ctx = super().get_context_data() if 'webauthn_challenge' in self.request.session: del self.request.session['webauthn_challenge'] challenge = generate_challenge(32) self.request.session['webauthn_challenge'] = challenge devices = [ device.webauthnuser for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user) ] + [ device.webauthnuser for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user) ] if devices: webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( devices, challenge) ad = webauthn_assertion_options.assertion_dict ad['extensions'] = {'appid': get_u2f_appid(self.request)} ctx['jsondata'] = json.dumps(ad) return ctx
def login_start(): username, password = request.json.get('username'), request.json.get('password') if username not in ADMIN_USERS: logger.info(f"Login start failed: no user with username {username}") return jsonify(status='failure', error='User with given username not found') user = ADMIN_USERS[username] if not isinstance(password, str) or not check_password_hash(user.password_hash, password): logger.info(f"Login start failed: incorrect password") return jsonify(status='failure', error='Incorrect password') logger.info(f"Login started for user {username} with password hash {user.password_hash}: password correct") if DISABLE_ADMIN_WEBAUTHN: logger.info(f"Login completed for user {username} (DISABLE_ADMIN_WEBAUTHN is enabled)") login_user(user, remember=False) return jsonify(status='success', options={}) challenge = secrets.token_urlsafe(32) session['challenge'] = challenge webauthn_user = webauthn.WebAuthnUser(user_id=user.id, username=user.username, display_name=user.username, icon_url=ICON_URL, credential_id=user.credential_id, public_key=user.public_key, sign_count=0, rp_id=RELYING_PARTY_ID) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(webauthn_user, challenge) logger.info(f"Login challenge issued for user {username} with challenge {challenge}") return jsonify(status='success', options=webauthn_assertion_options.assertion_dict)
def login_webauthn_user_begin_assertion(request): current_site_details = get_current_site(request) username = request.POST["username"] webauthn_user = WebAuthnProfile.objects.filter( user__username=username).first() if webauthn_user is None: return JsonResponse({"error": "User doesn't exist"}) assertion_challenge = token_urlsafe_without_stripping(32) request.session["challenge"] = assertion_challenge.rstrip('=') generated_webauthn_user = webauthn.WebAuthnUser( user_id=webauthn_user.webauthn_ukey, username=webauthn_user.user.username, display_name=webauthn_user.display_name, credential_id=webauthn_user.credential_id, icon_url= "https://rajeevkr.me/assets/img/portfolio/IMG_20180302_232843.jpg", public_key=webauthn_user.user_public_key, sign_count=webauthn_user.signature_counter, rp_id=current_site_details.domain.split(":")[0]) generated_webauthn_user_assertion = webauthn.WebAuthnAssertionOptions( generated_webauthn_user, assertion_challenge) return JsonResponse(generated_webauthn_user_assertion.assertion_dict)
def auth_request(): username = request.form.get('login_username') user = User.query.filter_by(username=username).first() if 'challenge' in session: del session['challenge'] challenge = util.generate_challenge(32) session['challenge'] = challenge webauthn_user = webauthn.WebAuthnUser(user.ukey, user.username, user.display_name, user.icon_url, user.credential_id, user.pub_key, user.sign_count, user.rp_id) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) print(webauthn_assertion_options) print(webauthn_assertion_options.assertion_dict) return json.jsonify(webauthn_assertion_options.assertion_dict)
def webauthn_login_start(): '''Start the biometric login process by generating a random challenge for the user''' email = request.form['email'] if not validate_email(email): flash('Please enter a valid email', 'error') return make_response(jsonify({'redirect': url_for('login')}), 401) # Attempt to find user in database user = User.query.filter_by(email=email).first() if not user: flash('User does not exist', 'error') return make_response(jsonify({'redirect': url_for('login')}), 401) if not user.credential_id: flash('User does not have biometric to sign in with', 'error') return make_response(jsonify({'redirect': url_for('login')}), 401) # We strip the padding from the challenge stored in the session # for the reasons outlined in the comment in webauthn_begin_activate. challenge = secrets.token_urlsafe(32) session['login'] = {'challenge': challenge.rstrip('=')} webauthn_user = webauthn.WebAuthnUser( user_id=user.ukey, username=user.email, display_name=user.display_name, icon_url=user.icon_url, credential_id=user.credential_id, public_key=user.public_key, sign_count=user.sign_count, rp_id=user.rp_id, ) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge ) return jsonify(webauthn_assertion_options.assertion_dict)
def fido(): # passed from login page user_id = session.get(MFA_USER_ID) # user access this page directly without passing by login page if not user_id: flash("Unknown error, redirect back to main page", "warning") return redirect(url_for("auth.login")) user = User.get(user_id) if not (user and user.fido_enabled()): flash("Only user with security key linked should go to this page", "warning") return redirect(url_for("auth.login")) auto_activate = True fido_token_form = FidoTokenForm() next_url = request.args.get("next") if request.cookies.get("mfa"): browser = MfaBrowser.get_by(token=request.cookies.get("mfa")) if browser and not browser.is_expired() and browser.user_id == user.id: login_user(user) flash(f"Welcome back {user.name}!", "success") # Redirect user to correct page return redirect(next_url or url_for("dashboard.index")) else: # Trigger rate limiter g.deduct_limit = True # Handling POST requests if fido_token_form.validate_on_submit(): try: sk_assertion = json.loads(fido_token_form.sk_assertion.data) except Exception as e: flash("Key verification failed. Error: Invalid Payload", "warning") return redirect(url_for("auth.login")) challenge = session["fido_challenge"] try: fido_key = Fido.get_by(uuid=user.fido_uuid, credential_id=sk_assertion["id"]) webauthn_user = webauthn.WebAuthnUser( user.fido_uuid, user.email, user.name if user.name else user.email, False, fido_key.credential_id, fido_key.public_key, fido_key.sign_count, RP_ID, ) webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, sk_assertion, challenge, URL, uv_required=False) new_sign_count = webauthn_assertion_response.verify() except Exception as e: LOG.exception( f"An error occurred in WebAuthn verification process: {e}") flash("Key verification failed.", "warning") # Trigger rate limiter g.deduct_limit = True auto_activate = False else: user.fido_sign_count = new_sign_count db.session.commit() del session[MFA_USER_ID] login_user(user) flash(f"Welcome back {user.name}!", "success") # Redirect user to correct page response = make_response( redirect(next_url or url_for("dashboard.index"))) if fido_token_form.remember.data: browser = MfaBrowser.create_new(user=user) db.session.commit() response.set_cookie( "mfa", value=browser.token, expires=browser.expires.datetime, secure=True if URL.startswith("https") else False, httponly=True, samesite="Lax", ) return response # Prepare information for key registration process session.pop("challenge", None) challenge = secrets.token_urlsafe(32) session["fido_challenge"] = challenge.rstrip("=") fidos = Fido.filter_by(uuid=user.fido_uuid).all() webauthn_users = [] for fido in fidos: webauthn_users.append( webauthn.WebAuthnUser( user.fido_uuid, user.email, user.name if user.name else user.email, False, fido.credential_id, fido.public_key, fido.sign_count, RP_ID, )) webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_users, challenge) webauthn_assertion_options = webauthn_assertion_options.assertion_dict return render_template( "auth/fido.html", fido_token_form=fido_token_form, webauthn_assertion_options=webauthn_assertion_options, enable_otp=user.enable_otp, auto_activate=auto_activate, next_url=next_url, )
def fido(): # passed from login page user_id = session.get(MFA_USER_ID) # user access this page directly without passing by login page if not user_id: flash("Unknown error, redirect back to main page", "warning") return redirect(url_for("auth.login")) user = User.get(user_id) if not (user and user.fido_enabled()): flash("Only user with security key linked should go to this page", "warning") return redirect(url_for("auth.login")) auto_activate = True fido_token_form = FidoTokenForm() next_url = request.args.get("next") webauthn_user = webauthn.WebAuthnUser( user.fido_uuid, user.email, user.name if user.name else user.email, False, user.fido_credential_id, user.fido_pk, user.fido_sign_count, RP_ID, ) # Handling POST requests if fido_token_form.validate_on_submit(): try: sk_assertion = json.loads(fido_token_form.sk_assertion.data) except Exception as e: flash("Key verification failed. Error: Invalid Payload", "warning") return redirect(url_for("auth.login")) challenge = session["fido_challenge"] webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, sk_assertion, challenge, URL, uv_required=False) try: new_sign_count = webauthn_assertion_response.verify() except Exception as e: LOG.error( f"An error occurred in WebAuthn verification process: {e}") flash("Key verification failed.", "warning") auto_activate = False else: user.fido_sign_count = new_sign_count db.session.commit() del session[MFA_USER_ID] login_user(user) flash(f"Welcome back {user.name}!", "success") # User comes to login page from another page if next_url: LOG.debug("redirect user to %s", next_url) return redirect(next_url) else: LOG.debug("redirect user to dashboard") return redirect(url_for("dashboard.index")) # Prepare information for key registration process session.pop("challenge", None) challenge = secrets.token_urlsafe(32) session["fido_challenge"] = challenge.rstrip("=") webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge) webauthn_assertion_options = webauthn_assertion_options.assertion_dict return render_template( "auth/fido.html", fido_token_form=fido_token_form, webauthn_assertion_options=webauthn_assertion_options, enable_otp=user.enable_otp, auto_activate=auto_activate, )