def verify_registration_response(response, challenge, *, rp_id, origin): """ Validates the challenge and attestation information sent from the client during device registration. Returns a WebAuthnCredential on success. Raises RegistrationRejectedError on failire. """ # NOTE: We re-encode the challenge below, because our # response's clientData.challenge is encoded twice: # first for the entire clientData payload, and then again # for the individual challenge. encoded_challenge = _webauthn_b64encode(challenge.encode()).decode() response = pywebauthn.WebAuthnRegistrationResponse( rp_id, origin, response, encoded_challenge, self_attestation_permitted=True, none_attestation_permitted=True, ) try: return response.verify() except _RegistrationRejectedError as e: raise RegistrationRejectedError(str(e))
def webauthn_verify_credential_info(request): challenge = request.session["challenge"] ukey = request.session["register_ukey"] registration_response = request.POST trust_anchor_dir = settings.WEBAUTHN_TRUSTED_CERTIFICATES trusted_attestation_cert_required = ( settings.WEBAUTHN_TRUSTED_ATTESTATION_CERT_REQUIRED) self_attestation_permitted = settings.WEBAUTHN_SELF_ATTESTATION_PERMITTED none_attestation_permitted = settings.WEBAUTHN_NONE_ATTESTATION_PERMITTED webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( settings.RELYING_PARTY_ID, util.get_origin(request), registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False, # User validation ) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return JsonResponse( {"fail": "Registration failed. Error: {}".format(e)}, status=400) # W3C spec. Step 17. # # Check that the credentialId is not yet registered to any other user. # If registration is requested for a credential that is already registered # to a different user, the Relying Party SHOULD fail this registration # ceremony, or it MAY decide to accept the registration, e.g. while deleting # the older registration. credential_id_exists = WebAuthnKey.objects.filter( credential_id=webauthn_credential.credential_id.decode( "utf-8")).first() if credential_id_exists: return JsonResponse({"fail": "Credential ID already exists."}, status=400) WebAuthnKey.objects.create( user=request.user, key_name=request.session.get("key_name", ""), ukey=ukey, public_key=webauthn_credential.public_key.decode("utf-8"), credential_id=webauthn_credential.credential_id.decode("utf-8"), sign_count=webauthn_credential.sign_count, ) try: del request.session["challenge"] del request.session["register_ukey"] del request.session["key_name"] except KeyError: # pragma: no cover pass return JsonResponse({"success": "User successfully registered."})
def register(): challenge = session['challenge'] user_id = session['user_id'] username = session['username'] client_data, att_obj, registration_client_extensions = request.json.get('clientData'), request.json.get('attObj'), request.json.get('registrationClientExtensions') webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( rp_id=RELYING_PARTY_ID, origin=ORIGIN, registration_response={'clientData': client_data, 'attObj': att_obj, 'registrationClientExtensions': registration_client_extensions}, challenge=challenge, trusted_attestation_cert_required=True, ) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return jsonify(status='failure', error=str(e)) if any(u['credential_id'] == webauthn_credential.credential_id for u in USERS.values()): return jsonify(status='failure', error='Credential ID already in use') if username in USERS: return jsonify(status='failure', error='Username already in use') user = { 'id': user_id, 'username': username, 'credential_id': webauthn_credential.credential_id.decode('utf8'), 'public_key': webauthn_credential.public_key.decode('utf8'), } USERS[username] = user return jsonify(status='success', user=user)
def verify_credential_info(request): print("verify_credential_info") # user = authenticate(request, username=username) # global username challenge = request.session['challenge'] username = request.session['register_username'] display_name = request.session['register_display_name'] ukey = request.session['register_ukey'] # user = User.objects.get(username=username) # print("user {}".format(user)) print("challenge {}".format(challenge)) print("username {}".format(username)) print("display_name {}".format(display_name)) print("ukey {}".format(ukey)) registration_response = request.POST trust_anchor_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR) trusted_attestation_cert_required = True self_attestation_permitted = True none_attestation_permitted = True webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) print(webauthn_registration_response) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return JsonResponse({'fail': 'Registration failed. Error: {}'.format(e)}) credential_id_exists = User.objects.filter( credential_id=webauthn_credential.credential_id).first() if credential_id_exists: return JsonResponse({'fail': 'Credential ID already exists.'}) existing_user = User.objects.filter(username=username).first() if not existing_user: print("create") if sys.version_info >= (3, 0): webauthn_credential.credential_id = str( webauthn_credential.credential_id, "utf-8") user = User.objects.create( ukey=ukey, username=username, display_name=username, pub_key=webauthn_credential.public_key, credential_id=webauthn_credential.credential_id, sign_count=webauthn_credential.sign_count, rp_id=RP_ID, icon_url='https://example.com') else: return JsonResponse({'fail': 'User already exists.'}) print('Successfully registered as {}.'.format(username)) return JsonResponse({'success': 'User successfully registered.'})
def register(): challenge = session['challenge'] user_id = session['user_id'] username = session['username'] password_hash = session['password_hash'] logger.info(f"Register submitted for user {username} with challenge {challenge}") client_data, att_obj, registration_client_extensions = request.json.get('clientData'), request.json.get('attObj'), request.json.get('registrationClientExtensions') webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( rp_id=RELYING_PARTY_ID, origin=ORIGIN, registration_response={'clientData': client_data, 'attObj': att_obj, 'registrationClientExtensions': registration_client_extensions}, challenge=challenge, trusted_attestation_cert_required=True, ) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: logger.info(f"Register failed: {e}") return jsonify(status='failure', error=str(e)) logger.info(f"Register completed for user {username}") user = { 'id': user_id, 'username': username, 'password_hash': password_hash, 'credential_id': webauthn_credential.credential_id.decode('utf8'), 'public_key': webauthn_credential.public_key.decode('utf8'), } return jsonify(status='success', user=user)
def test_validate_registration(self): registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, copy(REGISTRATION_RESPONSE_TMPL), REGISTRATION_CHALLENGE, TRUST_ANCHOR_DIR, True, True, uv_required=False) return registration_response.verify()
def test_validate_registration(self): registration_response = webauthn.WebAuthnRegistrationResponse( self.RP_ID, self.ORIGIN, copy(self.REGISTRATION_RESPONSE_TMPL), self.REGISTRATION_CHALLENGE, TRUST_ANCHOR_DIR, True, True, uv_required=False, none_attestation_permitted=True, ) return registration_response.verify()
def test_registration_invalid_user_verification(self): registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, copy(REGISTRATION_RESPONSE_TMPL), REGISTRATION_CHALLENGE, TRUST_ANCHOR_DIR, True, True, uv_required=True ) with self.assertRaises(webauthn.webauthn.RegistrationRejectedException): registration_response.verify()
def verify_credential_info(): name = session['register_username'] display_name = session['register_display_name'] ukey = session['register_ukey'] challenge = session['challenge'] registration_response = request.form webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( rp["id"], request.host_url[:-1], # This aims to remove the ending slash registration_response, challenge, self_attestation_permitted=True, none_attestation_permitted=True) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return make_response(jsonify({"status": "failed", "msg": str(e)}), 401) if PublicKeyCredential.query.filter_by( credential_id=webauthn_credential.credential_id).first(): return make_response( jsonify({ "status": "failed", "msg": "Key already exists." }), 401) if User.query.filter_by(email=name).first(): return make_response( jsonify({ "status": "failed", "msg": "User already exists." }), 401) webauthn_credential.credential_id = str(webauthn_credential.credential_id, "utf-8") webauthn_credential.public_key = str(webauthn_credential.public_key, "utf-8") user = User(display_name=display_name, email=name) db.session.add(user) publickey_redential = PublicKeyCredential( ukey=ukey, credential_id=webauthn_credential.credential_id, pub_key=webauthn_credential.public_key, rp_id=rp["id"], email=name, user=user) db.session.add(publickey_redential) db.session.commit() login_user(user) return make_response(jsonify({"status": "success"}))
def webauthn_end_activate(): 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 trusted_attestation_cert_required = True self_attestation_permitted = True none_attestation_permitted = True registration_response = {'clientData':AuthenticatorAttestationResponse['clientDataJSON'],'attObj':AuthenticatorAttestationResponse['attestationObject']} trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR) webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, registration_response, data[5], trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) # User Verification try: webauthn_credential = webauthn_registration_response.verify() except (RuntimeError, TypeError, NameError): print(RuntimeError) return jsonify({'fail': 'Registration failed. Error: {}'.format(RuntimeError)}) credential_id=webauthn_credential.credential_id.decode("utf-8") duplicatedId = database.query_db("select credential_id from Users where credential_id=?",[credential_id]) if len(duplicatedId)!=0: return jsonify({"Error":"Error with register, try again"}),500 existing_user = database.query_db("select user_id from Users where username=?",[data[4]]) if len(existing_user)!=0: return jsonify({"Error":"Error with register, try again"}),500 #Add user to database database.insert_db("insert into Users VALUES (?,?,?,?,?,?,?,?)",[data[2],data[4],data[3],webauthn_credential.public_key,credential_id,webauthn_credential.sign_count,'http://localhost',data[1]]) #Remove from PublicKeyCredentialCreationOptions database.delete_db("delete from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge]) return jsonify({'success': 'User successfully registered.'})
def register(): claims = current_custom_claims() if not claims.get("is_registration", False): return "", 401 data = RegisterSchema().load(request.get_json()) response = webauthn.WebAuthnRegistrationResponse( rp_id=app.config["WEBAUTHN_RP_ID"], origin=app.config["WEBAUTHN_ORIGIN"], registration_response=data, challenge=claims["challenge"], none_attestation_permitted=True, ) try: credential = response.verify() except Exception as exc: return {"error": str(exc)}, 400 user = User( id=UUID(current_user_id()), email=claims["email"], name=claims["name"], ) authenticator = Authenticator( name=data["name"], credential_id=str(credential.credential_id, "utf-8"), public_key=str(credential.public_key, "utf-8"), sign_count=credential.sign_count, ) user.authenticators = [authenticator] db.session.add(user) try: db.session.commit() except IntegrityError: return "", 409 return "", 201
def confirm_register(self, request, validated_data): validated_data = dict(validated_data) challenge = request.session['challenge'].rstrip('=') webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( self._get_rp_id(request), self._get_origin(request), validated_data['data'], challenge, 'trusted_keys', # directory trusted_attestation_cert_required=False, self_attestation_permitted=True, none_attestation_permitted=True, uv_required=False) # User Verification webauthn_credential = webauthn_registration_response.verify() validated_data['data'] = { 'pub_key': webauthn_credential.public_key.decode('utf-8'), 'credential_id': webauthn_credential.credential_id.decode('utf-8'), 'rp_id': self._get_rp_id(request), 'icon_url': self._get_origin(request), } validated_data['counter'] = webauthn_credential.sign_count return validated_data
def post(self, request, *args, **kwargs): if not request.session.get('webauthn_attest'): return HttpResponseBadRequest() if 'credential' not in request.POST or len( request.POST['credential']) > 65536: return HttpResponseBadRequest(_('Invalid WebAuthn response')) if 'name' not in request.POST or len(request.POST['name']) > 100: return HttpResponseBadRequest(_('Invalid name')) credential = json.loads(request.POST['credential']) response = webauthn.WebAuthnRegistrationResponse( rp_id=settings.WEBAUTHN_RP_ID, origin='https://' + request.get_host(), registration_response=credential['response'], challenge=request.session['webauthn_attest'], none_attestation_permitted=True, ) try: credential = response.verify() except Exception as e: return HttpResponseBadRequest(str(e)) model = WebAuthnCredential( user=request.profile, name=request.POST['name'], cred_id=credential.credential_id.decode('ascii'), public_key=credential.public_key.decode('ascii'), counter=credential.sign_count, ) model.save() if not request.profile.is_webauthn_enabled: request.profile.is_webauthn_enabled = True request.profile.save(update_fields=['is_webauthn_enabled']) return HttpResponse('OK')
def verify_register_webauthn_user(request): current_site_details = get_current_site(request) webauthn_ukey = request.session["webauthn_ukey"] registration_response = json.loads(request.POST["signedCredentials"]) webauthn_challenge = request.session["challenge"] origin = request.build_absolute_uri('/').strip("/") if DEBUG: # For angular dev server frontend - need to remove this later origin = origin.replace("8000", "4200").replace("http", "https") webauthn_verify_challege = webauthn.WebAuthnRegistrationResponse( rp_id=current_site_details.domain.split(":")[0], origin=origin, registration_response=registration_response, challenge=webauthn_challenge, self_attestation_permitted=True, none_attestation_permitted=True, uv_required=False) try: verified_webauthn_credentials = webauthn_verify_challege.verify() except Exception as e: return JsonResponse({"error": f"Registration failed due to {e}"}, status=400) django_user = User() username = request.session["username"] user_display_name = request.session["user_display_name"] django_user.username = username django_user.save() newwebauthnprofile = WebAuthnProfile() newwebauthnprofile.user = django_user newwebauthnprofile.credential_id = str( verified_webauthn_credentials.credential_id, 'utf-8') newwebauthnprofile.user_public_key = str( verified_webauthn_credentials.public_key, 'utf-8') newwebauthnprofile.display_name = user_display_name newwebauthnprofile.webauthn_ukey = webauthn_ukey newwebauthnprofile.save() return JsonResponse(newwebauthnprofile.hackyDict())
def verify_registration_credentials(): '''Verify the credential attestation generated during the registration process''' register_info = session['registration'] challenge = register_info['challenge'] ukey = register_info['ukey'] email = register_info['email'] display_name = register_info['display_name'] registration_response = request.form # Craft the response from the webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( rp_id=RP_ID, origin=ORIGIN, registration_response=registration_response, challenge=challenge, trust_anchor_dir=TRUST_ANCHOR_DIR, trusted_attestation_cert_required=True, self_attestation_permitted=True, none_attestation_permitted=True, uv_required=False, # User Verification ) try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: flash(f'Registration failed. Error: {e}', 'error') return make_response(jsonify({'redirect': url_for('register')}), 401) # Step 17. # # Check that the credentialId is not yet registered to any other user. # If registration is requested for a credential that is already registered # to a different user, the Relying Party SHOULD fail this registration # ceremony, or it MAY decide to accept the registration, e.g. while deleting # the older registration. credential_id_exists = User.query.filter_by( credential_id=webauthn_credential.credential_id ).first() if credential_id_exists: flash('Credential ID already exists.', 'error') return make_response(jsonify({'redirect': url_for('register')}), 401) # Ensure the user's email isn't already in use (TODO: refactor into a function for # both registration stages) if User.query.filter_by(email=email).first(): flash('Email address is already in use', 'error') return make_response(jsonify({'redirect': url_for('register')}), 401) # Create a new user user = User( ukey=ukey, email=email, display_name=display_name, public_key=webauthn_credential.public_key, credential_id=str(webauthn_credential.credential_id, "utf-8"), sign_count=webauthn_credential.sign_count, rp_id=RP_ID, icon_url='https://example.com', ) db.session.add(user) db.session.commit() # Clear registration session info session.pop('registration', None) flash(f'Successfully registered with email {email}') return jsonify({'redirect': url_for('login')})
def fido_setup(): if current_user.fido_enabled(): flash("You have already registered your security key", "warning") return redirect(url_for("dashboard.index")) if not current_user.can_use_fido: flash( "This feature is currently in invitation-only beta. Please send us an email if you want to try", "warning", ) return redirect(url_for("dashboard.index")) fido_token_form = FidoTokenForm() # 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 registration failed. Error: Invalid Payload", "warning") return redirect(url_for("dashboard.index")) fido_uuid = session["fido_uuid"] challenge = session["fido_challenge"] fido_reg_response = webauthn.WebAuthnRegistrationResponse( RP_ID, URL, sk_assertion, challenge, trusted_attestation_cert_required=False, none_attestation_permitted=True, ) try: fido_credential = fido_reg_response.verify() except Exception as e: LOG.error( f"An error occurred in WebAuthn registration process: {e}") flash("Key registration failed.", "warning") return redirect(url_for("dashboard.index")) current_user.fido_pk = str(fido_credential.public_key, "utf-8") current_user.fido_uuid = fido_uuid current_user.fido_sign_count = fido_credential.sign_count current_user.fido_credential_id = str(fido_credential.credential_id, "utf-8") db.session.commit() flash("Security key has been activated", "success") return redirect(url_for("dashboard.index")) # Prepare information for key registration process fido_uuid = str(uuid.uuid4()) challenge = secrets.token_urlsafe(32) credential_create_options = webauthn.WebAuthnMakeCredentialOptions( challenge, "SimpleLogin", RP_ID, fido_uuid, current_user.email, current_user.name if current_user.name else current_user.email, False, attestation="none", user_verification="discouraged", ) # Don't think this one should be used, but it's not configurable by arguments # https://www.w3.org/TR/webauthn/#sctn-location-extension registration_dict = credential_create_options.registration_dict del registration_dict["extensions"]["webauthn.loc"] session["fido_uuid"] = fido_uuid session["fido_challenge"] = challenge.rstrip("=") return render_template( "dashboard/fido_setup.html", fido_token_form=fido_token_form, credential_create_options=registration_dict, )
def verify_credential_info(): challenge = session['challenge'] username = session['register_username'] display_name = session['register_display_name'] ukey = session['register_ukey'] registration_response = request.form trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR) trusted_attestation_cert_required = True self_attestation_permitted = True none_attestation_permitted = True webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) # User Verification try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return jsonify({'fail': 'Registration failed. Error: {}'.format(e)}) # Step 17. # # Check that the credentialId is not yet registered to any other user. # If registration is requested for a credential that is already registered # to a different user, the Relying Party SHOULD fail this registration # ceremony, or it MAY decide to accept the registration, e.g. while deleting # the older registration. credential_id_exists = User.query.filter_by( credential_id=webauthn_credential.credential_id).first() if credential_id_exists: return make_response( jsonify({'fail': 'Credential ID already exists.'}), 401) existing_user = User.query.filter_by(username=username).first() if not existing_user: if sys.version_info >= (3, 0): webauthn_credential.credential_id = str( webauthn_credential.credential_id, "utf-8") webauthn_credential.public_key = str( webauthn_credential.public_key, "utf-8") user = User(ukey=ukey, username=username, display_name=display_name, pub_key=webauthn_credential.public_key, credential_id=webauthn_credential.credential_id, sign_count=webauthn_credential.sign_count, rp_id=RP_ID, icon_url='https://example.com') db.session.add(user) db.session.commit() else: return make_response(jsonify({'fail': 'User already exists.'}), 401) flash('Successfully registered as {}.'.format(username)) return jsonify({'success': 'User successfully registered.'})
def post(self, request, *args, **kwargs): try: challenge = self.request.session['webauthn_challenge'] ukey = self.request.session['webauthn_register_ukey'] resp = json.loads(self.request.POST.get("token")) trust_anchor_dir = os.path.normpath(os.path.join( os.path.dirname(os.path.abspath(__file__)), '../../static/webauthn_trusted_attestation_roots' # currently does not exist )) # We currently do not check attestation certificates, since there's no real risk # and we do not have any policies specifying what devices can be used. (Also, we # didn't get it to work.) # Read more: https://fidoalliance.org/fido-technotes-the-truth-about-attestation/ trusted_attestation_cert_required = False self_attestation_permitted = True none_attestation_permitted = True webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( urlparse(settings.SITE_URL).netloc, settings.SITE_URL, resp, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False ) webauthn_credential = webauthn_registration_response.verify() # Check that the credentialId is not yet registered to any other user. # If registration is requested for a credential that is already registered # to a different user, the Relying Party SHOULD fail this registration # ceremony, or it MAY decide to accept the registration, e.g. while deleting # the older registration. credential_id_exists = WebAuthnDevice.objects.filter( credential_id=webauthn_credential.credential_id ).first() if credential_id_exists: messages.error(request, _('This security device is already registered.')) return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={ 'device': self.device.pk })) webauthn_credential.credential_id = str(webauthn_credential.credential_id, "utf-8") webauthn_credential.public_key = str(webauthn_credential.public_key, "utf-8") self.device.credential_id = webauthn_credential.credential_id self.device.ukey = ukey self.device.pub_key = webauthn_credential.public_key self.device.sign_count = webauthn_credential.sign_count self.device.rp_id = urlparse(settings.SITE_URL).netloc self.device.icon_url = settings.SITE_URL self.device.confirmed = True self.device.save() self.request.user.log_action('pretix.user.settings.2fa.device.added', user=self.request.user, data={ 'id': self.device.pk, 'devicetype': 'u2f', 'name': self.device.name, }) notices = [ _('A new two-factor authentication device has been added to your account.') ] activate = request.POST.get('activate', '') if activate == 'on' and not self.request.user.require_2fa: self.request.user.require_2fa = True self.request.user.save() self.request.user.log_action('pretix.user.settings.2fa.enabled', user=self.request.user) notices.append( _('Two-factor authentication has been enabled.') ) self.request.user.send_security_notice(notices) self.request.user.update_session_token() update_session_auth_hash(self.request, self.request.user) note = '' if not self.request.user.require_2fa: note = ' ' + str(_('Please note that you still need to enable two-factor authentication for your ' 'account using the buttons below to make a second factor required for logging ' 'into your account.')) messages.success(request, str(_('The device has been verified and can now be used.')) + note) return redirect(reverse('control:user.settings.2fa')) except Exception: messages.error(request, _('The registration could not be completed. Please try again.')) logger.exception('WebAuthn registration failed') return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={ 'device': self.device.pk }))
def webauthn_finish_register(): user = User() cookie = None challenge = session['challenge'] username = session['register_username'] display_name = session['register_display_name'] password = session['register_password'] ukey = session['register_ukey'] registration_response = request.form trust_anchor_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR) trusted_attestation_cert_required = False self_attestation_permitted = True none_attestation_permitted = True webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) # User Verification try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return make_response( jsonify({'fail': 'Registration failed. Error: {}'.format(e)}), 401) # Step 17. # # Check that the credentialId is not yet registered to any other user. # If registration is requested for a credential that is already registered # to a different user, the Relying Party SHOULD fail this registration # ceremony, or it MAY decide to accept the registration, e.g. while deleting # the older registration. if auth.credentialIDExists(webauthn_credential.credential_id): return make_response( jsonify({'fail': 'Credential ID already exists.'}), 401) if sys.version_info >= (3, 0): webauthn_credential.credential_id = str( webauthn_credential.credential_id, "utf-8") webauthn_credential.public_key = str( webauthn_credential.public_key, "utf-8") cookie = user.addRegistration(ukey=ukey, username=username, password=password, display_name=display_name, pub_key=webauthn_credential.public_key, credential_id=webauthn_credential.credential_id, sign_count=webauthn_credential.sign_count, rp_id=RP_ID, icon_url='https://localhost:8080') if not cookie: return make_response(jsonify({'fail': 'User already exists.'}), 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 fido_setup(): if current_user.fido_uuid is not None: fidos = Fido.filter_by(uuid=current_user.fido_uuid).all() else: fidos = [] fido_token_form = FidoTokenForm() # Handling POST requests if fido_token_form.validate_on_submit(): try: sk_assertion = json.loads(fido_token_form.sk_assertion.data) except Exception: flash("Key registration failed. Error: Invalid Payload", "warning") return redirect(url_for("dashboard.index")) fido_uuid = session["fido_uuid"] challenge = session["fido_challenge"] fido_reg_response = webauthn.WebAuthnRegistrationResponse( RP_ID, URL, sk_assertion, challenge, trusted_attestation_cert_required=False, none_attestation_permitted=True, ) try: fido_credential = fido_reg_response.verify() except Exception as e: LOG.warning( f"An error occurred in WebAuthn registration process: {e}") flash("Key registration failed.", "warning") return redirect(url_for("dashboard.index")) if current_user.fido_uuid is None: current_user.fido_uuid = fido_uuid db.session.flush() Fido.create( credential_id=str(fido_credential.credential_id, "utf-8"), uuid=fido_uuid, public_key=str(fido_credential.public_key, "utf-8"), sign_count=fido_credential.sign_count, name=fido_token_form.key_name.data, ) db.session.commit() LOG.d( f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}" ) flash("Security key has been activated", "success") if not RecoveryCode.query.filter_by(user_id=current_user.id).all(): return redirect(url_for("dashboard.recovery_code_route")) else: return redirect(url_for("dashboard.fido_manage")) # Prepare information for key registration process fido_uuid = (str(uuid.uuid4()) if current_user.fido_uuid is None else current_user.fido_uuid) challenge = secrets.token_urlsafe(32) credential_create_options = webauthn.WebAuthnMakeCredentialOptions( challenge, "SimpleLogin", RP_ID, fido_uuid, current_user.email, current_user.name if current_user.name else current_user.email, False, attestation="none", user_verification="discouraged", ) # Don't think this one should be used, but it's not configurable by arguments # https://www.w3.org/TR/webauthn/#sctn-location-extension registration_dict = credential_create_options.registration_dict del registration_dict["extensions"]["webauthn.loc"] # Prevent user from adding duplicated keys for fido in fidos: registration_dict["excludeCredentials"].append({ "type": "public-key", "id": fido.credential_id, "transports": ["usb", "nfc", "ble", "internal"], }) session["fido_uuid"] = fido_uuid session["fido_challenge"] = challenge.rstrip("=") return render_template( "dashboard/fido_setup.html", fido_token_form=fido_token_form, credential_create_options=registration_dict, )
def verify_credential_info(election): challenge = session['challenge'] email = session['email'] name = session['name'] ukey = session['register_ukey'] registration_response = request.form trust_anchor_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), TRUST_ANCHOR_DIR) trusted_attestation_cert_required = True self_attestation_permitted = True none_attestation_permitted = True http = request.url.split("://") origin = f"{http[0]}://{election}.{ORIGIN}" webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, origin, registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) # User Verification try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return jsonify({'fail': 'Registration failed. Error: {}'.format(e)}) credential_id_exists = Authority.query.filter_by( credential_id=webauthn_credential.credential_id).first() if credential_id_exists: return make_response( jsonify({'fail': 'Credential ID already exists.'}), 401) if not current_user.is_authenticated: if Election.query.filter_by(name=election).first(): return make_response( jsonify({ 'fail': 'Attempted to create initial authority for existing election.' }), 401) existing_user = Authority.query.filter(Election.name == election, Authority.email == email).first() if not existing_user: webauthn_credential.credential_id = str( webauthn_credential.credential_id, "utf-8") # Create the election election_data = Election(election) # Create the user user = Authority(ukey=ukey, email=email, name=name, election=election_data, webauthn=True, pub_key=webauthn_credential.public_key, credential_id=webauthn_credential.credential_id, sign_count=webauthn_credential.sign_count, rp_id=RP_ID, icon_url=origin) db.session.add(election_data) db.session.add(user) db.session.commit() else: return make_response(jsonify({'fail': 'User already exists.'}), 401) login_user(user) return jsonify({'success': 'User successfully registered.'})