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 login(): challenge = session.get('challenge') credential_id = request.json.get('id') user_handle = request.json.get('userHandle') client_data = request.json.get('clientData') auth_data = request.json.get('authData') signature = request.json.get('signature') assertion_client_extensions = request.json.get('assertionClientExtensions') user = next((u for u in USERS.values() if u['credential_id'] == credential_id), None) if user is None: return jsonify(status='failure', error='User with given credential ID not found') 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user=webauthn_user, assertion_response={'id': credential_id, 'userHandle': user_handle, 'clientData': client_data, 'authData': auth_data, 'signature': signature, 'assertionClientExtensions': assertion_client_extensions}, challenge=challenge, origin=ORIGIN, ) try: webauthn_assertion_response.verify() except Exception as e: return jsonify(status='failure', error=str(e)) return jsonify(status='success', user=user)
def login(): challenge = session.get('challenge') credential_id = request.json.get('id') user_handle = request.json.get('userHandle') client_data = request.json.get('clientData') auth_data = request.json.get('authData') signature = request.json.get('signature') assertion_client_extensions = request.json.get('assertionClientExtensions') user = next((u for u in ADMIN_USERS.values() if u.credential_id == credential_id), None) if user is None: logger.info(f"Login start failed: no user with credential ID {credential_id}") return jsonify(status='failure', error='User with given credential_id not found') logger.info(f"Login submitted for user {user.username} with 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user=webauthn_user, assertion_response={'id': user.credential_id, 'userHandle': user_handle, 'clientData': client_data, 'authData': auth_data, 'signature': signature, 'assertionClientExtensions': assertion_client_extensions}, challenge=challenge, origin=ORIGIN, ) try: webauthn_assertion_response.verify() except Exception as e: logger.info(f"Login failed: {e}") return jsonify(status='failure', error=str(e)) logger.info(f"Login completed for user {user.username}") login_user(user, remember=False) return jsonify(status='success')
def verify_assertion(): challenge = session.get('challenge') assertion_response = request.form credential_id = assertion_response.get('id') user = User.query.filter_by(credential_id=credential_id).first() if not user: return make_response(jsonify({'fail': 'User does not exist.'}), 401) 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, ORIGIN, uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}) # Update counter. user.sign_count = sign_count db.session.add(user) db.session.commit() login_user(user) return jsonify( {'success': 'Successfully authenticated as {}'.format(user.username)})
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 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 verify_assertion(request): print("verify_assertion") challenge = request.session.get('challenge', False) assertion_response = request.POST credential_id = assertion_response.get('id') user = User.objects.filter(credential_id=credential_id).first() if not user: return JsonResponse({'fail': 'User does not exist.'}) 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, ORIGIN, uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: return JsonResponse({'fail': 'Assertion failed. Error: {}'.format(e)}) # Update counter. user.sign_count = sign_count user.save() login(request, user) return JsonResponse({ 'success': 'Successfully authenticated as {}'.format(user.username) })
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 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(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 login(): claims = current_custom_claims() if not claims.get("is_login", False): return "", 401 data = LoginSchema().load(request.get_json()) email = claims["email"] credential_id = data["id"] authenticator = Authenticator.query.filter( Authenticator.credential_id == credential_id, Authenticator.user.has(email=email), ).first() if not authenticator: return {"error": "authenticator not found"}, 401 webauthn_user = 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"], ) response = webauthn.WebAuthnAssertionResponse( webauthn_user=webauthn_user, challenge=claims["challenge"], assertion_response=data, origin=app.config["WEBAUTHN_ORIGIN"], ) try: sign_count = response.verify() except Exception as exc: return {"error": str(exc)}, 401 authenticator.sign_count = sign_count authenticator.last_used_at = datetime.now(timezone.utc) db.session.add(authenticator) db.session.commit() return ( { "token": guard.encode_jwt_token(authenticator.user), "user": UserSchema().dump(authenticator.user), "authenticator": AuthenticatorSchema().dump(authenticator), }, 200, )
def webauthnuser(self): return webauthn.WebAuthnUser( self.ukey, self.user.email, str(self.user), settings.SITE_URL, self.credential_id, self.pub_key, self.sign_count, urlparse(settings.SITE_URL).netloc )
def verify_assertion(): jsonData = request.get_json() AuthenticatorAttestationResponse = jsonData['AuthenticatorAttestationResponse'] clientDataJSON = AuthenticatorAttestationResponse['clientDataJSON'] clientDataJSON_padding = clientDataJSON.ljust((int)(math.ceil(len(clientDataJSON) / 4)) * 4, '=') clientDataJSON = base64.b64decode(clientDataJSON_padding).decode('utf8') clientDataJSONparsed = json.loads(clientDataJSON) retrievedChallenge = clientDataJSONparsed['challenge'] try: data = database.query_db("select * from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge])[0] except: return jsonify({"Error:","Could not find challenge"}),500 #DELETE from table database.delete_db("delete from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge]) signature = AuthenticatorAttestationResponse['signature'] assertion_response = {'clientData':AuthenticatorAttestationResponse['clientDataJSON'],'authData':AuthenticatorAttestationResponse['authenticatorData'],'signature':signature,'userHandle':AuthenticatorAttestationResponse['userHandle']} credential_id = AuthenticatorAttestationResponse.get('id') user = database.query_db("select * from Users where credential_id=?",[credential_id])[0] if len(user)==0: return make_response(jsonify({'fail': 'User does not exist.'}), 401) webauthn_user = webauthn.WebAuthnUser( user[0], user[0], user[2], user[6], user[4], user[3], user[5], user[7]) webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, retrievedChallenge, ORIGIN, uv_required=False) # User Verification sign_count = webauthn_assertion_response.verify() try: sign_count = webauthn_assertion_response.verify() except Exception as e: print(e) return make_response(jsonify({'fail': 'Assertion failed'}),500) # Update counter. #Update database update = database.insert_db("update Users SET sign_count=? where username=?",[sign_count,user[1]]) identityObj={'username':user[1],'id':user[0],'displayname':user[2]} expires = datetime.timedelta(hours=2) print(identityObj) jwt = create_access_token(identity=identityObj,expires_delta=expires) return jsonify({ 'success': 'Successfully authenticated as {}'.format(user[1]), 'jwt':jwt, 'username': user[1] })
def webauthn_finish_login(): user = User() cookie = None challenge = session.get('challenge') password = session.get('login_password') assertion_response = request.form credential_id = assertion_response.get('id') db, person = auth.getPersonByCredentialID(credential_id) if not person: return make_response(jsonify({'fail': 'User does not exist.'}), 401) 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, ORIGIN, uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: return make_response(jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}), 401) # Update counter. person.sign_count = sign_count db.commit() # TODO: Is this check not performing anything useful? if not person.username: return make_response(jsonify({'fail': 'You must supply a username to log in.'}), 401) elif not password: return make_response(jsonify({'fail': 'You must supply a password to log in.'}), 401) else: cookie = user.checkLogin(person.username, password) if not cookie: return make_response(jsonify({'fail': 'Invalid username or password.'}), 401) nexturl = request.values.get('nexturl', https_url_for('index')) response = make_response(jsonify({'nexturl': nexturl}), 200) ## Be careful not to include semicolons in cookie value; see ## https://github.com/mitsuhiko/werkzeug/issues/226 for more ## details. response.set_cookie('PyZoobarLogin', cookie) return response
def webauthn_user(self): from judge.jinja2.gravatar import gravatar return webauthn.WebAuthnUser( user_id=self.user.webauthn_id, username=self.user.username, display_name=self.user.username, icon_url=gravatar(self.user.user.email), credential_id=webauthn_decode(self.cred_id), public_key=self.public_key, sign_count=self.counter, rp_id=settings.WEBAUTHN_RP_ID, )
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 get_assertion_response(self): credential = self.test_validate_registration() webauthn_user = webauthn.WebAuthnUser( USER_ID, USER_NAME, USER_DISPLAY_NAME, ICON_URL, credential.credential_id.decode(), credential.public_key, credential.sign_count, credential.rp_id) webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, copy(ASSERTION_RESPONSE_TMPL), ASSERTION_CHALLENGE, ORIGIN, uv_required=False) return webauthn_assertion_response
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_verify_login(): '''Verify the user's credential attesttion during the login process''' challenge = session['login']['challenge'] assertion_response = request.form credential_id = assertion_response.get('id') # Ensure a matching user exists user = User.query.filter_by(credential_id=credential_id).first() if not user: flash('User does not exist') return make_response(jsonify({'redirect': url_for('login')}), 401) # TODO: determine if this info should be stored at the session level 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user=webauthn_user, assertion_response=assertion_response, challenge=challenge, origin=ORIGIN, uv_required=False, ) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: flash(f'Assertion failed. Error: {e}') return make_response(jsonify({'redirect': url_for('login')}), 401) # Update counter. user.sign_count = sign_count db.session.add(user) db.session.commit() # Clear login session info session.pop('login', None) login_user(user) return jsonify({'redirect': url_for('profile')})
def webauthnuser(self): d = json.loads(self.json_data) # We manually need to convert the pubkey from DER format (used in our # former U2F implementation) to the format required by webauthn. This # is based on the following example: # https://www.w3.org/TR/webauthn/#sctn-encoded-credPubKey-examples pub_key = pub_key_from_der( websafe_decode(d['publicKey'].replace('+', '-').replace('/', '_'))) pub_key = binascii.unhexlify( 'A5010203262001215820{:064x}225820{:064x}'.format( pub_key.public_numbers().x, pub_key.public_numbers().y)) return webauthn.WebAuthnUser(d['keyHandle'], self.user.email, str(self.user), settings.SITE_URL, d['keyHandle'], websafe_encode(pub_key), 1, urlparse(settings.SITE_URL).netloc)
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 _get_webauthn_users(user, *, icon_url, rp_id): """ Returns a webauthn.WebAuthnUser instance corresponding to the given user model, with properties suitable for usage within the webauthn API. """ return [ pywebauthn.WebAuthnUser( str(user.id), user.username, user.name, icon_url, credential.credential_id, credential.public_key, credential.sign_count, rp_id, ) for credential in user.webauthn ]
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 verify_assertion(election): challenge = session.get('challenge') assertion_response = request.form credential_id = assertion_response.get('id') user = Authority.query.filter( Election.name == election, Authority.credential_id == credential_id).first() if not user: return make_response(jsonify({'fail': 'User does not exist.'}), 401) 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) http = request.url.split("://") origin = f"{http[0]}://{election}.{ORIGIN}" webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, origin, uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}) # Update counter user.sign_count = sign_count db.session.add(user) db.session.commit() login_user(user) return jsonify( {'success': 'Successfully authenticated as {}'.format(user.email)})
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 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 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 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 verify(self, request, otp_device, data): challenge = request.session.get('challenge', '').rstrip('=') 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_response = webauthn.WebAuthnAssertionResponse( webauthn_user, data, challenge, self._get_origin(request), uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except AuthenticationRejectedException as e: raise ValidationError(str(e)) otp_device.counter = sign_count otp_device.save() return {}
def verify_assertion(): challenge = session['challenge'] assertion_response = request.form credential_id = assertion_response.get('id') publickey_redential = PublicKeyCredential.query.filter_by( credential_id=credential_id).first() if not publickey_redential: return make_response( jsonify({ "status": "failed", "msg": "No pubkey found" }), 401) user = publickey_redential.user name = publickey_redential.email webauthn_user = webauthn.WebAuthnUser(publickey_redential.ukey, name, user.display_name, "", credential_id, publickey_redential.pub_key, 0, publickey_redential.rp_id) webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, request.host_url[:-1], uv_required=False) try: webauthn_assertion_response.verify() except Exception as e: return make_response( jsonify({ 'status': 'failed', 'msg': '{}'.format(e) }), 401) session['register_username'] = name session['register_display_name'] = user.display_name session['register_ukey'] = publickey_redential.ukey login_user(user) return jsonify({"status": "success"})