def validate_2fa(name):
    form = forms.SignTokenForm()
    if form.validate_on_submit():
        errorCode = json.loads(form.response.data)['errorCode']
        if errorCode != 0:
            flash("Token authentication failed", "error")
            return redirect(url_for('select_2fa'))
        device, c, t = complete_authentication(session['u2f_sign'],
                                               form.response.data, app_id)
        if t != 1:
            flash("Token authentication failed", "error")
            return redirect(url_for('select_2fa'))
        # Log in the user
        user = load_user(session['user'])
        flask_login.login_user(user)
        flash("Login complete", "success")
        return redirect(url_for('index'))
    key = models.U2FCredentials.query.filter_by(owner=session['user'],
                                                name=name).first()
    sign = begin_authentication(app_id, [key.device])
    session['u2f_sign'] = sign.json
    challenge = sign['challenge']
    registeredKeys = json.loads(sign['registeredKeys'][0])
    version = registeredKeys['version']
    keyHandle = registeredKeys['keyHandle']

    return render_template('validate_2fa.html',
                           challenge=challenge,
                           version=version,
                           keyHandle=keyHandle,
                           app_id=app_id,
                           form=form,
                           key=key)
Beispiel #2
0
def login():
    devices = [doc['device'] for doc in DB.u2f.find()]
    u2f_enabled = True if devices else False
    if request.method == 'POST':
        csrf.protect()
        pwd = request.form.get('pass')
        if pwd and verify_pass(pwd):
            if devices:
                resp = json.loads(request.form.get('resp'))
                print(resp)
                try:
                    u2f.complete_authentication(session['challenge'], resp)
                except ValueError as exc:
                    print('failed', exc)
                    abort(401)
                    return
                finally:
                    session['challenge'] = None

            session['logged_in'] = True
            return redirect(request.args.get('redirect') or '/admin')
        else:
            abort(401)

    payload = None
    if devices:
        payload = u2f.begin_authentication(ID, devices)
        session['challenge'] = payload

    return render_template(
        'login.html',
        u2f_enabled=u2f_enabled,
        me=ME,
        payload=payload,
    )
Beispiel #3
0
def login(request):
    username = request.data.get('username')
    password = request.data.get('password')

    user = authenticate(username=username, password=password)
    if not user:
        return Response(
            {"error": "Login failed"},
            status=HTTP_401_UNAUTHORIZED
        )

    if user.u2f_key.exists():
        if user.u2f_authentication_request.exists():
            user.u2f_authentication_request.first().delete()

        serializer = U2FKeySerializer(user.u2f_key.first())

        sign_request = u2f.begin_authentication(
            settings.APP_ID, [serializer.data])
        U2FAuthenticationRequest.objects.create(
            user=user, body=json.dumps(sign_request))
        return Response(sign_request.data_for_client)
    else:
        token, _ = Token.objects.get_or_create(user=user)
        return Response({"token": token.key})
 def __init__(self, user, initial_device, request, **kwargs):
     """
     `initial_device` is either the user's default device, or the backup
     device when the user chooses to enter a backup token. The token will
     be verified against all devices, it is not limited to the given
     device.
     """
     super(AuthenticationTokenForm, self).__init__(**kwargs)
     self.user = user
     self.request = request
     self.initial_device = initial_device
     if request.META['HTTP_X_FORWARDED_HOST']:
         self.appId = '{scheme}://{host}'.format(scheme='https' if self.request.is_secure() else 'http',
                                                 host=request.META['HTTP_X_FORWARDED_HOST']).rstrip('/')
     else:
         self.appId = '{scheme}://{host}'.format(
             scheme='https' if self.request.is_secure() else 'http', host=self.request.get_host()).rstrip('/')
     # YubiKey generates a OTP of 44 characters (not digits). So if the
     # user's primary device is a YubiKey, replace the otp_token
     # IntegerField with a CharField.
     if RemoteYubikeyDevice and YubikeyDevice and \
             isinstance(initial_device, (RemoteYubikeyDevice, YubikeyDevice)):
         self.fields['otp_token'] = forms.CharField(label=_('YubiKey'), widget=forms.PasswordInput())
     elif isinstance(initial_device, U2FDevice):
         self.fields['otp_token'] = forms.CharField(label=_('Token'))
         if self.data:
             self.sign_request = self.request.session['u2f_sign_request']
         else:
             self.sign_request = u2f.begin_authentication(self.appId, [key.to_json() for key in user.u2f_keys.all()])
             self.request.session['u2f_sign_request'] = self.sign_request
Beispiel #5
0
def sign(username):
    u2f_devices = [
        d.properties["device"]
        for d in User_Keys.objects.filter(username=username, key_type="U2F")
    ]
    challenge = begin_authentication(settings.U2F_APPID, u2f_devices)
    return [challenge.json, simplejson.dumps(challenge.data_for_client)]
Beispiel #6
0
    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 sign(self, username):
     if username not in self.users:
         return json.dumps(False)
     user = self.users[username]
     challenge = begin_authentication(self.app_id,
                                      user.get('_u2f_devices_', []))
     user['_u2f_challenge_'] = challenge.json
     return json.dumps(challenge.data_for_client)
Beispiel #8
0
 def __init__(self, *args, **kwargs):
     super(KeyResponseForm, self).__init__(*args, **kwargs)
     if self.data:
         self.sign_request = self.request.session['u2f_sign_request']
     else:
         self.sign_request = u2f.begin_authentication(
             self.appId, [d.to_json() for d in self.user.u2f_keys.all()])
         self.request.session['u2f_sign_request'] = self.sign_request
Beispiel #9
0
 def set_u2f_challenge(self):
     user_devices = [ud.device for ud in self.user.useru2f_set.all()]
     if user_devices:
         authentication_request = begin_authentication(
             zentral_settings["api"]["tls_hostname"], user_devices)
         u2f_challenge = self.session["u2f_challenge"] = dict(
             authentication_request)
         return u2f_challenge
Beispiel #10
0
def _get_u2f_request(request, u2f_key):
  origin = get_origin(request)
  u2f_request = u2f.begin_authentication(u2f_key.app_id, [{
    'publicKey': u2f_key.public_key,
    'keyHandle': u2f_key.key_handle,
    'appId': u2f_key.app_id,
    'version': 'U2F_V2'}])
  return u2f_request
def sign():
    app_id = get_origin(request.environ)
    username = request.args.get('username', 'user')
    data = request.data.decode()
    user = users[username]
    challenge = begin_authentication(app_id, user.get('_u2f_devices_', []),
                                     data)
    user['_u2f_challenge_'] = challenge.json
    return json.dumps(challenge.data_for_client)
Beispiel #12
0
 def _u2f_get_login_challenge(self):
     self.ensure_one()
     icp = self.env['ir.config_parameter'].sudo()
     baseurl = icp.get_param('web.base.url')
     devices = self._u2f_get_device()
     if devices:
         challenge = u2f.begin_authentication(baseurl, [devices.json])
         return challenge
     return False
Beispiel #13
0
 def __init__(self, *args, **kwargs):
     self.user = kwargs.pop('user')
     super(U2FTokenAuthenticationForm, self).__init__(*args, **kwargs)
     self.helper.form_class = 'u2f-challenge'
     challenge = begin_authentication(APP_ID, get_user_devices(self.user))
     self.fields['u2f_challenge'].initial = json.dumps(
         challenge.data_for_client)
     self.fields['u2f_challenge_signed'].initial = signing.dumps(
         challenge, salt=self._get_sign_salt())
Beispiel #14
0
 def __init__(self, *args, **kwargs):
     super(KeyResponseForm, self).__init__(*args, **kwargs)
     if self.data:
         self.sign_request = self.request.session['u2f_sign_request']
     else:
         self.sign_request = u2f.begin_authentication(self.appId, [
             d.to_json() for d in self.user.u2f_keys.all()
         ])
         self.request.session['u2f_sign_request'] = self.sign_request
Beispiel #15
0
 def generate_challenge(self):
     challenge = begin_authentication(
         settings.OTP_U2F_APP_ID,
         [key.as_device_registration() for key in self.u2fkey_set.all()])
     U2FChallenge.objects.create(
         device=self,
         challenge=challenge.data_for_client['challenge'],
         challenge_data=challenge.json)
     return challenge.data_for_client
    def get_context_data(self, **kwargs):
        challenge = u2f.begin_authentication(
            mf_settings['U2F_APPID'],
            list(self.keys.values_list("properties__device", flat=True)))

        self.request.session["_u2f_challenge_"] = challenge.json

        return {
            'token': json.dumps(challenge.data_for_client),
        }
Beispiel #17
0
def sign(user):
    user_u2f_tokens = user.credentials.filter(U2F)
    if not user_u2f_tokens.count:
        current_app.logger.error('Found no U2F token for user.')
        return {'_error': True, 'message': 'security.u2f.no_token_found'}
    registered_keys = credentials_to_registered_keys(user_u2f_tokens)
    challenge = begin_authentication(current_app.config['U2F_APP_ID'], registered_keys)
    session['_u2f_challenge_'] = challenge.json
    current_app.stats.count(name='u2f_sign')
    return U2FSignResponseSchema().load(challenge.data_for_client).data
Beispiel #18
0
def sign(user):
    user_u2f_tokens = user.credentials.filter(U2F)
    if not user_u2f_tokens.count:
        current_app.logger.error('Found no U2F token for user.')
        return error_response(message=SecurityMsg.no_u2f)

    registered_keys = credentials_to_registered_keys(user_u2f_tokens)
    challenge = begin_authentication(current_app.config.u2f_app_id,
                                     registered_keys)
    session['_u2f_challenge_'] = challenge.json
    current_app.stats.count(name='u2f_sign')
    return U2FSignResponseSchema().load(challenge.data_for_client)
Beispiel #19
0
    def test_authenticate_single_soft_u2f(self):
        # Register
        device, token = register_token()

        # Authenticate
        request = begin_authentication(APP_ID, [device])
        data = request.data_for_client

        response = token.getAssertion(FACET, data['appId'], data['challenge'],
                                      data['registeredKeys'][0])

        complete_authentication(request.json, response)
Beispiel #20
0
    def sign(self, user_name, object_dn):

        # Do we have read permissions for the requested attribute
        self.__check_acl(user_name, object_dn, "r")

        uuid = self.__dn_to_uuid(object_dn)
        user_settings = self.__settings[uuid] if uuid in self.__settings else {}
        devices = [DeviceRegistration.wrap(device)
                   for device in user_settings.get('_u2f_devices_', [])]
        challenge = begin_authentication(self.app_id, devices)
        user_settings['_u2f_challenge_'] = challenge.json
        self.__save_settings()
        return challenge.json
Beispiel #21
0
    def sign(self, user_name, object_dn):

        # Do we have read permissions for the requested attribute
        self.__check_acl(user_name, object_dn, "r")

        uuid = self.__dn_to_uuid(object_dn)
        user_settings = self.__settings[
            uuid] if uuid in self.__settings else {}
        devices = [
            DeviceRegistration.wrap(device)
            for device in user_settings.get('_u2f_devices_', [])
        ]
        challenge = begin_authentication(self.app_id, devices)
        user_settings['_u2f_challenge_'] = challenge.json
        self.__save_settings()
        return challenge.json
Beispiel #22
0
    def activate(self, request):
        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)
Beispiel #23
0
    def test_authenticate_single_soft_u2f(self):
        # Register
        device, token = register_token()

        # Authenticate
        request = begin_authentication(APP_ID, [device])
        data = request.data_for_client

        response = token.getAssertion(
            FACET,
            data['appId'],
            data['challenge'],
            data['registeredKeys'][0]
        )

        complete_authentication(request.json, response)
Beispiel #24
0
    def activate(self, request):
        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)
Beispiel #25
0
def admin_login() -> _Response:
    if session.get("logged_in") is True:
        return redirect(url_for("admin.admin_notifications"))

    devices = [doc["device"] for doc in DB.u2f.find()]
    u2f_enabled = True if devices else False
    if request.method == "POST":
        csrf.protect()
        # 1. Check regular password login flow
        pwd = request.form.get("pass")
        if pwd:
            if verify_pass(pwd):
                session.permanent = True
                session["logged_in"] = True
                return redirect(
                    request.args.get("redirect")
                    or url_for("admin.admin_notifications"))
            else:
                abort(403)
        # 2. Check for U2F payload, if any
        elif devices:
            resp = json.loads(request.form.get("resp"))  # type: ignore
            try:
                u2f.complete_authentication(session["challenge"], resp)
            except ValueError as exc:
                print("failed", exc)
                abort(403)
                return
            finally:
                session["challenge"] = None

            session.permanent = True
            session["logged_in"] = True
            return redirect(
                request.args.get("redirect")
                or url_for("admin.admin_notifications"))
        else:
            abort(401)

    payload = None
    if devices:
        payload = u2f.begin_authentication(ID, devices)
        session["challenge"] = payload

    return htmlify(
        render_template("login.html", u2f_enabled=u2f_enabled,
                        payload=payload))
Beispiel #26
0
def _sign_request(user_id, challenge, handles, properties):
    client = get_client()
    user = get_user(user_id)
    if user is None or len(user.devices) == 0:
        app.logger.info('User "%s" has no devices registered', user_id)
        raise exc.NoEligibleDevicesException('No devices registered', [])

    registered_keys = []
    descriptors = []
    handle_map = {}

    if not handles:
        handles = user.devices.keys()

    for handle in handles:
        try:
            dev = user.devices[handle]
        except KeyError:
            raise exc.BadInputException('Invalid device handle: ' + handle)
        if not dev.compromised:
            descriptor = dev.get_descriptor(get_metadata(dev))
            descriptors.append(descriptor)
            key = _get_registered_key(dev, descriptor)
            registered_keys.append(key)
            handle_map[key['keyHandle']] = dev.handle

    if not registered_keys:
        raise exc.NoEligibleDevicesException(
            'All devices compromised',
            [d.get_descriptor() for d in user.devices.values()]
        )

    request_data = begin_authentication(
        client.app_id,
        registered_keys,
        challenge
    )
    request_data['handleMap'] = handle_map
    request_data['properties'] = properties

    store.store(client.id, user_id, challenge, request_data.json)
    data = SignRequestData.wrap(request_data.data_for_client)
    data['descriptors'] = descriptors
    return data
Beispiel #27
0
def sign(username, **_):
    """
    Start signin in procedure

    Variables:
    username     user name of the user you want to login with

    Arguments:
    None

    Data Block:
    None

    Result example:
    <U2F_SIGN_IN_CHALLENGE_BLOCK>
    """
    user = STORAGE.get_user(username)
    if not user:
        return make_api_response({'success': False}, err="Bad Request", status_code=400)

    challenge = begin_authentication(APP_ID, user.get('u2f_devices', []))
    session['_u2f_challenge_'] = challenge.json

    return make_api_response(challenge.data_for_client)
Beispiel #28
0
    def get_config_for_bundle(self, action):
        if action.old_format:
            userid = action.user_id
            user = current_app.central_userdb.get_user_by_id(
                userid, raise_on_missing=False)
        else:
            eppn = action.eppn
            user = current_app.central_userdb.get_user_by_eppn(
                eppn, raise_on_missing=False)
        current_app.logger.debug('Loaded User {} from db'.format(user))
        if not user:
            raise self.ActionError('mfa.user-not-found')

        credentials = _get_user_credentials(user)
        current_app.logger.debug('FIDO credentials for user {}:\n{}'.format(user, pprint.pformat(credentials)))

        # CTAP1/U2F
        # TODO: Only make U2F challenges for U2F tokens?
        challenge = None
        if current_app.config.get('GENERATE_U2F_CHALLENGES') is True:
            u2f_tokens = [v['u2f'] for v in credentials.values()]
            try:
                challenge = begin_authentication(current_app.config['U2F_APP_ID'], u2f_tokens)
                current_app.logger.debug('U2F challenge:\n{}'.format(pprint.pformat(challenge)))
            except ValueError:
                # there is no U2F key registered for this user
                pass

        # CTAP2/Webauthn
        # TODO: Only make Webauthn challenges for Webauthn tokens?
        webauthn_credentials = [v['webauthn'] for v in credentials.values()]
        fido2rp = RelyingParty(current_app.config['FIDO2_RP_ID'], 'eduID')
        fido2server = _get_fido2server(credentials, fido2rp)
        raw_fido2data, fido2state = fido2server.authenticate_begin(webauthn_credentials)
        current_app.logger.debug('FIDO2 authentication data:\n{}'.format(pprint.pformat(raw_fido2data)))
        fido2data = base64.urlsafe_b64encode(cbor.dumps(raw_fido2data)).decode('ascii')
        fido2data = fido2data.rstrip('=')

        config = {'u2fdata': '{}', 'webauthn_options': fido2data}

        # Save the challenge to be used when validating the signature in perform_action() below
        if challenge is not None:
            session[self.PACKAGE_NAME + '.u2f.challenge'] = challenge.json
            config['u2fdata'] = json.dumps(challenge.data_for_client)
            current_app.logger.debug(f'FIDO1/U2F challenge for user {user}: {challenge.data_for_client}')

        current_app.logger.debug(f'FIDO2/Webauthn state for user {user}: {fido2state}')
        session[self.PACKAGE_NAME + '.webauthn.state'] = json.dumps(fido2state)

        # Explicit check for boolean True
        if current_app.config.get('MFA_TESTING') is True:
            current_app.logger.info('MFA test mode is enabled')
            config['testing'] = True
        else:
            config['testing'] = False

        # Add config for external mfa auth
        config['eidas_url'] = current_app.config['EIDAS_URL']
        config['mfa_authn_idp'] = current_app.config['MFA_AUTHN_IDP']

        return config
 def sign(self, username):
     user = self.users[username]
     challenge = begin_authentication(
         self.app_id, user.get('_u2f_devices_', []))
     user['_u2f_challenge_'] = challenge.json
     return json.dumps(challenge.data_for_client)
Beispiel #30
0
 def _u2f_get_login_challenge(self):
     baseurl = self.env['ir.config_parameter'].sudo().get_param(
         'web.base.url')
     challenge = begin_authentication(baseurl,
                                      [self._u2f_get_device().json])
     return challenge
Beispiel #31
0
    def get_config_for_bundle(self, action):
        if action.old_format:
            userid = action.user_id
            user = current_app.central_userdb.get_user_by_id(
                userid, raise_on_missing=False)
        else:
            eppn = action.eppn
            user = current_app.central_userdb.get_user_by_eppn(
                eppn, raise_on_missing=False)
        current_app.logger.debug('Loaded User {} from db'.format(user))
        if not user:
            raise self.ActionError('mfa.user-not-found')

        credentials = _get_user_credentials(user)
        current_app.logger.debug('FIDO credentials for user {}:\n{}'.format(
            user, pprint.pformat(credentials)))

        # CTAP1/U2F
        # TODO: Only make U2F challenges for U2F tokens?
        challenge = None
        if current_app.config.get('GENERATE_U2F_CHALLENGES') is True:
            u2f_tokens = [v['u2f'] for v in credentials.values()]
            try:
                challenge = begin_authentication(
                    current_app.config['U2F_APP_ID'], u2f_tokens)
                current_app.logger.debug('U2F challenge:\n{}'.format(
                    pprint.pformat(challenge)))
            except ValueError:
                # there is no U2F key registered for this user
                pass

        # CTAP2/Webauthn
        # TODO: Only make Webauthn challenges for Webauthn tokens?
        webauthn_credentials = [v['webauthn'] for v in credentials.values()]
        fido2rp = RelyingParty(current_app.config['FIDO2_RP_ID'], 'eduID')
        fido2server = _get_fido2server(credentials, fido2rp)
        raw_fido2data, fido2state = fido2server.authenticate_begin(
            webauthn_credentials)
        current_app.logger.debug('FIDO2 authentication data:\n{}'.format(
            pprint.pformat(raw_fido2data)))
        fido2data = base64.urlsafe_b64encode(
            cbor.dumps(raw_fido2data)).decode('ascii')
        fido2data = fido2data.rstrip('=')

        config = {'u2fdata': '{}', 'webauthn_options': fido2data}

        # Save the challenge to be used when validating the signature in perform_action() below
        if challenge is not None:
            session[self.PACKAGE_NAME + '.u2f.challenge'] = challenge.json
            config['u2fdata'] = json.dumps(challenge.data_for_client)
            current_app.logger.debug(
                f'FIDO1/U2F challenge for user {user}: {challenge.data_for_client}'
            )

        current_app.logger.debug(
            f'FIDO2/Webauthn state for user {user}: {fido2state}')
        session[self.PACKAGE_NAME + '.webauthn.state'] = json.dumps(fido2state)

        # Explicit check for boolean True
        if current_app.config.get('MFA_TESTING') is True:
            current_app.logger.info('MFA test mode is enabled')
            config['testing'] = True
        else:
            config['testing'] = False

        # Add config for external mfa auth
        config['eidas_url'] = current_app.config['EIDAS_URL']
        config['mfa_authn_idp'] = current_app.config['MFA_AUTHN_IDP']

        return config