예제 #1
0
def login():
    challenge = session.get('challenge')
    credential_id = request.json.get('id')
    user_handle = request.json.get('userHandle')
    client_data = request.json.get('clientData')
    auth_data = request.json.get('authData')
    signature = request.json.get('signature')
    assertion_client_extensions = request.json.get('assertionClientExtensions')

    user = next((u for u in ADMIN_USERS.values() if u.credential_id == credential_id), None)
    if user is None:
        logger.info(f"Login start failed: no user with credential ID {credential_id}")
        return jsonify(status='failure', error='User with given credential_id not found')
    logger.info(f"Login submitted for user {user.username} with challenge {challenge}")

    webauthn_user = webauthn.WebAuthnUser(user_id=user.id, username=user.username, display_name=user.username, icon_url=ICON_URL, credential_id=user.credential_id, public_key=user.public_key, sign_count=0, rp_id=RELYING_PARTY_ID)
    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user=webauthn_user,
        assertion_response={'id': user.credential_id, 'userHandle': user_handle, 'clientData': client_data, 'authData': auth_data, 'signature': signature, 'assertionClientExtensions': assertion_client_extensions},
        challenge=challenge,
        origin=ORIGIN,
    )
    try:
        webauthn_assertion_response.verify()
    except Exception as e:
        logger.info(f"Login failed: {e}")
        return jsonify(status='failure', error=str(e))

    logger.info(f"Login completed for user {user.username}")
    login_user(user, remember=False)
    return jsonify(status='success')
예제 #2
0
def verify_assertion(request):
    print("verify_assertion")
    challenge = request.session.get('challenge', False)
    assertion_response = request.POST
    credential_id = assertion_response.get('id')

    user = User.objects.filter(credential_id=credential_id).first()
    if not user:
        return JsonResponse({'fail': 'User does not exist.'})
    webauthn_user = webauthn.WebAuthnUser(
        user.ukey, user.username, user.display_name, user.icon_url,
        user.credential_id, user.pub_key, user.sign_count, user.rp_id)

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        ORIGIN,
        uv_required=False)  # User Verification
    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return JsonResponse({'fail': 'Assertion failed. Error: {}'.format(e)})
    # Update counter.
    user.sign_count = sign_count
    user.save()

    login(request, user)

    return JsonResponse({
        'success':
            'Successfully authenticated as {}'.format(user.username)
    })
예제 #3
0
    def post(self, request, *args, **kwargs):
        r = request.POST.get("webauthn", "")
        valid = False

        if 'webauthn_challenge' in self.request.session and r.startswith('{'):
            challenge = self.request.session['webauthn_challenge']

            resp = json.loads(r)
            try:
                devices = [
                    WebAuthnDevice.objects.get(user=self.request.user,
                                               credential_id=resp.get("id"))
                ]
            except WebAuthnDevice.DoesNotExist:
                devices = U2FDevice.objects.filter(user=self.request.user)

            for d in devices:
                try:
                    wu = d.webauthnuser

                    if isinstance(d, U2FDevice):
                        # RP_ID needs to be appId for U2F devices, but we can't
                        # set it that way in U2FDevice.webauthnuser, since that
                        # breaks the frontend part.
                        wu.rp_id = settings.SITE_URL

                    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
                        wu,
                        resp,
                        challenge,
                        settings.SITE_URL,
                        uv_required=False  # User Verification
                    )
                    sign_count = webauthn_assertion_response.verify()
                except Exception:
                    logger.exception('U2F login failed')
                else:
                    if isinstance(d, WebAuthnDevice):
                        d.sign_count = sign_count
                        d.save()
                    valid = True
                    break

        valid = valid or self.form.is_valid()

        if valid:
            t = int(time.time())
            request.session['pretix_auth_login_time'] = t
            request.session['pretix_auth_last_used'] = t
            next_url = get_auth_backends()[
                request.user.auth_backend].get_next_url(request)
            if next_url and url_has_allowed_host_and_scheme(
                    next_url, allowed_hosts=None):
                return redirect(next_url)
            return redirect(reverse('control:index'))
        else:
            messages.error(
                request,
                _('The password you entered was invalid, please try again.'))
            return self.get(request, *args, **kwargs)
예제 #4
0
def login():
    challenge = session.get('challenge')
    credential_id = request.json.get('id')
    user_handle = request.json.get('userHandle')
    client_data = request.json.get('clientData')
    auth_data = request.json.get('authData')
    signature = request.json.get('signature')
    assertion_client_extensions = request.json.get('assertionClientExtensions')

    user = next((u for u in USERS.values() if u['credential_id'] == credential_id), None)
    if user is None:
        return jsonify(status='failure', error='User with given credential ID not found')

    user_id, username, credential_id, public_key = user['id'], user['username'], user['credential_id'], user['public_key']
    webauthn_user = webauthn.WebAuthnUser(user_id=user_id, username=username, display_name=username, icon_url=ICON_URL, credential_id=credential_id, public_key=public_key, sign_count=0, rp_id=RELYING_PARTY_ID)
    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user=webauthn_user,
        assertion_response={'id': credential_id, 'userHandle': user_handle, 'clientData': client_data, 'authData': auth_data, 'signature': signature, 'assertionClientExtensions': assertion_client_extensions},
        challenge=challenge,
        origin=ORIGIN,
    )
    try:
        webauthn_assertion_response.verify()
    except Exception as e:
        return jsonify(status='failure', error=str(e))

    return jsonify(status='success', user=user)
예제 #5
0
def verify_assertion_response(assertion, *, challenge, user, origin, icon_url,
                              rp_id):
    """
    Validates the challenge and assertion information
    sent from the client during authentication.

    Returns an updated signage count on success.
    Raises AuthenticationRejectedException on failure.
    """
    webauthn_users = _get_webauthn_users(user, icon_url=icon_url, rp_id=rp_id)
    cred_ids = [cred.credential_id for cred in webauthn_users]

    for webauthn_user in webauthn_users:
        response = pywebauthn.WebAuthnAssertionResponse(
            webauthn_user,
            assertion,
            _webauthn_b64encode(challenge.encode()).decode(),
            origin,
            allow_credentials=cred_ids,
        )
        try:
            return (webauthn_user.credential_id, response.verify())
        except _AuthenticationRejectedException:
            pass

    # If we exit the loop, then we've failed to verify the assertion against
    # any of the user's WebAuthn credentials. Fail.
    raise AuthenticationRejectedException("Invalid WebAuthn credential")
예제 #6
0
def verify_assertion():
    challenge = session.get('challenge')
    assertion_response = request.form
    credential_id = assertion_response.get('id')

    user = User.query.filter_by(credential_id=credential_id).first()
    if not user:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)

    webauthn_user = webauthn.WebAuthnUser(user.ukey, user.username,
                                          user.display_name, user.icon_url,
                                          user.credential_id, user.pub_key,
                                          user.sign_count, user.rp_id)

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        ORIGIN,
        uv_required=False)  # User Verification

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)})

    # Update counter.
    user.sign_count = sign_count
    db.session.add(user)
    db.session.commit()

    login_user(user)

    return jsonify(
        {'success': 'Successfully authenticated as {}'.format(user.username)})
예제 #7
0
파일: auth.py 프로젝트: zjw2011/pretix
    def post(self, request, *args, **kwargs):
        token = request.POST.get('token', '').strip().replace(' ', '')

        valid = False
        if 'webauthn_challenge' in self.request.session and token.startswith(
                '{'):
            challenge = self.request.session['webauthn_challenge']

            resp = json.loads(self.request.POST.get("token"))
            try:
                devices = [
                    WebAuthnDevice.objects.get(user=self.user,
                                               credential_id=resp.get("id"))
                ]
            except WebAuthnDevice.DoesNotExist:
                devices = U2FDevice.objects.filter(user=self.user)

            for d in devices:
                try:
                    wu = d.webauthnuser

                    if isinstance(d, U2FDevice):
                        # RP_ID needs to be appId for U2F devices, but we can't
                        # set it that way in U2FDevice.webauthnuser, since that
                        # breaks the frontend part.
                        wu.rp_id = settings.SITE_URL

                    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
                        wu,
                        resp,
                        challenge,
                        settings.SITE_URL,
                        uv_required=False  # User Verification
                    )
                    sign_count = webauthn_assertion_response.verify()
                except Exception:
                    logger.exception('U2F login failed')
                else:
                    if isinstance(d, WebAuthnDevice):
                        d.sign_count = sign_count
                        d.save()
                    valid = True
                    break
        else:
            valid = match_token(self.user, token)

        if valid:
            auth_login(request, self.user)
            request.session['pretix_auth_login_time'] = int(time.time())
            del request.session['pretix_auth_2fa_user']
            del request.session['pretix_auth_2fa_time']
            if "next" in request.GET and is_safe_url(request.GET.get("next"),
                                                     allowed_hosts=None):
                return redirect(request.GET.get("next"))
            return redirect(reverse('control:index'))
        else:
            messages.error(request, _('Invalid code, please try again.'))
            return redirect('control:auth.login.2fa')
예제 #8
0
    def clean(self):
        totp_or_scratch_code = self.cleaned_data.get('totp_or_scratch_code')
        if self.profile.is_webauthn_enabled and self.cleaned_data.get(
                'webauthn_response'):
            if len(self.cleaned_data['webauthn_response']) > 65536:
                raise ValidationError(_('Invalid WebAuthn response.'))

            if not self.webauthn_challenge:
                raise ValidationError(_('No WebAuthn challenge issued.'))

            response = json.loads(self.cleaned_data['webauthn_response'])
            try:
                credential = self.profile.webauthn_credentials.get(
                    cred_id=response.get('id', ''))
            except WebAuthnCredential.DoesNotExist:
                raise ValidationError(_('Invalid WebAuthn credential ID.'))

            user = credential.webauthn_user
            # Work around a useless check in the webauthn package.
            user.credential_id = credential.cred_id
            assertion = webauthn.WebAuthnAssertionResponse(
                webauthn_user=user,
                assertion_response=response.get('response'),
                challenge=self.webauthn_challenge,
                origin=self.webauthn_origin,
                uv_required=False,
            )

            try:
                sign_count = assertion.verify()
            except Exception as e:
                raise ValidationError(str(e))

            credential.counter = sign_count
            credential.save(update_fields=['counter'])
        elif totp_or_scratch_code:
            if self.profile.is_totp_enabled and self.profile.check_totp_code(
                    totp_or_scratch_code):
                return
            elif self.profile.scratch_codes and totp_or_scratch_code in json.loads(
                    self.profile.scratch_codes):
                scratch_codes = json.loads(self.profile.scratch_codes)
                scratch_codes.remove(totp_or_scratch_code)
                self.profile.scratch_codes = json.dumps(scratch_codes)
                self.profile.save(update_fields=['scratch_codes'])
                return
            elif self.profile.is_totp_enabled:
                raise ValidationError(
                    _('Invalid two-factor authentication token or scratch code.'
                      ))
            else:
                raise ValidationError(_('Invalid scratch code.'))
        else:
            raise ValidationError(
                _('Must specify either totp_token or webauthn_response.'))
예제 #9
0
def login():
    claims = current_custom_claims()
    if not claims.get("is_login", False):
        return "", 401

    data = LoginSchema().load(request.get_json())

    email = claims["email"]
    credential_id = data["id"]

    authenticator = Authenticator.query.filter(
        Authenticator.credential_id == credential_id,
        Authenticator.user.has(email=email),
    ).first()

    if not authenticator:
        return {"error": "authenticator not found"}, 401

    webauthn_user = webauthn.WebAuthnUser(
        user_id=authenticator.user.id,
        username=authenticator.user.email,
        display_name=authenticator.user.name,
        icon_url=app.config["WEBAUTHN_ICON_URL"],
        credential_id=authenticator.credential_id,
        public_key=authenticator.public_key,
        sign_count=authenticator.sign_count,
        rp_id=app.config["WEBAUTHN_RP_ID"],
    )

    response = webauthn.WebAuthnAssertionResponse(
        webauthn_user=webauthn_user,
        challenge=claims["challenge"],
        assertion_response=data,
        origin=app.config["WEBAUTHN_ORIGIN"],
    )

    try:
        sign_count = response.verify()
    except Exception as exc:
        return {"error": str(exc)}, 401

    authenticator.sign_count = sign_count
    authenticator.last_used_at = datetime.now(timezone.utc)
    db.session.add(authenticator)
    db.session.commit()

    return (
        {
            "token": guard.encode_jwt_token(authenticator.user),
            "user": UserSchema().dump(authenticator.user),
            "authenticator": AuthenticatorSchema().dump(authenticator),
        },
        200,
    )
예제 #10
0
def verify_assertion():
    jsonData = request.get_json()
    AuthenticatorAttestationResponse = jsonData['AuthenticatorAttestationResponse']
    clientDataJSON = AuthenticatorAttestationResponse['clientDataJSON']
    clientDataJSON_padding = clientDataJSON.ljust((int)(math.ceil(len(clientDataJSON) / 4)) * 4, '=')
    clientDataJSON = base64.b64decode(clientDataJSON_padding).decode('utf8')
    clientDataJSONparsed = json.loads(clientDataJSON)
    retrievedChallenge = clientDataJSONparsed['challenge']
    try:
        data = database.query_db("select * from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge])[0]
    except:
        return jsonify({"Error:","Could not find challenge"}),500
    #DELETE from table
    database.delete_db("delete from PublicKeyCredentialCreationOptions where challenge=?",[retrievedChallenge])
    signature = AuthenticatorAttestationResponse['signature']
    assertion_response =  {'clientData':AuthenticatorAttestationResponse['clientDataJSON'],'authData':AuthenticatorAttestationResponse['authenticatorData'],'signature':signature,'userHandle':AuthenticatorAttestationResponse['userHandle']}

    credential_id = AuthenticatorAttestationResponse.get('id')
    user = database.query_db("select * from Users where credential_id=?",[credential_id])[0]
    if len(user)==0:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    webauthn_user = webauthn.WebAuthnUser(
        user[0], user[0], user[2], user[6],
        user[4], user[3], user[5], user[7])

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        retrievedChallenge,
        ORIGIN,
        uv_required=False)  # User Verification
    sign_count = webauthn_assertion_response.verify()
    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        print(e)
        return make_response(jsonify({'fail': 'Assertion failed'}),500)

    # Update counter.

    #Update database
    update = database.insert_db("update Users SET sign_count=? where username=?",[sign_count,user[1]])
    identityObj={'username':user[1],'id':user[0],'displayname':user[2]}
    expires = datetime.timedelta(hours=2)
    print(identityObj)
    jwt = create_access_token(identity=identityObj,expires_delta=expires)
    return jsonify({
        'success':
        'Successfully authenticated as {}'.format(user[1]),
        'jwt':jwt,
        'username': user[1]
    })
예제 #11
0
def webauthn_finish_login():
    user = User()
    cookie = None    
    
    challenge = session.get('challenge')
    password = session.get('login_password')
    
    assertion_response = request.form
    credential_id = assertion_response.get('id')

    db, person = auth.getPersonByCredentialID(credential_id)
    if not person:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)

    webauthn_user = webauthn.WebAuthnUser(
        person.ukey, person.username, person.display_name, person.icon_url,
        person.credential_id, person.pub_key, person.sign_count, person.rp_id)

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        ORIGIN,
        uv_required=False)  # User Verification
   
    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return make_response(jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}), 401)
    
    # Update counter.
    person.sign_count = sign_count
    db.commit()

    # TODO: Is this check not performing anything useful?
    if not person.username:
        return make_response(jsonify({'fail': 'You must supply a username to log in.'}), 401)
    elif not password:
        return make_response(jsonify({'fail': 'You must supply a password to log in.'}), 401)
    else:
        cookie = user.checkLogin(person.username, password)
        if not cookie:
            return make_response(jsonify({'fail': 'Invalid username or password.'}), 401)

    nexturl = request.values.get('nexturl', https_url_for('index'))
    response = make_response(jsonify({'nexturl': nexturl}), 200)
    
    ## Be careful not to include semicolons in cookie value; see
    ## https://github.com/mitsuhiko/werkzeug/issues/226 for more
    ## details.
    response.set_cookie('PyZoobarLogin', cookie)
    return response
예제 #12
0
    def get_assertion_response(self):
        credential = self.test_validate_registration()
        webauthn_user = webauthn.WebAuthnUser(
            USER_ID, USER_NAME, USER_DISPLAY_NAME, ICON_URL,
            credential.credential_id.decode(), credential.public_key,
            credential.sign_count, credential.rp_id)

        webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
            webauthn_user,
            copy(ASSERTION_RESPONSE_TMPL),
            ASSERTION_CHALLENGE,
            ORIGIN,
            uv_required=False)

        return webauthn_assertion_response
예제 #13
0
def webauthn_verify_login():
    '''Verify the user's credential attesttion during the login process'''
    challenge = session['login']['challenge']
    assertion_response = request.form
    credential_id = assertion_response.get('id')

    # Ensure a matching user exists
    user = User.query.filter_by(credential_id=credential_id).first()
    if not user:
        flash('User does not exist')
        return make_response(jsonify({'redirect': url_for('login')}), 401)

    # TODO: determine if this info should be stored at the session level
    webauthn_user = webauthn.WebAuthnUser(
        user_id=user.ukey,
        username=user.email,
        display_name=user.display_name,
        icon_url=user.icon_url,
        credential_id=user.credential_id,
        public_key=user.public_key,
        sign_count=user.sign_count,
        rp_id=user.rp_id,
    )

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user=webauthn_user,
        assertion_response=assertion_response,
        challenge=challenge,
        origin=ORIGIN,
        uv_required=False,
    )  # User Verification

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        flash(f'Assertion failed. Error: {e}')
        return make_response(jsonify({'redirect': url_for('login')}), 401)

    # Update counter.
    user.sign_count = sign_count
    db.session.add(user)
    db.session.commit()

    # Clear login session info
    session.pop('login', None)

    login_user(user)
    return jsonify({'redirect': url_for('profile')})
예제 #14
0
    def clean(self):
        if self.profile.is_webauthn_enabled and self.cleaned_data.get(
                'webauthn_response'):
            if len(self.cleaned_data['webauthn_response']) > 65536:
                raise ValidationError(_('Invalid WebAuthn response.'))

            if not self.webauthn_challenge:
                raise ValidationError(_('No WebAuthn challenge issued.'))

            response = json.loads(self.cleaned_data['webauthn_response'])
            try:
                credential = self.profile.webauthn_credentials.get(
                    cred_id=response.get('id', ''))
            except WebAuthnCredential.DoesNotExist:
                raise ValidationError(_('Invalid WebAuthn credential ID.'))

            user = credential.webauthn_user
            # Work around a useless check in the webauthn package.
            user.credential_id = credential.cred_id
            assertion = webauthn.WebAuthnAssertionResponse(
                webauthn_user=user,
                assertion_response=response.get('response'),
                challenge=self.webauthn_challenge,
                origin=self.webauthn_origin,
                uv_required=False,
            )

            try:
                sign_count = assertion.verify()
            except Exception as e:
                raise ValidationError(str(e))

            credential.counter = sign_count
            credential.save(update_fields=['counter'])
        elif self.profile.is_totp_enabled and self.cleaned_data.get(
                'totp_token'):
            if pyotp.TOTP(self.profile.totp_key).verify(
                    self.cleaned_data['totp_token'],
                    valid_window=self.TOLERANCE):
                return
            raise ValidationError(
                _('Invalid two-factor authentication token.'))
        else:
            raise ValidationError(
                _('Must specify either totp_token or webauthn_response.'))
예제 #15
0
def verify_assertion(election):
    challenge = session.get('challenge')
    assertion_response = request.form
    credential_id = assertion_response.get('id')

    user = Authority.query.filter(
        Election.name == election,
        Authority.credential_id == credential_id).first()
    if not user:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)

    webauthn_user = webauthn.WebAuthnUser(user.ukey, user.email, user.name,
                                          user.icon_url, user.credential_id,
                                          user.pub_key, user.sign_count,
                                          user.rp_id)

    http = request.url.split("://")
    origin = f"{http[0]}://{election}.{ORIGIN}"

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        origin,
        uv_required=False)  # User Verification

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)})

    # Update counter
    user.sign_count = sign_count
    db.session.add(user)
    db.session.commit()

    login_user(user)

    return jsonify(
        {'success': 'Successfully authenticated as {}'.format(user.email)})
예제 #16
0
    def verify(self, request, otp_device, data):
        challenge = request.session.get('challenge', '').rstrip('=')

        ukey = base64.urlsafe_b64encode(str(request.user.id).encode('utf-8'))
        webauthn_user = webauthn.WebAuthnUser(
            ukey, otp_device.user.username, otp_device.user.username,
            otp_device.data['icon_url'], otp_device.data['credential_id'],
            otp_device.data['pub_key'], otp_device.counter,
            otp_device.data['rp_id'])

        webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
            webauthn_user,
            data,
            challenge,
            self._get_origin(request),
            uv_required=False)  # User Verification
        try:
            sign_count = webauthn_assertion_response.verify()
        except AuthenticationRejectedException as e:
            raise ValidationError(str(e))
        otp_device.counter = sign_count
        otp_device.save()
        return {}
def login_webauthn_user_assertion_verify(request):
    current_site_details = get_current_site(request)
    assertion_challenge = request.session["challenge"]
    assertion_client_response = json.loads(
        request.POST["signedAssertionCredentials"])
    webauthn_user = WebAuthnProfile.objects.filter(
        credential_id=assertion_client_response["id"]).first()
    origin = request.build_absolute_uri('/').strip("/")
    if DEBUG:  # For angular dev server frontend - need to remove this later
        origin = origin.replace("8000", "4200").replace("http", "https")
    if webauthn_user is None:
        return JsonResponse({"error": "Invalid User"})
    generated_webauthn_user = webauthn.WebAuthnUser(
        user_id=webauthn_user.webauthn_ukey,
        username=webauthn_user.user.username,
        display_name=webauthn_user.display_name,
        icon_url=
        "https://rajeevkr.me/assets/img/portfolio/IMG_20180302_232843.jpg",
        credential_id=webauthn_user.credential_id,
        public_key=webauthn_user.user_public_key,
        sign_count=webauthn_user.signature_counter,
        rp_id=current_site_details.domain.split(":")[0])
    generated_webauthn_user_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user=generated_webauthn_user,
        assertion_response=assertion_client_response,
        challenge=assertion_challenge,
        origin=origin,
        uv_required=False)
    try:
        signature_counter = generated_webauthn_user_assertion_response.verify()
    except Exception as e:
        traceback.print_exc()
        return JsonResponse({"error": f"Login assertion failed due to {e}"},
                            status=400)
    webauthn_user.signature_counter = signature_counter
    webauthn_user.save()
    return JsonResponse(webauthn_user.hackyDict())
예제 #18
0
파일: app.py 프로젝트: billchenchina/wasl
def verify_assertion():
    challenge = session['challenge']
    assertion_response = request.form
    credential_id = assertion_response.get('id')
    publickey_redential = PublicKeyCredential.query.filter_by(
        credential_id=credential_id).first()
    if not publickey_redential:
        return make_response(
            jsonify({
                "status": "failed",
                "msg": "No pubkey found"
            }), 401)
    user = publickey_redential.user
    name = publickey_redential.email
    webauthn_user = webauthn.WebAuthnUser(publickey_redential.ukey, name,
                                          user.display_name, "", credential_id,
                                          publickey_redential.pub_key, 0,
                                          publickey_redential.rp_id)
    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        request.host_url[:-1],
        uv_required=False)
    try:
        webauthn_assertion_response.verify()
    except Exception as e:
        return make_response(
            jsonify({
                'status': 'failed',
                'msg': '{}'.format(e)
            }), 401)
    session['register_username'] = name
    session['register_display_name'] = user.display_name
    session['register_ukey'] = publickey_redential.ukey
    login_user(user)
    return jsonify({"status": "success"})
예제 #19
0
def fido():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not (user and user.fido_enabled()):
        flash("Only user with security key linked should go to this page",
              "warning")
        return redirect(url_for("auth.login"))

    auto_activate = True
    fido_token_form = FidoTokenForm()

    next_url = request.args.get("next")

    webauthn_user = webauthn.WebAuthnUser(
        user.fido_uuid,
        user.email,
        user.name if user.name else user.email,
        False,
        user.fido_credential_id,
        user.fido_pk,
        user.fido_sign_count,
        RP_ID,
    )

    # Handling POST requests
    if fido_token_form.validate_on_submit():
        try:
            sk_assertion = json.loads(fido_token_form.sk_assertion.data)
        except Exception as e:
            flash("Key verification failed. Error: Invalid Payload", "warning")
            return redirect(url_for("auth.login"))

        challenge = session["fido_challenge"]

        webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
            webauthn_user, sk_assertion, challenge, URL, uv_required=False)

        try:
            new_sign_count = webauthn_assertion_response.verify()
        except Exception as e:
            LOG.error(
                f"An error occurred in WebAuthn verification process: {e}")
            flash("Key verification failed.", "warning")
            auto_activate = False
        else:
            user.fido_sign_count = new_sign_count
            db.session.commit()
            del session[MFA_USER_ID]

            login_user(user)
            flash(f"Welcome back {user.name}!", "success")

            # User comes to login page from another page
            if next_url:
                LOG.debug("redirect user to %s", next_url)
                return redirect(next_url)
            else:
                LOG.debug("redirect user to dashboard")
                return redirect(url_for("dashboard.index"))

    # Prepare information for key registration process
    session.pop("challenge", None)
    challenge = secrets.token_urlsafe(32)

    session["fido_challenge"] = challenge.rstrip("=")

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)
    webauthn_assertion_options = webauthn_assertion_options.assertion_dict

    return render_template(
        "auth/fido.html",
        fido_token_form=fido_token_form,
        webauthn_assertion_options=webauthn_assertion_options,
        enable_otp=user.enable_otp,
        auto_activate=auto_activate,
    )
예제 #20
0
def webauthn_finish_transfer():
    cookie = None

    challenge = session.get('challenge')
    amount = session.get('transfer_amount')
    recipient = session.get('transfer_recipient')
    clientExtensions = session.get('clientExtensions')

    assertion_response = request.form
    credential_id = assertion_response.get('id')

    # Make sure action is performed on correct user
    if g.user.person.credential_id != credential_id:
        return make_response(
            jsonify({
                'fail':
                'Credential ID does not match that of logged in user.'
            }), 401)

    db, person = auth.getPersonByCredentialID(credential_id)
    if not person:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)

    webauthn_user = webauthn.WebAuthnUser(person.ukey, person.username,
                                          person.display_name, person.icon_url,
                                          person.credential_id, person.pub_key,
                                          person.sign_count, person.rp_id)

    def verify_authenticator_extensions_fn(client_data,
                                           expected_authenticator_extensions):
        client_data_extensions = client_data.get('clientExtensions')

        # Make sure that the extensions dicts have the same keys
        if client_data_extensions.keys(
        ) != expected_authenticator_extensions.keys():
            return False

        # Make sure that the key is only `txAuthSimple` for now
        if client_data_extensions.keys() != {'txAuthSimple'}:
            return False

        # Test the `txAuthSimple` extension, except for line breaks
        if client_data_extensions['txAuthSimple'].replace('\n', '') != \
           expected_authenticator_extensions['txAuthSimple'].replace('\n', ''):
            return False

        # All passed
        return True

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        ORIGIN,
        uv_required=False,  # User Verification
        expected_assertion_authenticator_extensions=clientExtensions,
        verify_authenticator_extensions_fn=verify_authenticator_extensions_fn,
    )

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return make_response(
            jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}), 401)

    # Update counter.
    person.sign_count = sign_count
    db.commit()

    # Perform the zoobar transfer
    bank.transfer(g.user.person.username, recipient, amount)

    nexturl = request.values.get('nexturl', https_url_for('transfer_page'))
    response = make_response(jsonify({'nexturl': nexturl}), 200)

    return response
예제 #21
0
def fido():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not (user and user.fido_enabled()):
        flash("Only user with security key linked should go to this page",
              "warning")
        return redirect(url_for("auth.login"))

    auto_activate = True
    fido_token_form = FidoTokenForm()

    next_url = request.args.get("next")

    if request.cookies.get("mfa"):
        browser = MfaBrowser.get_by(token=request.cookies.get("mfa"))
        if browser and not browser.is_expired() and browser.user_id == user.id:
            login_user(user)
            flash(f"Welcome back {user.name}!", "success")
            # Redirect user to correct page
            return redirect(next_url or url_for("dashboard.index"))
        else:
            # Trigger rate limiter
            g.deduct_limit = True

    # Handling POST requests
    if fido_token_form.validate_on_submit():
        try:
            sk_assertion = json.loads(fido_token_form.sk_assertion.data)
        except Exception as e:
            flash("Key verification failed. Error: Invalid Payload", "warning")
            return redirect(url_for("auth.login"))

        challenge = session["fido_challenge"]

        try:
            fido_key = Fido.get_by(uuid=user.fido_uuid,
                                   credential_id=sk_assertion["id"])
            webauthn_user = webauthn.WebAuthnUser(
                user.fido_uuid,
                user.email,
                user.name if user.name else user.email,
                False,
                fido_key.credential_id,
                fido_key.public_key,
                fido_key.sign_count,
                RP_ID,
            )
            webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
                webauthn_user, sk_assertion, challenge, URL, uv_required=False)
            new_sign_count = webauthn_assertion_response.verify()
        except Exception as e:
            LOG.exception(
                f"An error occurred in WebAuthn verification process: {e}")
            flash("Key verification failed.", "warning")
            # Trigger rate limiter
            g.deduct_limit = True
            auto_activate = False
        else:
            user.fido_sign_count = new_sign_count
            db.session.commit()
            del session[MFA_USER_ID]

            login_user(user)
            flash(f"Welcome back {user.name}!", "success")

            # Redirect user to correct page
            response = make_response(
                redirect(next_url or url_for("dashboard.index")))

            if fido_token_form.remember.data:
                browser = MfaBrowser.create_new(user=user)
                db.session.commit()
                response.set_cookie(
                    "mfa",
                    value=browser.token,
                    expires=browser.expires.datetime,
                    secure=True if URL.startswith("https") else False,
                    httponly=True,
                    samesite="Lax",
                )

            return response

    # Prepare information for key registration process
    session.pop("challenge", None)
    challenge = secrets.token_urlsafe(32)

    session["fido_challenge"] = challenge.rstrip("=")

    fidos = Fido.filter_by(uuid=user.fido_uuid).all()
    webauthn_users = []
    for fido in fidos:
        webauthn_users.append(
            webauthn.WebAuthnUser(
                user.fido_uuid,
                user.email,
                user.name if user.name else user.email,
                False,
                fido.credential_id,
                fido.public_key,
                fido.sign_count,
                RP_ID,
            ))

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_users, challenge)
    webauthn_assertion_options = webauthn_assertion_options.assertion_dict

    return render_template(
        "auth/fido.html",
        fido_token_form=fido_token_form,
        webauthn_assertion_options=webauthn_assertion_options,
        enable_otp=user.enable_otp,
        auto_activate=auto_activate,
        next_url=next_url,
    )
예제 #22
0
파일: api.py 프로젝트: bellyfat/kagi
def webauthn_verify_assertion(request):
    challenge = request.session.get("challenge")
    assertion_response = request.POST
    credential_id = assertion_response.get("id")

    user = util.get_user(request)

    username = user.get_username()
    display_name = user.get_full_name()

    key = WebAuthnKey.objects.filter(credential_id=credential_id,
                                     user=user).first()
    if not key:
        return JsonResponse({"fail": "Key does not exist."}, status=400)

    webauthn_user = webauthn.WebAuthnUser(
        key.ukey,
        username,
        display_name,
        settings.WEBAUTHN_ICON_URL,
        key.credential_id,
        key.public_key,
        key.sign_count,
        settings.RELYING_PARTY_ID,
    )

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user,
        assertion_response,
        challenge,
        util.get_origin(request),
        uv_required=False,  # User Verification
    )

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return JsonResponse({"fail": "Assertion failed. Error: {}".format(e)},
                            status=400)

    # Update counter.
    key.sign_count = sign_count
    key.last_used = now()
    key.save()

    try:
        del request.session["kagi_pre_verify_user_pk"]
        del request.session["kagi_pre_verify_user_backend"]
        del request.session["challenge"]
    except KeyError:  # pragma: no cover
        pass

    auth.login(request, user)

    redirect_to = request.POST.get(
        auth.REDIRECT_FIELD_NAME, request.GET.get(auth.REDIRECT_FIELD_NAME,
                                                  ""))
    if not is_safe_url(url=redirect_to, allowed_hosts=[request.get_host()]):
        redirect_to = resolve_url(django_settings.LOGIN_REDIRECT_URL)

    return JsonResponse({
        "success":
        "Successfully authenticated as {}".format(username),
        "redirect_to":
        redirect_to,
    })