def authenticate_complete_kdf(self, state, pkconfs, credential_id, client_data, auth_data, signature): if credential_id not in pkconfs: raise Exception('Unrecognized credential id') pkconf = pkconfs[credential_id] message = auth_data + client_data.hash pycapos, pycaneg = ecdsa_recover_pubkey(signature, message, NISTP256, hashes.SHA256()) pkpos = ES256.from_cryptography_key(pycapos) pkneg = ES256.from_cryptography_key(pycaneg) def confirm(pkcbor): return bytes_eq(pkconf, sha256(b'FIDOKDF1' + pkcbor).digest()) def complete(public_key, pkcbor): credentials = [ AttestedCredentialData.create(b'\0' * 16, credential_id, public_key) ] self.authenticate_complete(state, credentials, credential_id, client_data, auth_data, signature) return sha256(b'FIDOKDF2' + pkcbor).digest() pkposcbor = cbor.encode(pkpos) if confirm(pkposcbor): return complete(pkpos, pkposcbor) pknegcbor = cbor.encode(pkneg) if confirm(pknegcbor): return complete(pkneg, pknegcbor) raise Exception('Key mismatch')
def test_login_webauthn(live_server, selenium, test_user): # pylint: disable=unused-argument """test login by webauthn""" device = SoftWebauthnDevice() device.cred_init(webauthn.rp.id, b'randomhandle') persist_and_detach( WebauthnCredential(user=test_user, user_handle=device.user_handle, credential_data=cbor.encode( device.cred_as_attested().__dict__))) selenium.get(url_for('auth.login_route', _external=True)) selenium.find_element_by_xpath( '//form//input[@name="username"]').send_keys(test_user.username) selenium.find_element_by_xpath('//form//input[@type="submit"]').click() # some javascript code must be emulated webdriver_waituntil(selenium, js_variable_ready('window.pkcro_raw')) pkcro = cbor.decode( b64decode( selenium.execute_script('return window.pkcro_raw;').encode( 'utf-8'))) assertion = device.get(pkcro, 'https://%s' % webauthn.rp.id) selenium.execute_script( 'authenticate_assertion(CBOR.decode(Sner.base64_to_array_buffer("%s")));' % b64encode(cbor.encode(assertion)).decode('utf-8')) # and back to standard test codeflow webdriver_waituntil( selenium, EC.presence_of_element_located((By.XPATH, '//a[text()="Logout"]')))
def test_send_cbor_ok(self): ctap = self.mock_ctap() ctap.device.call.return_value = b"\0" + cbor.encode({1: b"response"}) self.assertEqual({1: b"response"}, ctap.send_cbor(2, b"foobar")) ctap.device.call.assert_called_with(0x10, b"\2" + cbor.encode(b"foobar"), mock.ANY, None)
def test_send_cbor_ok(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + cbor.encode({1: b'response'}) self.assertEqual({1: b'response'}, ctap.send_cbor(2, b'foobar')) ctap.device.call.assert_called_with(0x10, b'\2' + cbor.encode(b'foobar'), None, None)
def authenticator_make_credential(self, args_cbor): args = cbor.decode(args_cbor) clientDataJSON_hash = args[0x01] rp_id = args[0x02]['id'] extension_inputs = args[0x06] rp_id_hash = sha256(rp_id.encode('utf-8')) flags = 0b11000001 # ED, AT, UP sign_count = 0 credential_id = secrets.token_bytes(32) (credential_private_key, credential_public_key) = fastecdsa.keys.gen_keypair(P256) self._credentials[credential_id] = credential_private_key attested_cred_data = pack_attested_credential_data( self._aaguid, credential_id, credential_public_key, ) authData_without_extensions = struct.pack( f'>32sBL{len(attested_cred_data)}s', rp_id_hash, flags, sign_count, attested_cred_data, ) extensions = {} if "recovery" in extension_inputs: extensions["recovery"] = self.process_recovery_extension( rp_id, authData_without_extensions, clientDataJSON_hash, extension_inputs["recovery"], ) authData = authData_without_extensions + cbor.encode(extensions) attStmt = { 0x01: 'packed', 0x02: authData, 0x03: { 'alg': -7, 'sig': DEREncoder.encode_signature( *ecdsa.sign( authData + clientDataJSON_hash, credential_private_key, hashfunc=hashlib.sha256 ) ), }, } return cbor.encode(attStmt)
def create(self, options, origin): """create credential and return PublicKeyCredential object aka attestation""" if { 'alg': -7, 'type': 'public-key' } not in options['publicKey']['pubKeyCredParams']: raise ValueError( 'Requested pubKeyCredParams does not contain supported type') if ('attestation' in options['publicKey']) and ( options['publicKey']['attestation'] != 'none'): raise ValueError('Only none attestation supported') # prepare new key self.cred_init(options['publicKey']['rp']['id'], options['publicKey']['user']['id']) # generate credential response client_data = { 'type': 'webauthn.create', 'challenge': urlsafe_b64encode( options['publicKey']['challenge']).decode('ascii'), 'origin': origin } rp_id_hash = sha256(self.rp_id.encode('ascii')) flags = b'\x41' # attested_data + user_present sign_count = pack('>I', self.sign_count) credential_id_length = pack('>H', len(self.credential_id)) cose_key = cbor.encode( ES256.from_cryptography_key(self.private_key.public_key())) attestation_object = { 'authData': rp_id_hash + flags + sign_count + self.aaguid + credential_id_length + self.credential_id + cose_key, 'fmt': 'none', 'attStmt': {} } return { 'id': urlsafe_b64encode(self.credential_id), 'rawId': self.credential_id, 'response': { 'clientDataJSON': json.dumps(client_data).encode('utf-8'), 'attestationObject': cbor.encode(attestation_object) }, 'type': 'public-key' }
def authenticate_complete(): token = request.cookies.get("token") user_identity = current_user.get_id() database_id, remember_me = get_current_user_info(token, user_identity) if database_id is None: abort(401) user = User.query.filter_by(did=database_id).first() credentials = get_credentials(database_id) if not credentials: abort(401) data = cbor.decode(request.get_data()) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] server.authenticate_complete( session.pop("state"), credentials, credential_id, client_data, auth_data, signature, ) current_counter = int(auth_data.counter) keys = Key.query.filter_by(user_id=database_id).all() for key in keys: if key.credential_id == credential_id: last_counter = key.counter if last_counter is None or last_counter >= current_counter: # Cloned => untrusted key! return cbor.encode({ "status": "error", "reason": "invalid counter" }) key.last_access = datetime.utcnow() key.counter = current_counter break db.session.add(key) db.session.commit() login_user(user, remember=remember_me) return cbor.encode({"status": "OK"})
def profile_webauthn_register_route(): """register credential for current user""" user = User.query.get(current_user.id) form = WebauthnRegisterForm() if form.validate_on_submit(): try: attestation = cbor.decode(b64decode(form.attestation.data)) auth_data = webauthn.register_complete( session.pop('webauthn_register_state'), ClientData(attestation['clientDataJSON']), AttestationObject(attestation['attestationObject'])) db.session.add(WebauthnCredential( user_id=user.id, user_handle=session.pop('webauthn_register_user_handle'), credential_data=cbor.encode(auth_data.credential_data.__dict__), name=form.name.data)) db.session.commit() return redirect(url_for('auth.profile_route')) except (KeyError, ValueError) as e: current_app.logger.exception(e) flash('Error during registration.', 'error') return render_template('auth/profile/webauthn_register.html', form=form)
def authenticate_begin(): if not credentials: abort(404) auth_data, state = server.authenticate_begin(credentials) session["state"] = state return cbor.encode(auth_data)
def authenticate_complete(request): data = cbor.decode(request.body) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] credentials = [ row.credential for row in User.objects.get( pk=request.session['user']).fidocredential_set.all() ] fido_server.authenticate_complete( request.session.pop("state"), credentials, credential_id, client_data, auth_data, signature, ) try: user = User.objects.get(pk=request.session.pop('user')) except User.DoesNotExist: raise Http404 else: # If we're here, we've already confirmed the user has the # username and the password. login(request, user) return HttpResponse( cbor.encode({"status": "OK"}), content_type='application/cbor', )
def basic_get_assertion(authenticator, clientDataJSON_hash, allowList): authenticator.authenticator_get_assertion(cbor.encode({ 0x01: RP_ID, 0x02: clientDataJSON_hash, 0x03: allowList, 0x04: {}, }))
def export_recovery_seed(self, allow_algs): for alg in allow_algs: if alg == 0: if self._recovery_seed_pri_key is None: self._initialize_recovery_seed() S = fastecdsa.keys.get_public_key(self._recovery_seed_pri_key, P256) S_enc = encode_pub(S) signed_data = struct.pack('>B16s65s', alg, self._aaguid, S_enc) sig = DEREncoder.encode_signature( *ecdsa.sign( signed_data, self._attestation_key, hashfunc=hashlib.sha256 ) ) payload = { 1: alg, 2: [], 3: self._aaguid, 4: sig, -1: S_enc, } return cbor.encode(payload) raise UnknownKeyAgreementScheme(allow_algs)
def test_webauthn_register_route(cl_user): """register new credential for user""" device = SoftWebauthnDevice() response = cl_user.get(url_for('app.webauthn_register_route')) # some javascript code must be emulated pkcco = cbor.decode( b64decode( cl_user.post(url_for('app.webauthn_pkcco_route'), { 'csrf_token': get_csrf_token(cl_user) }).body)) attestation = device.create(pkcco, 'https://%s' % webauthn.rp.ident) attestation_data = { 'clientDataJSON': attestation['response']['clientDataJSON'], 'attestationObject': attestation['response']['attestationObject'] } form = response.form form['attestation'] = b64encode(cbor.encode(attestation_data)) # and back to standard test codeflow form['name'] = 'pytest token' response = form.submit() assert response.status_code == HTTPStatus.FOUND user = User.query.filter(User.username == 'pytest_user').one() assert user.webauthn_credentials
def activate(self, request: Request, is_webauthn_signin_ff_enabled): if not is_webauthn_signin_ff_enabled: challenge = dict(u2f.begin_authentication(self.u2f_app_id, self.get_u2f_devices())) # XXX: Upgrading python-u2flib-server to 5.0.0 changes the response # format. Our current js u2f library expects the old format, so # massaging the data to include the old `authenticateRequests` key here. authenticate_requests = [] for registered_key in challenge["registeredKeys"]: authenticate_requests.append( { "challenge": challenge["challenge"], "version": registered_key["version"], "keyHandle": registered_key["keyHandle"], "appId": registered_key["appId"], } ) challenge["authenticateRequests"] = authenticate_requests return ActivationChallengeResult(challenge=challenge) credentials = [] for device in self.get_u2f_devices(): if type(device) == AuthenticatorData: credentials.append(device.credential_data) else: credentials.append(create_credential_object(device)) challenge, state = self.webauthn_authentication_server.authenticate_begin( credentials=credentials ) request.session["webauthn_authentication_state"] = state return ActivationChallengeResult(challenge=cbor.encode(challenge["publicKey"]))
def authenticate_complete(): if not User.query.count(): abort(404) try: data = cbor.decode(request.get_data()) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] except IndexError: abort(400) user = User.get_by(credential_id) if user is None: abort(401) server.authenticate_complete( session.pop("state"), [user.create_data], credential_id, client_data, auth_data, signature, ) return cbor.encode({"status": "OK"})
def register_complete_kdf(self, state, client_data, attestation_object): auth_data = self.register_complete(state, client_data, attestation_object) credential_id = auth_data.credential_data.credential_id public_key = auth_data.credential_data.public_key if not isinstance(public_key, dict): raise Exception('Invalid public key') if set(public_key.keys()) != {1, 3, -1, -2, -3}: raise Exception('Malformed public key') if public_key[1] != 2: # kty = verify raise Exception('Inappropriate public key type') if public_key[3] != ES256.ALGORITHM: # alg = ES256, ECDSA w/ SHA-256 raise Exception('Unsupported signature algorithm') if public_key[-1] != 1: # curve = NIST P-256 raise Exception('Unsupported ECDSA curve') if not isinstance(public_key[-2], bytes): raise Exception('Invalid x coordinate') if not isinstance(public_key[-3], bytes): raise Exception('Invalid y coordinate') pkcbor = cbor.encode(public_key) pkconf = sha256(b'FIDOKDF1' + pkcbor).digest() key = sha256(b'FIDOKDF2' + pkcbor).digest() return {credential_id: pkconf}, key
def get_assertion(authenticator, request_json): request = json.loads(request_json) pkcro = request['publicKeyCredentialRequestOptions'] collectedClientData = { 'type': 'webauthn.get', 'challenge': pkcro['challenge'], 'origin': 'https://localhost:8443', } clientDataJSON = json.dumps(collectedClientData, indent=None).encode('utf-8') clientDataJSON_hash = sha256(clientDataJSON) authenticator_response = cbor.decode(authenticator.authenticator_get_assertion(cbor.encode({ 0x01: pkcro['rpId'], 0x02: clientDataJSON_hash, 0x03: [{'id': base64.urlsafe_b64decode(c['id']), 'type': 'public-key'} for c in pkcro['allowCredentials']], 0x04: pkcro['extensions'], }))) authenticatorData = authenticator_response[0x02] sig = authenticator_response[0x03] credential = { 'type': 'public-key', 'id': base64.urlsafe_b64encode(authenticator_response[0x01]['id']).decode('utf-8'), 'response': { 'authenticatorData': base64.urlsafe_b64encode(authenticatorData).decode('utf-8'), 'clientDataJSON': base64.urlsafe_b64encode(clientDataJSON).decode('utf-8'), 'signature': base64.urlsafe_b64encode(sig).decode('utf-8'), }, 'clientExtensionResults': {}, } print(json.dumps(credential, indent=2))
def create_credential(authenticator, request_json): request = json.loads(request_json) pkcco = request['publicKeyCredentialCreationOptions'] collectedClientData = { 'type': 'webauthn.create', 'challenge': pkcco['challenge'], 'origin': 'https://localhost:8443', } clientDataJSON = json.dumps(collectedClientData, indent=None).encode('utf-8') clientDataJSON_hash = sha256(clientDataJSON) if 'extensions' in pkcco and 'recovery' in pkcco['extensions'] and 'allowCredentials' in pkcco['extensions']['recovery']: for cred in pkcco['extensions']['recovery']['allowCredentials']: cred['id'] = b64d(cred['id']) attObj_bytes = authenticator.authenticator_make_credential(cbor.encode({ 0x01: clientDataJSON_hash, 0x02: pkcco['rp'], 0x06: pkcco['extensions'], })) attObj = ctap2.AttestationObject(attObj_bytes) credential_id = attObj.auth_data.credential_data.credential_id credential = { 'type': 'public-key', 'id': base64.urlsafe_b64encode(credential_id).decode('utf-8'), 'response': { 'attestationObject': base64.urlsafe_b64encode(ctap2_to_webauthn_attestation_object(attObj_bytes)).decode('utf-8'), 'clientDataJSON': base64.urlsafe_b64encode(clientDataJSON).decode('utf-8'), }, 'clientExtensionResults': {}, } print(json.dumps(credential, indent=2))
def ctap2_to_webauthn_attestation_object(attObj_cbor): attObj = cbor.decode(attObj_cbor) return cbor.encode({ 'fmt': attObj[0x01], 'authData': attObj[0x02], 'attStmt': attObj[0x03], })
def assertion_response(): app.logger.debug("/assertion/response") data = cbor.decode(request.get_data()) app.logger.debug('post_data:\n%s', pp.pformat(data)) credential_id = data['id'] raw_id = data['rawId'] client_data = ClientData(data['clientDataJSON']) app.logger.debug('client_data:\n%s', pp.pformat(client_data)) auth_data = AuthenticatorData(data['authenticatorData']) app.logger.debug('auth_data:\n%s', pp.pformat(auth_data)) signature = data['signature'] credential = Credential.get(credential_id) if not credential: abort(404) credential_data = credential.to_credential_data() server.authenticate_complete(session.pop('state'), [credential_data], raw_id, client_data, auth_data, signature) # Checking signCount if auth_data.counter > credential.counter: # TODO: flask_ldapconn is not allow to store integer value credential.counter = str(auth_data.counter) credential.save() else: app.logger.warn('wrong counter: stored counter=%d but %d asserted', credential.counter, auth_data.counter) abort(404) user = credential.user if not user: abort(404) login_user(user) return cbor.encode({'status': 'OK'})
def test_login_webauthn(live_server, selenium, webauthn_credential_factory): # pylint: disable=unused-argument """test login by webauthn""" device = SoftWebauthnDevice() device.cred_init(webauthn.rp.id, b'randomhandle') wncred = webauthn_credential_factory.create(initialized_device=device) # factory post_generate does not call commit to propagate self.attr changes, that messes the actual db state when # accessing from different process such as real browser db.session.commit() selenium.get(url_for('auth.login_route', _external=True)) selenium.find_element_by_xpath( '//form//input[@name="username"]').send_keys(wncred.user.username) selenium.find_element_by_xpath('//form//input[@type="submit"]').click() # some javascript code must be emulated webdriver_waituntil(selenium, js_variable_ready('window.pkcro_raw')) pkcro = cbor.decode( b64decode( selenium.execute_script('return window.pkcro_raw;').encode( 'utf-8'))) assertion = device.get(pkcro, 'https://%s' % webauthn.rp.id) selenium.execute_script( 'authenticate_assertion(CBOR.decode(Sner.base64_to_array_buffer("%s")));' % b64encode(cbor.encode(assertion)).decode('utf-8')) # and back to standard test codeflow webdriver_waituntil( selenium, EC.presence_of_element_located((By.XPATH, '//a[text()="Logout"]')))
def test_profile_webauthn_register_route(live_server, sl_user): # pylint: disable=unused-argument """register new credential for user""" device = SoftWebauthnDevice() sl_user.get(url_for('auth.profile_webauthn_register_route', _external=True)) # some javascript code must be emulated webdriver_waituntil(sl_user, js_variable_ready('window.pkcco_raw')) pkcco = cbor.decode( b64decode( sl_user.execute_script('return window.pkcco_raw;').encode( 'utf-8'))) attestation = device.create(pkcco, 'https://%s' % webauthn.rp.id) sl_user.execute_script( 'pack_attestation(CBOR.decode(Sner.base64_to_array_buffer("%s")));' % b64encode(cbor.encode(attestation)).decode('utf-8')) # and back to standard test codeflow sl_user.find_element_by_xpath( '//form[@id="webauthn_register_form"]//input[@name="name"]').send_keys( 'pytest token') sl_user.find_element_by_xpath( '//form[@id="webauthn_register_form"]//input[@type="submit"]').click() user = User.query.filter(User.username == 'pytest_user').one() assert user.webauthn_credentials
def buildFIDO2Registration(self, user): try: self.credentials[user] = {} self.credentials[user]["credentials"] = [] registration_data, state = self.server.register_begin( { "id": user.encode('utf-8'), #Required "name": user, # Optional #"displayName": "First Last", #Optional #"icon": "https://domain.com/user_avatar.png" #Optional }, self.credentials[user]["credentials"], # User Verification Mode # discouraged - passwordless devices can be used, required - User password required for device to be used, # preferred - User password not required but preferred # Note - Windows will still require a PIN be created for devices that support it if using 'preferred' user_verification="discouraged", # Supported Authenticator Types # cross-platform - e.g. usb yubikey, platform - e.g. Windows Hello, omit/None - Any type # If using USB tokens like Yubikeys, it's recommended cross-platform is set, Windows Hello will prompt before a Yubikey #authenticator_attachment="cross-platform", ) self.credentials[user]["state"] = state ec_data = cbor.encode(registration_data) b64reg = base64.b64encode(ec_data).decode('utf-8') b64user = base64encode(user) reply = "CRV1:FIDO2,R:reg:%s:%s" % (b64user, b64reg) self.saveCredentials(self.credsfile) return reply except Exception as e: print("Failed buildFIDO2Registration") print(e) return None
def get_cbor_resp(data=None, redirect=None): resp = HttpResponse(content_type="application/cbor") if data: resp.write(cbor.encode(data)) if redirect: resp.write(redirect) return resp
def test_login_webauthn(client, webauthn_credential_factory): """test login by webauthn""" device = SoftWebauthnDevice() device.cred_init(webauthn.rp.id, b'randomhandle') wncred = webauthn_credential_factory.create(initialized_device=device) form = client.get(url_for('auth.login_route')).form form['username'] = wncred.user.username response = form.submit() assert response.status_code == HTTPStatus.FOUND response = response.follow() # some javascript code muset be emulated pkcro = cbor.decode( b64decode( client.post(url_for('auth.login_webauthn_pkcro_route'), { 'csrf_token': get_csrf_token(client) }).body)) assertion = device.get(pkcro, f'https://{webauthn.rp.id}') assertion_data = { 'credentialRawId': assertion['rawId'], 'authenticatorData': assertion['response']['authenticatorData'], 'clientDataJSON': assertion['response']['clientDataJSON'], 'signature': assertion['response']['signature'], 'userHandle': assertion['response']['userHandle'] } form = response.form form['assertion'] = b64encode(cbor.encode(assertion_data)) response = form.submit() # and back to standard test codeflow assert response.status_code == HTTPStatus.FOUND response = client.get(url_for('index_route')) assert response.lxml.xpath('//a[text()="Logout"]')
def test_webauthn_register_new_key(test_client, init_database): sign_in_response = sign_in(test_client, "dave", "wselfknskjdksdaiujlj") device = SoftWebauthnDevice() begin_register_response = test_client.post("/webauthn/register/begin", data=json.dumps( {"resident": False})) pkcco = cbor.decode(begin_register_response.data) attestation = device.create(pkcco, f"https://{TestConfig.RP_ID}") attestation_data = cbor.encode({ "clientDataJSON": attestation["response"]["clientDataJSON"], "attestationObject": attestation["response"]["attestationObject"], }) raw_response = test_client.post( "/webauthn/register/complete", input_stream=BytesIO(attestation_data), content_type="application/cbor", ) registration_response = cbor.decode(raw_response.data) assert registration_response == {"status": "OK"} user = User.query.filter_by(username="******").first() webauthn = Webauthn.query.filter_by(user_id=user.did).first() assert webauthn assert webauthn.number == 1 assert webauthn.is_enabled is False
def register_options(): app.logger.debug('/register/response') if 'resident_key' in request.json: resident_key = request.json['resident_key'] else: regident_key = False credentials = [] user_verification = app.config.get('RP_USER_VERIFICATION', "discouraged") user_id = UUID(current_user.entryUUID).bytes if current_user.description: displayName = current_user.description else: displayName = current_user.uid registration_data, state = server.register_begin( { 'id': user_id, 'name': current_user.uid, 'displayName': displayName, 'icon': 'https://example.com/' }, credentials, user_verification=user_verification, resident_key=resident_key) session['state'] = state app.logger.debug('registration_data:\n%s', pp.pformat(registration_data)) app.logger.debug('state:\n%s', pp.pformat(state)) return cbor.encode(registration_data)
def test_failure_on_non_dict_cbor_data(self): res = self._dev.call( CTAPHID.CBOR, data=ctap2.Ctap2.CMD.MAKE_CREDENTIAL.to_bytes(1, 'big') + cbor.encode('str'), ) assert res == CtapError.ERR.INVALID_CBOR.to_bytes(1, 'big')
def registration_begin(user, authenticator): user_webauthn_tokens = user.credentials.filter(FidoCredential) if user_webauthn_tokens.count >= current_app.config.webauthn_max_allowed_tokens: current_app.logger.error( 'User tried to register more than {} tokens.'.format(current_app.config.webauthn_max_allowed_tokens) ) return error_response(message=SecurityMsg.max_webauthn) creds = make_credentials(user_webauthn_tokens.to_list()) server = get_webauthn_server(current_app.config.fido2_rp_id) if user.given_name is None or user.surname is None or user.display_name is None: return error_response(message=SecurityMsg.no_pdata) registration_data, state = server.register_begin( { 'id': str(user.eppn).encode('ascii'), 'name': "{} {}".format(user.given_name, user.surname), 'displayName': user.display_name, }, credentials=creds, user_verification=USER_VERIFICATION.DISCOURAGED, authenticator_attachment=authenticator, ) session['_webauthn_state_'] = state current_app.logger.info('User {} has started registration of a webauthn token'.format(user)) current_app.logger.debug('Webauthn Registration data: {}.'.format(registration_data)) current_app.stats.count(name='webauthn_register_begin') encoded_data = base64.urlsafe_b64encode(cbor.encode(registration_data)).decode('ascii') encoded_data = encoded_data.rstrip('=') return {'csrf_token': session.new_csrf_token(), 'registration_data': encoded_data}
def authenticate_begin(username, **_): """ Begin authentication procedure Variables: username user name of the user you want to login with Arguments: None Data Block: None Result example: <WEBAUTHN_AUTHENTICATION_DATA> """ user = STORAGE.user.get(username, as_obj=False) if not user: return make_api_response({'success': False}, err="Bad Request", status_code=400) session.pop('state', None) security_tokens = user.get('security_tokens', {}) or {} credentials = [AttestedCredentialData(websafe_decode(x)) for x in security_tokens.values()] auth_data, state = server.authenticate_begin(credentials) session['state'] = state return make_api_response(list(cbor.encode(auth_data)))