def authenticate(self, request: HttpRequest, user: Optional[AbstractBaseUser], fido2_server: Fido2Server, fido2_state: Dict[str, bytes], fido2_response: Dict[str, Any]) -> Optional[AbstractBaseUser]: """Authenticate using FIDO 2.""" user_handle = fido2_response['user_handle'] try: device = Authenticator.objects.get(user_handle=user_handle) user = device.user credentials = [device.credential] fido2_server.authenticate_complete( fido2_state, credentials, fido2_response['credential_id'], fido2_response['client_data'], fido2_response['authenticator_data'], fido2_response['signature']) except ValueError as error: _LOGGER.info("FIDO 2 authentication failed with error: %r", error) return None except Authenticator.DoesNotExist: _LOGGER.info("FIDO 2 authentication could not find user handle: %s", user_handle) return None try: self.mark_device_used(device, fido2_response['authenticator_data'].counter) except ValueError: # Raise `PermissionDenied` to stop the authentication process and skip remaining backends. messages.error(request, self.counter_error_message) raise PermissionDenied("Counter didn't increase.") return user
def server(self) -> Fido2Server: """Return FIDO 2 server instance.""" rp = PublicKeyCredentialRpEntity(self.get_rp_id(), self.get_rp_name()) if AttestationVerifier is None: return Fido2Server(rp, attestation=self.attestation, attestation_types=self.attestation_types) elif self.verify_attestation is None: if self.attestation_types is not None: warnings.warn('You have defined `attestation_types` but not `verify_attestation`, this means that the ' '`attestation_types` setting is being iognored.', DeprecationWarning) return Fido2Server(rp, attestation=self.attestation) else: return Fido2Server(rp, attestation=self.attestation, verify_attestation=self.verify_attestation(self.attestation_types))
def test_authenticate_complete_invalid_signature(self): rp = PublicKeyCredentialRpEntity("Example", "example.com") server = Fido2Server(rp) state = { "challenge": "GAZPACHO!", "user_verification": UserVerificationRequirement.PREFERRED, } client_data = CollectedClientData.create( CollectedClientData.TYPE.GET, "GAZPACHO!", "https://example.com", ) _AUTH_DATA = bytes.fromhex( "A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D" ) with self.assertRaisesRegex(ValueError, "Invalid signature."): server.authenticate_complete( state, [AttestedCredentialData(_ATT_CRED_DATA)], _CRED_ID, client_data, AuthenticatorData(_AUTH_DATA), b"INVALID", )
def test_register_begin_custom_challenge_too_short(self): rp = PublicKeyCredentialRpEntity("Example", "example.com") server = Fido2Server(rp) challenge = b"123456789012345" with self.assertRaises(ValueError): request, state = server.register_begin(USER, challenge=challenge)
def fido2_api_register_begin(request): rp = PublicKeyCredentialRpEntity(get_domain(request), settings.FIDO2_RP_NAME) fido2 = Fido2Server(rp) all_devices = Fido2Device.objects.filter(user=request.user) registration_data, state = fido2.register_begin( { "id": request.user.email.encode(), "name": request.user.email, "displayName": request.user.email, "icon": "", }, # Pass existing fido2 credentials to prevent duplicate credentials=[ AttestedCredentialData(cbor2.loads(d.authenticator_data)) for d in all_devices ], user_verification="discouraged", authenticator_attachment="cross-platform", ) request.session[FIDO2_REGISTER_STATE] = state return HttpResponse(cbor2.dumps(registration_data), content_type="application/octet-stream")
def verify_token(self, token): data = cbor2.loads(base64.b64decode(token["token"])) credential_id = data["credentialId"] client_data = ClientData(data["clientDataJSON"]) auth_data = AuthenticatorData(data["authenticatorData"]) signature = data["signature"] state = token["state"] domain = token["domain"] credentials_query = Fido2Device.objects.filter(user=self.user) credentials = [ AttestedCredentialData(cbor2.loads(c.authenticator_data)) for c in credentials_query ] rp = PublicKeyCredentialRpEntity(domain, settings.FIDO2_RP_NAME) fido2 = Fido2Server(rp) try: fido2.authenticate_complete( state, credentials, credential_id, client_data, auth_data, signature, ) return True except ValueError: logger.exception("Error in FIDO2 final authentication") return False
def authenticate( self, request: HttpRequest, user: AbstractBaseUser, fido2_server: Fido2Server, fido2_state: Dict[str, bytes], fido2_response: Dict[str, Any]) -> Optional[AbstractBaseUser]: """Authenticate using FIDO 2.""" credentials = [a.credential for a in user.authenticators.all()] try: credential = fido2_server.authenticate_complete( fido2_state, credentials, fido2_response['credential_id'], fido2_response['client_data'], fido2_response['authenticator_data'], fido2_response['signature']) except ValueError as error: _LOGGER.info("FIDO 2 authentication failed with error: %r", error) return None device = user.authenticators.get(credential_id_data=base64.b64encode( credential.credential_id).decode('utf-8')) try: self.mark_device_used(device, fido2_response['authenticator_data'].counter) except ValueError: # Raise `PermissionDenied` to stop the authentication process and skip remaining backends. messages.error(request, self.counter_error_message) raise PermissionDenied("Counter didn't increase.") return user
def test_register_begin_custom_challenge_too_short(self): rp = RelyingParty("example.com", "Example") server = Fido2Server(rp) challenge = b"123456789012345" with self.assertRaises(ValueError): request, state = server.register_begin({}, challenge=challenge)
def test_authenticate_complete_invalid_signature(self): rp = RelyingParty("example.com", "Example") server = Fido2Server(rp) state = { "challenge": "GAZPACHO!", "user_verification": USER_VERIFICATION.PREFERRED, } client_data_dict = { "challenge": "GAZPACHO!", "origin": "https://example.com", "type": WEBAUTHN_TYPE.GET_ASSERTION, } client_data = ClientData(json.dumps(client_data_dict).encode("utf-8")) _AUTH_DATA = a2b_hex( "A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D" ) with six.assertRaisesRegex(self, ValueError, "Invalid signature."): server.authenticate_complete( state, [AttestedCredentialData(_ATT_CRED_DATA)], _CRED_ID, client_data, AuthenticatorData(_AUTH_DATA), b"INVALID", )
def test_register_begin_custom_challenge(self): rp = RelyingParty("example.com", "Example") server = Fido2Server(rp) challenge = b"1234567890123456" request, state = server.register_begin({}, challenge=challenge) self.assertEqual(request["publicKey"]["challenge"], challenge)
def test_register_begin_rp_no_icon(self): rp = RelyingParty('example.com', 'Example') server = Fido2Server(rp) request, state = server.register_begin({}) self.assertEqual(request['publicKey']['rp'], {'id': 'example.com', 'name': 'Example'})
def test_register_begin_custom_challenge(self): rp = PublicKeyCredentialRpEntity("Example", "example.com") server = Fido2Server(rp) challenge = b"1234567890123456" request, state = server.register_begin(USER, challenge=challenge) self.assertEqual(request["publicKey"]["challenge"], challenge)
def main(): server = Fido2Server({ "id": "example.com", "name": "Example RP" }, attestation="direct") client = get_client() # add_credentials(server, client) authenticate_device(server, client)
def test_register_begin_rp_no_icon(self): rp = RelyingParty("example.com", "Example") server = Fido2Server(rp) request, state = server.register_begin({}) self.assertEqual( request["publicKey"]["rp"], {"id": "example.com", "name": "Example"} )
def __init__(self, params): """ Create an instance of the class. :param params: Dictionary containing the following parameters db_path: Path to the user database rp_id: Relying Party identifier of the server. pre_share_eph_username: Indicates whether or not the server tries to establish an ephemeral user name for the next handshake db_encryption_key: The key the database is encrypted with. modes: List of allowed FIDO2 operation modes during authentication. force_fido2: Flag indicating whether or not to accept only FIDO2 authenticated users. """ # check arguments self._valid = False db_path = rp_id = encryption_key = None pre_share_eph_username = force_fido2 = False modes = FIDO2Mode.all if 'db_path' in params and isinstance(params['db_path'], str): db_path = params['db_path'] if 'rp_id' in params and isinstance(params['rp_id'], str): rp_id = params['rp_id'].lower() if not is_valid_hostname(rp_id): rp_id = None if 'db_encryption_key' in params and \ isinstance(params['db_encryption_key'], RSAKey): encryption_key = params['db_encryption_key'] if 'pre_share_eph_user_name' in params and \ isinstance(params['pre_share_eph_user_name'], bool): pre_share_eph_username = params['pre_share_eph_user_name'] if 'modes' in params and \ isinstance(params['modes'], list): modes = params['modes'] if 'force_fido2' in params and isinstance(params['force_fido2'], bool): force_fido2 = params['force_fido2'] # check if mandatory arguments are set if not db_path or not rp_id: return self.state = ServerState.init self.mode = None self.allowed_modes = modes self._db_connection = self._get_db_connection(db_path, encryption_key) relying_party = RelyingParty(rp_id) self._server = Fido2Server(relying_party) self.pre_share_eph_user_name = pre_share_eph_username self.force_fido2 = force_fido2 self._auth_state = None self._user_id = None self._eph_user_name_server_share = None self._allow_credentials = [] self._valid = bool(self._db_connection is not None)
def setUp(self): self.u2f = U2fInterface() self.login_as(user=self.user) rp = PublicKeyCredentialRpEntity("richardmasentry.ngrok.io", "Sentry") self.test_registration_server = Fido2Server(rp, verify_origin=verifiy_origin) self.test_authentication_server = U2FFido2Server( app_id="http://richardmasentry.ngrok.io/auth/2fa/u2fappid.json", rp={"id": "richardmasentry.ngrok.io", "name": "Sentry"}, verify_u2f_origin=verifiy_origin, )
def test_register_begin_rp(self): rp = PublicKeyCredentialRpEntity("Example", "example.com") server = Fido2Server(rp) request, state = server.register_begin(USER) self.assertEqual(request["publicKey"]["rp"], { "id": "example.com", "name": "Example" })
def test_register_begin_rp_icon(self): rp = RelyingParty('example.com', 'Example', 'http://example.com/icon.svg') server = Fido2Server(rp) request, state = server.register_begin({}) data = {'id': 'example.com', 'name': 'Example', 'icon': 'http://example.com/icon.svg'} self.assertEqual(request['publicKey']['rp'], data)
def test_register_begin_rp_icon(self): rp = RelyingParty("example.com", "Example", "http://example.com/icon.svg") server = Fido2Server(rp) request, state = server.register_begin({}) data = { "id": "example.com", "name": "Example", "icon": "http://example.com/icon.svg", } self.assertEqual(request["publicKey"]["rp"], data)
def _get_fido2server(credentials, fido2rp): # See if any of the credentials is a legacy U2F credential with an app-id # (assume all app-ids are the same - authenticating with a mix of different # app-ids isn't supported in current Webauthn) app_id = None for k, v in credentials.items(): if v['app_id']: app_id = v['app_id'] break if app_id: return U2FFido2Server(app_id, fido2rp) return Fido2Server(fido2rp)
def begin(request): rp_host = urlparse(request.build_absolute_uri()).hostname rp = RelyingParty(rp_host, 'Demo server') server = Fido2Server(rp) existing_credentials = AttestedCredentialData.objects.filter( user=request.user).all() auth_data, state = server.authenticate_begin(existing_credentials) request.session['state'] = { 'challenge': state['challenge'], 'user_verification': state['user_verification'].value, } return Response(auth_data, content_type="application/cbor")
def do_register_user(user, rp_id, resident_key=False): """ FIDO2 registration process :param user: The user to register :param rp_id: Relying Party identifier :param resident_key: Boolean indicating whether or not to store a resident key :return: Newly created credentials """ # begin registration relying_part = RelyingParty(rp_id) server = Fido2Server(relying_part) registration_data, state = server.register_begin(user) # make credential dev = next(CtapHidDevice.list_devices(), None) if not dev: print('No FIDO device found') sys.exit(1) client = Fido2Client(dev, 'https://' + rp_id) rp = {'id': rp_id, 'name': rp_id} challenge = websafe_encode(registration_data['publicKey']['challenge']) if resident_key: user['name'] = "." user_string = "(id: {0})".format(user['id'].hex()) else: user_string = "(name: {0}, display name: {1})".format( user['name'], user['displayName']) print("\nRegistration request for user: "******"From service: (Address: {0}, Name: {1})".format(rp['id'], rp['name'])) print('Touch your authenticator device now to consent to registration...\n') try: attestation_object, client_data = client.make_credential( rp, user, challenge, rk=resident_key) except Exception as e: print("Registration failed") raise e # complete registration registration_data = server.register_complete(state, client_data, attestation_object) credential = registration_data.credential_data print("Registration complete") return credential
class TestFido2GeneralAuthenticationBackend(TestCase): """Test `Fido2GeneralAuthenticationBackend` class.""" backend = Fido2GeneralAuthenticationBackend() server = Fido2Server(PublicKeyCredentialRpEntity(HOSTNAME, HOSTNAME)) state = { 'challenge': AUTHENTICATION_CHALLENGE, 'user_verification': UserVerificationRequirement.PREFERRED } fido2_response = { 'client_data': ClientData(base64.b64decode(AUTHENTICATION_CLIENT_DATA)), 'credential_id': base64.b64decode(CREDENTIAL_ID), 'authenticator_data': AuthenticatorData(base64.b64decode(AUTHENTICATOR_DATA)), 'signature': base64.b64decode(SIGNATURE) } def setUp(self): self.user = User.objects.create_user(USERNAME, password=PASSWORD) self.device = Authenticator.objects.create( user=self.user, credential_id_data=CREDENTIAL_ID, attestation_data=ATTESTATION_OBJECT) def test_authenticate(self): authenticated_user = self.backend.authenticate(sentinel.request, USERNAME, PASSWORD, self.server, self.state, self.fido2_response) self.assertEqual(authenticated_user, self.user) self.assertQuerysetEqual(Authenticator.objects.values_list( 'user', 'counter'), [(self.user.pk, 152)], transform=tuple) def test_authenticate_wrong_password(self): authenticated_user = self.backend.authenticate(sentinel.request, USERNAME, 'wrong_password', self.server, self.state, self.fido2_response) self.assertEqual(authenticated_user, None) self.assertQuerysetEqual(Authenticator.objects.values_list( 'user', 'counter'), [(self.user.pk, 0)], transform=tuple)
def test_authenticate_complete_invalid_signature(self): rp = RelyingParty('example.com', 'Example') server = Fido2Server(rp) state = {'challenge': 'GAZPACHO!', 'user_verification': USER_VERIFICATION.PREFERRED} client_data_dict = {'challenge': 'GAZPACHO!', 'origin': 'https://example.com', 'type': WEBAUTHN_TYPE.GET_ASSERTION} client_data = ClientData(json.dumps(client_data_dict).encode('utf-8')) _AUTH_DATA = a2b_hex('A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947010000001D') # noqa with six.assertRaisesRegex(self, ValueError, 'Invalid signature.'): server.authenticate_complete( state, [AttestedCredentialData(_ATT_CRED_DATA)], _CRED_ID, client_data, AuthenticatorData(_AUTH_DATA), b'INVALID')
def test_authenticate(): """test authentication""" device = SoftWebauthnDevice() device.cred_init('example.org', b'randomhandle') registered_credential = device.cred_as_attested() server = Fido2Server( PublicKeyCredentialRpEntity('example.org', 'test server')) options, state = server.authenticate_begin([registered_credential]) assertion = device.get(options, 'https://example.org') server.authenticate_complete( state, [registered_credential], assertion['rawId'], ClientData(assertion['response']['clientDataJSON']), AuthenticatorData(assertion['response']['authenticatorData']), assertion['response']['signature'])
def test_action_webauthn_legacy_token(self, mock_complete_authn): # mock_complete_authn.return_value = ({'keyHandle': 'test_key_handle'}, # 'dummy-touch', 'dummy-counter') # # Add a working U2F credential for this test u2f = U2F.from_dict( dict( version='U2F_V2', keyhandle= 'V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n_rHFXcXbSpOoX__aUKyTR6jEC8Xv678WjXC5KEkvziA', public_key= 'BHVTWuo3_D7ruRBe2Tw-m2atT2IOm_qQWSDreWShu3t21ne9c-DPSUdym-H-t7FcjV7rj1dSc3WSwaOJpFmkKxQ', app_id='https://dev.eduid.se/u2f-app-id.json', attest_cert='', description='unit test U2F token', )) self.user.credentials.add(u2f) self.app.central_userdb.save(self.user, check_sync=False) fido2_state = json.dumps( Fido2Server._make_internal_state( base64.b64decode( '3h/EAZpY25xDdSJCOMx1ABZEA5Odz3yejUI3AUNTQWc='), 'preferred')) self.app.config.fido2_rp_id = 'idp.dev.eduid.se' data1 = { 'authenticatorData': 'mZ9k6EPHoJxJZNA+UuvM0JVoutZHmqelg9kXe/DSefgBAAAA/w==', 'clientDataJSON': ('eyJjaGFsbGVuZ2UiOiIzaF9FQVpwWTI1eERkU0pDT014MUFCWkVBNU9k' 'ejN5ZWpVSTNBVU5UUVdjIiwib3JpZ2luIjoiaHR0cHM6Ly9pZHAuZGV2LmVkdWlkLnNlIiwidH' 'lwZSI6IndlYmF1dGhuLmdldCJ9'), 'credentialId': ('V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n/rHFXcXbSpOoX//aUKyTR6jEC8Xv678WjXC5KEkvziA==' ), 'signature': ('MEYCIQC5gM8inamJGUFKu3bNo4fT0jmJQuw33OSSXc242NCuiwIhAIWnVw2Spow72j6J92KaY2rLR6qSXEbLam09ZXbSkBnQ' ), } response = self._action(data1=data1, fido2_state=fido2_state) self.assertEqual(response.status_code, 200) self.assertEqual( len(self.app.actions_db.get_actions(self.user.eppn, 'mock-session')), 1)
def fido2_api_login_begin(request): user = request.user credentials_query = Fido2Device.objects.filter(user=user) credentials = [ AttestedCredentialData(cbor2.loads(c.authenticator_data)) for c in credentials_query ] rp = PublicKeyCredentialRpEntity(get_domain(request), settings.FIDO2_RP_NAME) fido2 = Fido2Server(rp) auth_data, state = fido2.authenticate_begin( credentials, user_verification="discouraged") request.session["fido2_state"] = state request.session["fido2_domain"] = get_domain(request) return HttpResponse(cbor2.dumps(auth_data), content_type="application/cbor")
def authenticate(request): rp_host = urlparse(request.build_absolute_uri()).hostname rp = RelyingParty(rp_host, 'Demo server') server = Fido2Server(rp) data = request.data[0] credential_id = data['credentialId'] credentials = AttestedCredentialData.objects.filter( user=request.user, ).all() client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] state = request.session['state'] cred = server.authenticate_complete(state, credentials, credential_id, client_data, auth_data, signature) return cred
def test_action_webauthn_legacy_token(self, mock_complete_authn): #mock_complete_authn.return_value = ({'keyHandle': 'test_key_handle'}, # 'dummy-touch', 'dummy-counter') # # Add a working U2F credential for this test u2f = U2F(version='U2F_V2', keyhandle='V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n_rHFXcXbSpOoX__aUKyTR6jEC8Xv678WjXC5KEkvziA', public_key='BHVTWuo3_D7ruRBe2Tw-m2atT2IOm_qQWSDreWShu3t21ne9c-DPSUdym-H-t7FcjV7rj1dSc3WSwaOJpFmkKxQ', app_id='https://dev.eduid.se/u2f-app-id.json', attest_cert='', description='unit test U2F token' ) self.user.credentials.add(u2f) self.app.central_userdb.save(self.user, check_sync=False) with self.session_cookie(self.browser) as client: self.prepare(client, Plugin, 'mfa', action_dict=MFA_ACTION) with self.app.test_request_context(): with client.session_transaction() as sess: fido2_state = Fido2Server._make_internal_state( base64.b64decode('3h/EAZpY25xDdSJCOMx1ABZEA5Odz3yejUI3AUNTQWc='), 'preferred') sess['eduid_action.mfa.webauthn.state'] = json.dumps(fido2_state) csrf_token = sess.get_csrf_token() data = json.dumps({'csrf_token': csrf_token, 'authenticatorData': 'mZ9k6EPHoJxJZNA+UuvM0JVoutZHmqelg9kXe/DSefgBAAAA/w==', 'clientDataJSON': 'eyJjaGFsbGVuZ2UiOiIzaF9FQVpwWTI1eERkU0pDT014MUFCWkVBNU9k'+\ 'ejN5ZWpVSTNBVU5UUVdjIiwib3JpZ2luIjoiaHR0cHM6Ly9pZHAuZGV2LmVkdWlkLnNlIiwidH'+\ 'lwZSI6IndlYmF1dGhuLmdldCJ9', 'credentialId': 'V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n/rHFXcXbSpOoX//'+\ 'aUKyTR6jEC8Xv678WjXC5KEkvziA==', 'signature': 'MEYCIQC5gM8inamJGUFKu3bNo4fT0jmJQuw33OSSXc242NCuiwIhAIWnVw2Sp'+\ 'ow72j6J92KaY2rLR6qSXEbLam09ZXbSkBnQ'} ) self.app.config['FIDO2_RP_ID'] = 'idp.dev.eduid.se' response = client.post('/post-action', data=data, content_type=self.content_type_json) self.assertEquals(response.status_code, 200) data = json.loads(response.data) self.assertEquals(len(self.app.actions_db.get_actions(self.user.eppn, 'mock-session')), 1) mock_idp_app = MockIdPApp(self.app.actions_db) mock_idp_app.logger = self.app.logger add_actions(mock_idp_app, self.user, MockTicket('mock-session')) self.assertEquals(len(self.app.actions_db.get_actions(self.user.eppn, 'mock-session')), 0)
def fido2_api_register_finish(request): data = cbor2.loads(request.body) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) rp = PublicKeyCredentialRpEntity(get_domain(request), settings.FIDO2_RP_NAME) fido2 = Fido2Server(rp) auth_data = fido2.register_complete(request.session[FIDO2_REGISTER_STATE], client_data, att_obj) device = Fido2Device( authenticator_data=cbor2.dumps(auth_data.credential_data)) device.user = request.user device.name = data["name"] or "Fido key" device.confirmed = True device.save() return HttpResponse(cbor2.dumps({"status": "OK"}), content_type="application/cbor")
def test_action_webauthn_legacy_token(self, mock_complete_authn): #mock_complete_authn.return_value = ({'keyHandle': 'test_key_handle'}, # 'dummy-touch', 'dummy-counter') # # Add a working U2F credential for this test u2f = U2F(version='U2F_V2', keyhandle='V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n_rHFXcXbSpOoX__aUKyTR6jEC8Xv678WjXC5KEkvziA', public_key='BHVTWuo3_D7ruRBe2Tw-m2atT2IOm_qQWSDreWShu3t21ne9c-DPSUdym-H-t7FcjV7rj1dSc3WSwaOJpFmkKxQ', app_id='https://dev.eduid.se/u2f-app-id.json', attest_cert='', description='unit test U2F token' ) self.user.credentials.add(u2f) self.app.central_userdb.save(self.user, check_sync=False) with self.session_cookie(self.browser) as client: self.prepare(client, Plugin, 'mfa', action_dict=MFA_ACTION) with self.app.test_request_context(): with client.session_transaction() as sess: fido2_state = Fido2Server._make_internal_state( base64.b64decode('3h/EAZpY25xDdSJCOMx1ABZEA5Odz3yejUI3AUNTQWc='), 'preferred') sess['eduid_webapp.actions.actions.mfa.webauthn.state'] = json.dumps(fido2_state) csrf_token = sess.get_csrf_token() data = json.dumps({'csrf_token': csrf_token, 'authenticatorData': 'mZ9k6EPHoJxJZNA+UuvM0JVoutZHmqelg9kXe/DSefgBAAAA/w==', 'clientDataJSON': 'eyJjaGFsbGVuZ2UiOiIzaF9FQVpwWTI1eERkU0pDT014MUFCWkVBNU9k'+\ 'ejN5ZWpVSTNBVU5UUVdjIiwib3JpZ2luIjoiaHR0cHM6Ly9pZHAuZGV2LmVkdWlkLnNlIiwidH'+\ 'lwZSI6IndlYmF1dGhuLmdldCJ9', 'credentialId': 'V1vXqZcwBJD2RMIH2udd2F7R9NoSNlP7ZSPOtKHzS7n/rHFXcXbSpOoX//'+\ 'aUKyTR6jEC8Xv678WjXC5KEkvziA==', 'signature': 'MEYCIQC5gM8inamJGUFKu3bNo4fT0jmJQuw33OSSXc242NCuiwIhAIWnVw2Sp'+\ 'ow72j6J92KaY2rLR6qSXEbLam09ZXbSkBnQ'} ) self.app.config['FIDO2_RP_ID'] = 'idp.dev.eduid.se' response = client.post('/post-action', data=data, content_type=self.content_type_json) self.assertEquals(response.status_code, 200) data = json.loads(response.data) self.assertEquals(len(self.app.actions_db.get_actions(self.user.eppn, 'mock-session')), 1)