Ejemplo n.º 1
0
 def test_unknown_input_data_allowed(self):
     one = copy.deepcopy(_one_dict)
     one['foo'] = 'bar'
     addr = Nin(data = one, raise_on_unknown = False)
     out = addr.to_dict()
     self.assertIn('foo', out)
     self.assertEqual(out['foo'], one['foo'])
Ejemplo n.º 2
0
    def test_proofing_flow_previously_added_wrong_nin(self):
        # Send letter to correct nin
        self.send_letter(self.test_user_nin)

        # Remove correct unverified nin and add wrong nin
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        user.nins.remove(self.test_user_nin)
        not_verified_nin = Nin.from_dict(
            dict(number=self.test_user_wrong_nin,
                 created_by='test',
                 verified=False,
                 primary=False))
        user.nins.add(not_verified_nin)
        self.app.central_userdb.save(user)

        # Time passes, user gets code in the mail. Enters code.
        proofing_state = self.app.proofing_statedb.get_state_by_eppn(user.eppn)
        response = self.verify_code(proofing_state.nin.verification_code, None)

        # Now check that the (now verified) NIN on the user is back to the one used to request the letter
        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.verified_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Ejemplo n.º 3
0
    def get_user_set_nins(self, eppn, ninlist):
        """
        Fetch a user from the FakeUserDb and set it's NINs to those in ninlist.
        :param eppn: eduPersonPrincipalName or email address
        :param ninlist: List of NINs to configure user with (all verified)

        :type eppn: str or unicode
        :type ninlist: [str or unicode]

        :return: IdPUser instance
        :rtype: IdPUser
        """
        user = self.idp_userdb.lookup_user(eppn)
        [user.nins.remove(x) for x in user.nins.to_list()]
        for number in ninlist:
            this_nin = Nin.from_dict(
                dict(
                    number=number,
                    created_by='unittest',
                    created_ts=True,
                    verified=True,
                    primary=user.nins.primary is None,
                ))
            user.nins.add(this_nin)
        return user
Ejemplo n.º 4
0
    def test_verify_existant_nin(self):
        # Add a non-verified NIN to the user with no NINs
        email = self.no_nin_user_email
        user = self.userdb_new.get_user_by_mail(email)
        for nin in user.nins.to_list():
            user.nins.remove(nin.number)
        new_nin = Nin(data={
            'number': '123456789050',
            'verified': False,
            'primary': False,
        })
        user.nins.add(new_nin)
        self.userdb_new.save(user)
        # Set up a pending verfication
        verification_data = {
            '_id': ObjectId(),
            'code': '123124',
            'model_name': 'norEduPersonNIN',
            'obj_id': '123456789050',
            'user_oid': user.user_id,
            'timestamp': datetime.utcnow(),
            'verified': False,
        }
        self.db.verifications.insert(verification_data)

        self.set_logged(email)

        response = self.testapp.post('/profile/nins-actions/', {
            'identifier': '123456789050  0',
            'action': 'verify'
        })

        response_json = json.loads(response.body)
        self.assertEqual(response_json['result'], 'getcode')
Ejemplo n.º 5
0
    def test_proofing_flow_previously_added_wrong_nin(self):
        json_data = self.get_state()
        csrf_token = json_data['payload']['csrf_token']
        # Send letter to correct nin
        self.send_letter(self.test_user_nin, csrf_token)

        # Remove correct unverified nin and add wrong nin
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        user.nins.remove(self.test_user_nin)
        not_verified_nin = Nin(number=self.test_user_wrong_nin, application='test', verified=False, primary=False)
        user.nins.add(not_verified_nin)
        self.app.central_userdb.save(user)

        json_data = self.get_state()
        csrf_token = json_data['payload']['csrf_token']
        with self.app.test_request_context():
            user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn, raise_on_missing=True)
            proofing_state = self.app.proofing_statedb.get_state_by_eppn(user.eppn, raise_on_missing=False)
        json_data = self.verify_code(proofing_state.nin.verification_code, csrf_token)
        self.assertTrue(json_data['payload']['success'])

        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by, proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.verified_by, proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Ejemplo n.º 6
0
    def test_create_oidcproofingstate(self):
        """
        {
            'eduPersonPrincipalName': 'foob-arra',
            'nin': {
                'created_ts': datetime.datetime(2016, 11, 16, 14, 47, 0, 379810),
                'verified': False,
                'number': '190101021234',
                'created_by': 'eduid_oidc_proofing'
            },
            'state': '2c84fedd-a694-46f0-b235-7c4dd7982852'
            'nonce': 'bbca50f6-5213-4784-b6e6-289bd1debda5',
            'token': 'de5b3f2a-14e9-49b8-9c78-a15fcf60d119',
        }
        """

        nin = Nin(number='200102034567',
                  application='eduid_oidc_proofing',
                  verified=False,
                  primary=False)
        state = OidcProofingState({
            'eduPersonPrincipalName':
            EPPN,
            'nin':
            nin.to_dict(),
            'state':
            '2c84fedd-a694-46f0-b235-7c4dd7982852',
            'nonce':
            'bbca50f6-5213-4784-b6e6-289bd1debda5',
            'token':
            'de5b3f2a-14e9-49b8-9c78-a15fcf60d119'
        })
        state_dict = state.to_dict()
        self.assertItemsEqual(state_dict.keys(), [
            '_id', 'nin', 'eduPersonPrincipalName', 'state', 'nonce', 'token'
        ])
        self.assertItemsEqual(
            state_dict['nin'].keys(),
            ['created_by', 'created_ts', 'number', 'verified'])
Ejemplo n.º 7
0
def _add_nin_to_user(new_nin, user, created_ts=None):
    """
    Add a NIN to a user.
    Part of set_nin_verified() above.
    """
    primary = user.nins.verified.count == 0
    new_nin_obj = Nin(number = new_nin,
                      application = 'dashboard',
                      verified = True,
                      primary = primary,
                      created_ts = created_ts,
                      )
    # Remove the NIN from the user if it is already there
    try:
        user.nins.remove(new_nin)
    except UserDBValueError:
        pass
    user.nins.add(new_nin_obj)
Ejemplo n.º 8
0
    def test_proofing_flow_previously_added_nin(self):
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        not_verified_nin = Nin.from_dict(
            dict(number=self.test_user_nin,
                 created_by='test',
                 verified=False,
                 primary=False))
        user.nins.add(not_verified_nin)
        self.app.central_userdb.save(user)

        self.send_letter(self.test_user_nin)
        proofing_state = self.app.proofing_statedb.get_state_by_eppn(user.eppn)
        self.verify_code(proofing_state.nin.verification_code, None)

        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by,
                         not_verified_nin.created_by)
        self.assertEqual(user.nins.primary.verified_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Ejemplo n.º 9
0
    def add_success_other(self, ninform):
        newnin = self.schema.serialize(ninform)
        newnin = newnin['norEduPersonNIN']

        newnin = normalize_nin(newnin)

        old_user = self.request.db.profiles.find_one({
            'norEduPersonNIN': newnin
            })

        if old_user:
            old_user = DashboardUser(data=old_user)
            retrieve_modified_ts(old_user, self.request.dashboard_userdb)
            old_user.nins.remove(newnin)
            self.context.save_dashboard_user(old_user)

        primary = False
        if self.user.nins.count == 0:
            primary = True
        newnin_obj = Nin(number=newnin, application='dashboard',
                verified=True, primary=primary)
        self.user.nins.add(newnin_obj)

        try:
            self.context.save_dashboard_user(self.user)
        except UserOutOfSync:
            message = _('Your user profile is out of sync. Please '
                        'reload the page and try again.')
        else:
            message = _('Your national identity number has been confirmed')
        # Save the state in the verifications collection
        save_as_verified(self.request, 'norEduPersonNIN', self.user, newnin)
        self.request.session.flash(
                get_localizer(self.request).translate(message),
                queue='forms')
        self.request.stats.count('nin_add_other')
Ejemplo n.º 10
0
    def test_freja_flow_previously_added_wrong_nin(self,
                                                   mock_request_user_sync,
                                                   mock_get_postal_address,
                                                   mock_oidc_call):
        mock_oidc_call.return_value = True
        mock_get_postal_address.return_value = self.mock_address
        mock_request_user_sync.side_effect = self.request_user_sync
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)

        not_verified_nin = Nin(number=self.test_user_wrong_nin,
                               application='test',
                               verified=False,
                               primary=False)
        user.nins.add(not_verified_nin)
        self.app.central_userdb.save(user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = json.loads(browser.get('/proofing').data)
        self.assertEqual(response['type'],
                         'GET_OIDC_PROOFING_PROOFING_SUCCESS')

        csrf_token = response['payload']['csrf_token']

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            data = {'nin': self.test_user_wrong_nin, 'csrf_token': csrf_token}
            response = browser.post('/freja/proofing',
                                    data=json.dumps(data),
                                    content_type=self.content_type_json)
            response = json.loads(response.data)
        self.assertEqual(response['type'],
                         'POST_OIDC_PROOFING_FREJA_PROOFING_SUCCESS')

        # No actual oidc flow tested here
        proofing_state = self.app.proofing_statedb.get_state_by_eppn(
            self.test_user_eppn)
        userinfo = {
            'results': {
                'freja_eid': {
                    'vetting_time':
                    time.time(),
                    'ref':
                    '1234.5678.9012.3456',
                    'opaque':
                    '1' + json.dumps({
                        'nonce': proofing_state.nonce,
                        'token': proofing_state.token
                    }),
                    'country':
                    'SE',
                    'ssn':
                    self.test_user_nin,
                }
            }
        }
        with self.app.app_context():
            handle_freja_eid_userinfo(user, proofing_state, userinfo)
        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.verified_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Ejemplo n.º 11
0
    def test_seleg_flow_previously_added_wrong_nin(self,
                                                   mock_request_user_sync,
                                                   mock_get_postal_address,
                                                   mock_oidc_call):
        mock_oidc_call.return_value = True
        mock_get_postal_address.return_value = self.mock_address
        mock_request_user_sync.side_effect = self.request_user_sync
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)

        not_verified_nin = Nin(number=self.test_user_wrong_nin,
                               application='test',
                               verified=False,
                               primary=False)
        user.nins.add(not_verified_nin)
        self.app.central_userdb.save(user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = json.loads(browser.get('/proofing').data)
        self.assertEqual(response['type'],
                         'GET_OIDC_PROOFING_PROOFING_SUCCESS')

        csrf_token = response['payload']['csrf_token']

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            data = {'nin': self.test_user_wrong_nin, 'csrf_token': csrf_token}
            response = browser.post('/proofing',
                                    data=json.dumps(data),
                                    content_type=self.content_type_json)
            response = json.loads(response.data)
        self.assertEqual(response['type'],
                         'POST_OIDC_PROOFING_PROOFING_SUCCESS')

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = json.loads(browser.get('/proofing').data)
        self.assertEqual(response['type'],
                         'GET_OIDC_PROOFING_PROOFING_SUCCESS')

        # Fake callback from OP
        qrdata = json.loads(response['payload']['qr_code'][1:])
        proofing_state = self.app.proofing_statedb.get_state_by_eppn(
            self.test_user_eppn)
        userinfo = {
            'identity': self.test_user_nin,
            'metadata': {
                'score':
                100,
                'opaque':
                '1' + json.dumps({
                    'nonce': proofing_state.nonce,
                    'token': proofing_state.token
                }),
                'ra_app':
                'App id for vetting app'
            }
        }
        self.mock_authorization_response(qrdata, proofing_state, userinfo)

        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.verified_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Ejemplo n.º 12
0
 def test_unknown_input_data(self):
     one = copy.deepcopy(_one_dict)
     one['foo'] = 'bar'
     with self.assertRaises(eduid_userdb.exceptions.UserHasUnknownData):
         Nin(data = one)
Ejemplo n.º 13
0
def authorization_response():
    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))
    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse,
                                                        info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug(
        'Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error(
            'AuthorizationError {!s} - {!s} ({!s})'.format(
                request.host, authn_resp['error'],
                authn_resp.get('error_message'), authn_resp.get('error_uri')))
        return make_response('OK', 200)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(
        user_oidc_state)
    if not proofing_state:
        msg = 'The \'state\' parameter ({!s}) does not match a user state.'.format(
            user_oidc_state)
        current_app.logger.error(msg)
        return make_response('OK', 200)
    current_app.logger.debug('Proofing state {!s} for user {!s} found'.format(
        proofing_state.state, proofing_state.eppn))

    # Check if the token from the QR code matches the token we created when making the auth request
    authorization_header = request.headers.get('Authorization')
    if authorization_header != 'Bearer {}'.format(proofing_state.token):
        current_app.logger.error(
            'The authorization token ({!s}) did not match the expected'.format(
                authorization_header))
        return make_response('FORBIDDEN', 403)

    # TODO: We should save the auth response code to the proofing state to be able to continue a failed attempt
    # do token request
    args = {
        'code':
        authn_resp['code'],
        'redirect_uri':
        url_for('oidc_proofing.authorization_response', _external=True)
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    # TODO: What and where should be save from the token response
    token_resp = current_app.oidc_client.do_access_token_request(
        scope='openid',
        state=authn_resp['state'],
        request_args=args,
        authn_method='client_secret_basic')
    current_app.logger.debug(
        'token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error(
            'The \'nonce\' parameter does not match for user {!s}.'.format(
                proofing_state.eppn))
        return make_response('OK', 200)

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    # TODO: Do we need to save anything else from the userinfo response
    userinfo = current_app.oidc_client.do_user_info_request(
        method=current_app.config['USERINFO_ENDPOINT_METHOD'],
        state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error(
            'The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'
            .format(proofing_state.eppn))
        return make_response('OK', 200)

    # Check proofed nin against self proclaimed OidcProofingState.nin.number
    number = userinfo['identity']
    if proofing_state.nin.number == number:
        nin = Nin(data=proofing_state.nin.to_dict())
        nin.verified = True

        # TODO: Break out in parts to be able to continue the proofing process after a successful authorization response
        # TODO: even if the token request, userinfo request or something internal fails
        am_user = current_app.central_userdb.get_user_by_eppn(
            proofing_state.eppn)
        user = ProofingUser(data=am_user.to_dict())

        # Check if the user has more than one verified nin
        if user.nins.primary is None:
            # No primary NIN found, make the only verified NIN primary
            nin.is_primary = True
        user.nins.add(nin)

        # XXX: Send proofing data to some kind of proofing log

        # User from central db is as up to date as it can be no need to check for modified time
        user.modified_ts = True
        # Save user to private db
        current_app.proofing_userdb.save(user, check_sync=False)

        # TODO: Need to decide where to "steal" NIN if multiple users have the NIN verified
        # Ask am to sync user to central db
        try:
            current_app.logger.info('Request sync for user {!s}'.format(user))
            result = current_app.am_relay.request_user_sync(user)
            current_app.logger.info('Sync result for user {!s}: {!s}'.format(
                user, result))
        except Exception as e:
            current_app.logger.error(
                'Sync request failed for user {!s}'.format(user))
            current_app.logger.error('Exception: {!s}'.format(e))
            # TODO: Need to able to retry this
            return make_response('OK', 200)

        # TODO: Remove saving of proof
        # Save proof for demo purposes
        proof_data = {
            'eduPersonPrincipalName': proofing_state.eppn,
            'authn_resp': authn_resp.to_dict(),
            'token_resp': token_resp.to_dict(),
            'userinfo': userinfo.to_dict()
        }
        current_app.proofdb.save(Proof(data=proof_data))

    # Remove users proofing state
    current_app.proofing_statedb.remove_state(proofing_state)
    return make_response('OK', 200)
Ejemplo n.º 14
0
def proofing(user, nin):

    current_app.logger.debug('Getting state for user {!s}.'.format(user))

    # TODO: Check if a user has a valid letter proofing
    # For now a user can just have one verified NIN
    if len(user.nins.to_list()) > 0:
        return {'_status': 'error', 'error': 'User is already verified'}

    proofing_state = current_app.proofing_statedb.get_state_by_eppn(
        user.eppn, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.debug(
            'No proofing state found for user {!s}. Initializing new proofing flow.'
            .format(user))
        state = get_unique_hash()
        nonce = get_unique_hash()
        token = get_unique_hash()
        nin = Nin(number=nin,
                  application='eduid_oidc_proofing',
                  verified=False,
                  primary=False)
        proofing_state = OidcProofingState({
            'eduPersonPrincipalName': user.eppn,
            'nin': nin.to_dict(),
            'state': state,
            'nonce': nonce,
            'token': token
        })
        # Initiate proofing
        oidc_args = {
            'client_id':
            current_app.oidc_client.client_id,
            'response_type':
            'code',
            'scope': ['openid'],
            'redirect_uri':
            url_for('oidc_proofing.authorization_response', _external=True),
            'state':
            state,
            'nonce':
            nonce,
            'claims':
            ClaimsRequest(userinfo=Claims(identity=None)).to_json()
        }
        current_app.logger.debug('AuthenticationRequest args:')
        current_app.logger.debug(oidc_args)
        try:
            response = requests.post(
                current_app.oidc_client.authorization_endpoint, data=oidc_args)
        except requests.exceptions.ConnectionError as e:
            msg = 'No connection to authorization endpoint: {!s}'.format(e)
            current_app.logger.error(msg)
            return {'_status': 'error', 'error': msg}
        # If authentication request went well save user state
        if response.status_code == 200:
            current_app.logger.debug(
                'Authentication request delivered to provider {!s}'.format(
                    current_app.config['PROVIDER_CONFIGURATION_INFO']
                    ['issuer']))
            current_app.proofing_statedb.save(proofing_state)
            current_app.logger.debug(
                'Proofing state {!s} for user {!s} saved'.format(
                    proofing_state.state, user))
        else:
            current_app.logger.error(
                'Bad response from OP: {!s} {!s} {!s}'.format(
                    response.status_code, response.reason, response.content))
            return {
                '_status': 'error',
                'error': 'Temporary technical problems'
            }
    # Return nonce and nonce as qr code
    current_app.logger.debug('Returning nonce for user {!s}'.format(user))
    buf = StringIO()
    # The "1" below denotes the version of the data exchanged, right now only version 1 is supported.
    qr_code = '1' + json.dumps({
        'nonce': proofing_state.nonce,
        'token': proofing_state.token
    })
    qrcode.make(qr_code).save(buf)
    qr_b64 = buf.getvalue().encode('base64')
    return {
        'qr_code': qr_code,
        'qr_img': 'data:image/png;base64, {!s}'.format(qr_b64),
    }
Ejemplo n.º 15
0
def verify_code(user, verification_code):
    user = ProofingUser(data=user.to_dict())
    current_app.logger.info('Verifying code for user {!r}'.format(user))
    proofing_state = current_app.proofing_statedb.get_state_by_eppn(
        user.eppn, raise_on_missing=False)

    if not proofing_state:
        return {'_status': 'error', 'message': 'No proofing state found'}

    # Check if provided code matches the one in the letter
    if not verification_code == proofing_state.nin.verification_code:
        current_app.logger.error(
            'Verification code for user {!r} does not match'.format(user))
        # TODO: Throttling to discourage an adversary to try brute force
        return {'_status': 'error', 'message': 'Wrong code'}

    # Update proofing state to use to create nin element
    proofing_state.nin.is_verified = True
    proofing_state.nin.verified_by = 'eduid-idproofing-letter'
    proofing_state.nin.verified_ts = True
    nin = Nin(data=proofing_state.nin.to_dict())

    # Save user to private db
    if user.nins.primary is None:  # No primary NIN found, make the only verified NIN primary
        nin.is_primary = True
    user.nins.add(nin)

    # XXX: Do not add letter_proofing_data after we update to new db models in central user db
    letter_proofing_data = proofing_state.nin.to_dict()
    letter_proofing_data[
        'official_address'] = proofing_state.proofing_letter.address
    letter_proofing_data[
        'transaction_id'] = proofing_state.proofing_letter.transaction_id
    user.add_letter_proofing_data(letter_proofing_data)

    # User from central db is as up to date as it can be no need to check for modified time
    user.modified_ts = True
    current_app.proofing_userdb.save(user, check_sync=False)

    # TODO: Need to decide where to "steal" NIN if multiple users have the NIN verified
    # Ask am to sync user to central db
    try:
        # XXX: Send proofing data to some kind of proofing log
        current_app.logger.info('Request sync for user {!s}'.format(user))
        result = current_app.am_relay.request_user_sync(user)
        current_app.logger.info('Sync result for user {!s}: {!s}'.format(
            user, result))
    except Exception as e:
        current_app.logger.error(
            'Sync request failed for user {!s}'.format(user))
        current_app.logger.error('Exception: {!s}'.format(e))
        # XXX: Probably not str(e) as message?
        return {'_status': 'error', 'message': 'Sync request failed for user'}

    # XXX: Remove dumping data to log
    current_app.logger.info('Logging data for user: {!r}'.format(user))
    current_app.logger.info(
        json.dumps(
            schemas.LetterProofingDataSchema().dump(letter_proofing_data)))
    current_app.logger.info('End data')

    current_app.logger.info('Verified code for user {!r}'.format(user))
    # Remove proofing state
    current_app.proofing_statedb.remove_document(
        {'eduPersonPrincipalName': proofing_state.eppn})
    return {'success': True}