def webauthn_begin_login():
    username = request.form.get('login_username')
    password = request.form.get('login_password')

    if not util.validate_username(username):
        return make_response(jsonify({'fail': 'Invalid username.'}), 401)

    _, person = auth.getPerson(username)

    if not person:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    if not person.credential_id:
        return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401)

    session.pop('challenge', None)
    session.pop('login_password', None)

    challenge = util.generate_challenge(32)

    # We strip the padding from the challenge stored in the session
    # for the reasons outlined in the comment in webauthn_begin_activate.
    session['challenge'] = challenge.rstrip('=')
    session['login_password'] = password
    
    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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)
    
    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #2
0
def webauthn_begin_assertion(election):
    email = request.form.get('email')

    if not len(email):
        return make_response(jsonify({'fail': 'Invalid email.'}), 401)

    user = Authority.query.filter(Election.name == election,
                                  Authority.email == email).first()
    if not user:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    if not user.webauthn:
        session['email'] = email
        return jsonify({'alt-login': '******'})
    if not user.credential_id:
        return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401)

    if 'challenge' in session:
        del session['challenge']
    challenge = generate_challenge(32)
    session['challenge'] = challenge

    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)

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)

    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #3
0
def webauthn_begin_assertion(request):
    challenge = util.generate_challenge(32)
    request.session["challenge"] = challenge

    user = util.get_user(request)

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

    keys = WebAuthnKey.objects.filter(user=user)

    webauthn_users = []
    for key in keys:
        webauthn_users.append(
            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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_users, challenge)

    return JsonResponse(webauthn_assertion_options.assertion_dict)
Beispiel #4
0
def webauthn_begin_assertion():
    jsonData = request.get_json()
    username = jsonData['username']

    if not util.validate_username(username):
        return make_response(jsonify({'fail': 'Invalid username.'}), 401)

    user = database.query_db("select * from Users where username=?",[username])[0]
    if len(user) == 0:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    if not user[4]:
        return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401)


    challenge = util.generate_challenge(32)

    # We strip the padding from the challenge stored in the session
    # for the reasons outlined in the comment in webauthn_begin_activate.
    toStoreChallenge = challenge.rstrip('=')
    try:
        insert = database.insert_db("insert into PublicKeyCredentialCreationOptions VALUES (?,?,?,?,?,?)",[None, None,user[0],None,username,toStoreChallenge])
    except:
        update = database.insert_db("update PublicKeyCredentialCreationOptions SET challenge=? where user_username=?",[toStoreChallenge,username])
    webauthn_user = webauthn.WebAuthnUser(
        user[0], user[1], user[2], user[6],
        user[4], user[3], user[5], user[7])

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)

    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #5
0
def webauthn_begin_assertion():
    username = request.form.get('login_username')

    if not util.validate_username(username):
        return make_response(jsonify({'fail': 'Invalid username.'}), 401)

    user = User.query.filter_by(username=username).first()

    if not user:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    if not user.credential_id:
        return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401)

    if 'challenge' in session:
        del session['challenge']

    challenge = util.generate_challenge(32)

    session['challenge'] = challenge

    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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)

    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #6
0
def login_challenge():
    data = LoginChallengeSchema().load(request.get_json())
    email = data["email"]

    fake_user = User.generate_fake(email=email)
    user = User.query.filter_by(email=email).first() or fake_user

    challenge = random_string()
    webauthn_users = [
        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"],
        )
        for authenticator in user.authenticators
    ]

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

    token = guard.encode_jwt_token(
        user, is_login=True, email=email, challenge=challenge
    )

    return {
        "token": token,
        "options": options,
    }
Beispiel #7
0
def get_assertion_options(user, *, challenge, icon_url, rp_id):
    """
    Returns a dictionary of options for assertion retrieval
    on the client side.
    """
    options = pywebauthn.WebAuthnAssertionOptions(
        _get_webauthn_users(user, icon_url=icon_url, rp_id=rp_id), challenge)

    return options.assertion_dict
Beispiel #8
0
 def get(self, request, *args, **kwargs):
     challenge = os.urandom(32)
     request.session['webauthn_assert'] = webauthn_encode(challenge)
     data = webauthn.WebAuthnAssertionOptions(
         [
             credential.webauthn_user for credential in request.profile.
             webauthn_credentials.select_related('user__user')
         ],
         challenge,
     ).assertion_dict
     return JsonResponse(data, encoder=WebAuthnJSONEncoder)
def login_start():
    username = request.json.get('username')
    if username not in USERS:
        return jsonify(status='failure', error='User with given username not found')

    challenge = secrets.token_urlsafe(32)
    session['challenge'] = challenge

    user = USERS[username]
    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_options = webauthn.WebAuthnAssertionOptions(webauthn_user, challenge)
    return jsonify(status='success', options=webauthn_assertion_options.assertion_dict)
Beispiel #10
0
    def challenge(self, request, otp_device):
        challenge = generate_challenge()
        request.session['challenge'] = challenge

        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_options = webauthn.WebAuthnAssertionOptions(
            webauthn_user, challenge)

        return webauthn_assertion_options.assertion_dict
Beispiel #11
0
def webauthn_begin_transfer():
    amount = request.form.get('transfer_amount')
    recipient = request.form.get('transfer_recipient')

    if not util.validate_transfer_amount(amount):
        return make_response(jsonify({'fail': 'Invalid transfer amount.'}),
                             401)

    # Make the amount into an int type. Type safety assured by `util.validate_transfer_amount`
    amount = int(amount)

    # Extract the logged in `g.user`
    person = g.user.person

    if not person:
        return make_response(jsonify({'fail': 'User does not exist.'}), 401)
    if not person.credential_id:
        return make_response(jsonify({'fail': 'Unknown credential ID.'}), 401)

    session.pop('challenge', None)
    session.pop('transfer_amount', None)
    session.pop('transfer_recipient', None)
    session.pop('clientExtensions', None)

    challenge = util.generate_challenge(32)

    # We strip the padding from the challenge stored in the session
    # for the reasons outlined in the comment in webauthn_begin_activate.
    session['challenge'] = challenge.rstrip('=')
    session['transfer_amount'] = amount
    session['transfer_recipient'] = recipient
    session['clientExtensions'] = {
        'txAuthSimple':
        "Authorize sending {} coins from {} to {}!".format(
            amount, person.username, recipient)
    }

    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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge, clientExtensions=session['clientExtensions'])

    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #12
0
def webauthn_begin_assertion(request):
    print("webauthn_begin_assertion")
    username = request.POST.get('login_username')
    challenge = generate_challenge(32)
    user = User.objects.get(username=username)
    if 'challenge' in request.session:
        del request.session['challenge']

    challenge = generate_challenge(32)
    print("assertion get challenge")
    request.session['challenge'] = challenge
    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)
    print("assertion get user")
    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)
    print("go return")
    return JsonResponse(webauthn_assertion_options.assertion_dict)
Beispiel #13
0
 def get_context_data(self, **kwargs):
     ctx = super().get_context_data()
     if 'webauthn_challenge' in self.request.session:
         del self.request.session['webauthn_challenge']
     challenge = generate_challenge(32)
     self.request.session['webauthn_challenge'] = challenge
     devices = [
         device.webauthnuser
         for device in WebAuthnDevice.objects.filter(confirmed=True,
                                                     user=self.request.user)
     ] + [
         device.webauthnuser
         for device in U2FDevice.objects.filter(confirmed=True,
                                                user=self.request.user)
     ]
     if devices:
         webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
             devices, challenge)
         ad = webauthn_assertion_options.assertion_dict
         ad['extensions'] = {'appid': get_u2f_appid(self.request)}
         ctx['jsondata'] = json.dumps(ad)
     return ctx
Beispiel #14
0
def login_start():
    username, password = request.json.get('username'), request.json.get('password')
    if username not in ADMIN_USERS:
        logger.info(f"Login start failed: no user with username {username}")
        return jsonify(status='failure', error='User with given username not found')
    user = ADMIN_USERS[username]
    if not isinstance(password, str) or not check_password_hash(user.password_hash, password):
        logger.info(f"Login start failed: incorrect password")
        return jsonify(status='failure', error='Incorrect password')
    logger.info(f"Login started for user {username} with password hash {user.password_hash}: password correct")

    if DISABLE_ADMIN_WEBAUTHN:
        logger.info(f"Login completed for user {username} (DISABLE_ADMIN_WEBAUTHN is enabled)")
        login_user(user, remember=False)
        return jsonify(status='success', options={})

    challenge = secrets.token_urlsafe(32)
    session['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_options = webauthn.WebAuthnAssertionOptions(webauthn_user, challenge)
    logger.info(f"Login challenge issued for user {username} with challenge {challenge}")
    return jsonify(status='success', options=webauthn_assertion_options.assertion_dict)
def login_webauthn_user_begin_assertion(request):
    current_site_details = get_current_site(request)
    username = request.POST["username"]
    webauthn_user = WebAuthnProfile.objects.filter(
        user__username=username).first()
    if webauthn_user is None:
        return JsonResponse({"error": "User doesn't exist"})
    assertion_challenge = token_urlsafe_without_stripping(32)
    request.session["challenge"] = assertion_challenge.rstrip('=')
    generated_webauthn_user = webauthn.WebAuthnUser(
        user_id=webauthn_user.webauthn_ukey,
        username=webauthn_user.user.username,
        display_name=webauthn_user.display_name,
        credential_id=webauthn_user.credential_id,
        icon_url=
        "https://rajeevkr.me/assets/img/portfolio/IMG_20180302_232843.jpg",
        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 = webauthn.WebAuthnAssertionOptions(
        generated_webauthn_user, assertion_challenge)
    return JsonResponse(generated_webauthn_user_assertion.assertion_dict)
Beispiel #16
0
def auth_request():
    username = request.form.get('login_username')

    user = User.query.filter_by(username=username).first()

    if 'challenge' in session:
        del session['challenge']

    challenge = util.generate_challenge(32)

    session['challenge'] = challenge

    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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)
    print(webauthn_assertion_options)
    print(webauthn_assertion_options.assertion_dict)
    return json.jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #17
0
def webauthn_login_start():
    '''Start the biometric login process by generating a random challenge for the user'''
    email = request.form['email']

    if not validate_email(email):
        flash('Please enter a valid email', 'error')
        return make_response(jsonify({'redirect': url_for('login')}), 401)

    # Attempt to find user in database
    user = User.query.filter_by(email=email).first()
    if not user:
        flash('User does not exist', 'error')
        return make_response(jsonify({'redirect': url_for('login')}), 401)
    if not user.credential_id:
        flash('User does not have biometric to sign in with', 'error')
        return make_response(jsonify({'redirect': url_for('login')}), 401)

    # We strip the padding from the challenge stored in the session
    # for the reasons outlined in the comment in webauthn_begin_activate.
    challenge = secrets.token_urlsafe(32)
    session['login'] = {'challenge': challenge.rstrip('=')}

    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_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge
    )

    return jsonify(webauthn_assertion_options.assertion_dict)
Beispiel #18
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,
    )
Beispiel #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,
    )