Merge pull request #199 from developStorm/webauthn-multiple-keys
Support Multiple Keys for WebAuthn
This commit is contained in:
commit
eb60028b1f
|
@ -22,10 +22,12 @@
|
|||
<form id="formRegisterKey" method="post">
|
||||
{{ fido_token_form.csrf_token }}
|
||||
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<button id="btnVerifyKey" class="btn btn-success mt-2">Use your security key</button>
|
||||
<button id="btnVerifyKey" class="btn btn-success mt-2" onclick="verifyKey();">Use your security key</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
{% if enable_otp %}
|
||||
<hr>
|
||||
|
@ -68,7 +70,6 @@
|
|||
$('#formRegisterKey').submit();
|
||||
}
|
||||
|
||||
$("#btnVerifyKey").click(verifyKey);
|
||||
</script>
|
||||
|
||||
{% if auto_activate %}
|
||||
|
|
|
@ -12,7 +12,7 @@ from app.config import MFA_USER_ID
|
|||
from app.config import RP_ID, URL
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import User
|
||||
from app.models import User, Fido
|
||||
|
||||
|
||||
class FidoTokenForm(FlaskForm):
|
||||
|
@ -40,17 +40,6 @@ def fido():
|
|||
|
||||
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:
|
||||
|
@ -61,11 +50,23 @@ def fido():
|
|||
|
||||
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
|
||||
)
|
||||
|
||||
try:
|
||||
new_sign_count = webauthn_assertion_response.verify()
|
||||
except Exception as e:
|
||||
LOG.error(f"An error occurred in WebAuthn verification process: {e}")
|
||||
|
@ -93,8 +94,24 @@ def fido():
|
|||
|
||||
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_user, challenge
|
||||
webauthn_users, challenge
|
||||
)
|
||||
webauthn_assertion_options = webauthn_assertion_options.assertion_dict
|
||||
|
||||
|
|
|
@ -9,10 +9,11 @@ from .views import (
|
|||
api_key,
|
||||
custom_domain,
|
||||
alias_contact_manager,
|
||||
enter_sudo,
|
||||
mfa_setup,
|
||||
mfa_cancel,
|
||||
fido_setup,
|
||||
fido_cancel,
|
||||
fido_manage,
|
||||
domain_detail,
|
||||
lifetime_licence,
|
||||
directory,
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
{% extends 'default.html' %}
|
||||
{% set active_page = "setting" %}
|
||||
{% block title %}
|
||||
Unlink Security Key
|
||||
SUDO MODE
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block default_content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1 class="h2">Unlink Your Security Key</h1>
|
||||
<h1 class="h2">Entering Sudo Mode</h1>
|
||||
<p>
|
||||
You are trying to change sensitive settings
|
||||
</p>
|
||||
<p>
|
||||
Please enter the password of your account so that we can ensure it's you.
|
||||
</p>
|
||||
|
@ -20,7 +23,7 @@
|
|||
|
||||
{{ password_check_form.password(class="form-control", autofocus="true") }}
|
||||
{{ render_field_errors(password_check_form.password) }}
|
||||
<button class="btn btn-lg btn-danger mt-2">Unlink Key</button>
|
||||
<button class="btn btn-lg btn-danger mt-2">Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,53 @@
|
|||
{% extends 'default.html' %}
|
||||
{% set active_page = "setting" %}
|
||||
{% block title %}
|
||||
Manage Security Key
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block default_content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1 class="h2">Manage Your Security Key</h1>
|
||||
<p>Unlink all keys will also disable WebAuthn 2FA.</p>
|
||||
|
||||
<form id="formManageKey" method="post">
|
||||
{{ fido_manage_form.csrf_token }}
|
||||
{{ fido_manage_form.credential_id(class="form-control", placeholder="") }}
|
||||
</form>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Linked At</th>
|
||||
<th scope="col" class="text-center">Operation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%for key in keys%}
|
||||
<tr>
|
||||
<th scope="row">{{ key.id }}</th>
|
||||
<td>{{ key.name }}</td>
|
||||
<td>{{key.created_at | dt}}</td>
|
||||
<td class="text-center"><button class="btn btn-outline-danger" onclick="$('#credential_id').val('{{ key.credential_id }}'); $('#formManageKey').submit();">Unlink</button></td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
<tr>
|
||||
<th scope="row">#</th>
|
||||
<td>Link a New Key</td>
|
||||
<td></td>
|
||||
<td class="text-center"><a href="{{ url_for('dashboard.fido_setup') }}"><button class="btn btn-outline-success">Link</button></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -19,10 +19,14 @@
|
|||
<form id="formRegisterKey" method="post">
|
||||
{{ fido_token_form.csrf_token }}
|
||||
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}
|
||||
</form>
|
||||
|
||||
{{ fido_token_form.key_name(class="form-control", placeholder="Name of your key (Required)") }}
|
||||
{{ render_field_errors(fido_token_form.key_name) }}
|
||||
|
||||
<div class="text-center">
|
||||
<button id="btnRegisterKey" class="btn btn-lg btn-primary mt-2">Register Key</button>
|
||||
<button id="btnRegisterKey" class="btn btn-lg btn-primary mt-2" onclick="registerKey();">Register Key</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
async function registerKey() {
|
||||
|
@ -33,6 +37,7 @@
|
|||
JSON.parse('{{credential_create_options|tojson|safe}}')
|
||||
)
|
||||
|
||||
|
||||
let credential
|
||||
try {
|
||||
credential = await navigator.credentials.create({
|
||||
|
@ -51,8 +56,6 @@
|
|||
$('#formRegisterKey').submit();
|
||||
}
|
||||
|
||||
$("#btnRegisterKey").click(registerKey);
|
||||
$('document').ready(registerKey());
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
{% if current_user.fido_uuid is none %}
|
||||
<a href="{{ url_for('dashboard.fido_setup') }}" class="btn btn-outline-primary">Setup WebAuthn</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('dashboard.fido_cancel') }}" class="btn btn-outline-danger">Disable WebAuthn</a>
|
||||
<a href="{{ url_for('dashboard.fido_manage') }}" class="btn btn-outline-info">Manage WebAuthn</a>
|
||||
<a href="{{ url_for('dashboard.recovery_code_route') }}" class="btn btn-outline-secondary">Recovery Codes</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
from time import time
|
||||
|
||||
from flask import render_template, flash, redirect, url_for, session, request
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import PasswordField, validators
|
||||
from functools import wraps
|
||||
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.log import LOG
|
||||
|
||||
_SUDO_GAP = 900
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
password = PasswordField("Password", validators=[validators.DataRequired()])
|
||||
|
||||
|
||||
@dashboard_bp.route("/enter_sudo", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def enter_sudo():
|
||||
password_check_form = LoginForm()
|
||||
|
||||
if password_check_form.validate_on_submit():
|
||||
password = password_check_form.password.data
|
||||
|
||||
if current_user.check_password(password):
|
||||
session["sudo_time"] = int(time())
|
||||
|
||||
# User comes to sudo page from another page
|
||||
next_url = request.args.get("next")
|
||||
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"))
|
||||
else:
|
||||
flash("Incorrect password", "warning")
|
||||
|
||||
return render_template(
|
||||
"dashboard/enter_sudo.html", password_check_form=password_check_form
|
||||
)
|
||||
|
||||
|
||||
def sudo_required(f):
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
if (
|
||||
"sudo_time" not in session
|
||||
or (time() - int(session["sudo_time"])) > _SUDO_GAP
|
||||
):
|
||||
return redirect(url_for("dashboard.enter_sudo", next=request.path))
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
|
@ -1,45 +0,0 @@
|
|||
from flask import render_template, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import PasswordField, validators
|
||||
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.extensions import db
|
||||
from app.models import RecoveryCode
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
password = PasswordField("Password", validators=[validators.DataRequired()])
|
||||
|
||||
|
||||
@dashboard_bp.route("/fido_cancel", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def fido_cancel():
|
||||
if not current_user.fido_enabled():
|
||||
flash("You haven't registed a security key", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
password_check_form = LoginForm()
|
||||
|
||||
if password_check_form.validate_on_submit():
|
||||
password = password_check_form.password.data
|
||||
|
||||
if current_user.check_password(password):
|
||||
current_user.fido_pk = None
|
||||
current_user.fido_uuid = None
|
||||
current_user.fido_sign_count = None
|
||||
current_user.fido_credential_id = None
|
||||
db.session.commit()
|
||||
|
||||
# user does not have any 2FA enabled left, delete all recovery codes
|
||||
if not current_user.two_factor_authentication_enabled():
|
||||
RecoveryCode.empty(current_user)
|
||||
|
||||
flash("We've unlinked your security key.", "success")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
else:
|
||||
flash("Incorrect password", "warning")
|
||||
|
||||
return render_template(
|
||||
"dashboard/fido_cancel.html", password_check_form=password_check_form
|
||||
)
|
|
@ -0,0 +1,59 @@
|
|||
from flask import render_template, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import HiddenField, validators
|
||||
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import RecoveryCode, Fido
|
||||
from app.dashboard.views.enter_sudo import sudo_required
|
||||
|
||||
|
||||
class FidoManageForm(FlaskForm):
|
||||
credential_id = HiddenField("credential_id", validators=[validators.DataRequired()])
|
||||
|
||||
|
||||
@dashboard_bp.route("/fido_manage", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@sudo_required
|
||||
def fido_manage():
|
||||
if not current_user.fido_enabled():
|
||||
flash("You haven't registered a security key", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
fido_manage_form = FidoManageForm()
|
||||
|
||||
if fido_manage_form.validate_on_submit():
|
||||
credential_id = fido_manage_form.credential_id.data
|
||||
|
||||
fido_key = Fido.get_by(uuid=current_user.fido_uuid, credential_id=credential_id)
|
||||
|
||||
if not fido_key:
|
||||
flash("Unknown error, redirect back to manage page", "warning")
|
||||
return redirect(url_for("dashboard.fido_manage"))
|
||||
|
||||
Fido.delete(fido_key.id)
|
||||
db.session.commit()
|
||||
|
||||
LOG.d(f"FIDO Key ID={fido_key.id} Removed")
|
||||
flash(f"Key {fido_key.name} successfully unlinked", "success")
|
||||
|
||||
# Disable FIDO for the user if all keys have been deleted
|
||||
if not Fido.filter_by(uuid=current_user.fido_uuid).all():
|
||||
current_user.fido_uuid = None
|
||||
db.session.commit()
|
||||
|
||||
# user does not have any 2FA enabled left, delete all recovery codes
|
||||
if not current_user.two_factor_authentication_enabled():
|
||||
RecoveryCode.empty(current_user)
|
||||
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
return redirect(url_for("dashboard.fido_manage"))
|
||||
|
||||
return render_template(
|
||||
"dashboard/fido_manage.html",
|
||||
fido_manage_form=fido_manage_form,
|
||||
keys=Fido.filter_by(uuid=current_user.fido_uuid),
|
||||
)
|
|
@ -1,30 +1,31 @@
|
|||
import json
|
||||
import secrets
|
||||
import uuid
|
||||
from time import time
|
||||
|
||||
import webauthn
|
||||
from flask import render_template, flash, redirect, url_for, session
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import HiddenField, validators
|
||||
from wtforms import StringField, HiddenField, validators
|
||||
|
||||
from app.config import RP_ID, URL
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import Fido, RecoveryCode
|
||||
from app.dashboard.views.enter_sudo import sudo_required
|
||||
|
||||
|
||||
class FidoTokenForm(FlaskForm):
|
||||
key_name = StringField("key_name", validators=[validators.DataRequired()])
|
||||
sk_assertion = HiddenField("sk_assertion", validators=[validators.DataRequired()])
|
||||
|
||||
|
||||
@dashboard_bp.route("/fido_setup", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@sudo_required
|
||||
def fido_setup():
|
||||
if current_user.fido_enabled():
|
||||
flash("You have already registered your security key", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
if not current_user.can_use_fido:
|
||||
flash(
|
||||
"This feature is currently in invitation-only beta. Please send us an email if you want to try",
|
||||
|
@ -32,6 +33,11 @@ def fido_setup():
|
|||
)
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
if current_user.fido_uuid is not None:
|
||||
fidos = Fido.filter_by(uuid=current_user.fido_uuid).all()
|
||||
else:
|
||||
fidos = []
|
||||
|
||||
fido_token_form = FidoTokenForm()
|
||||
|
||||
# Handling POST requests
|
||||
|
@ -61,17 +67,32 @@ def fido_setup():
|
|||
flash("Key registration failed.", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
current_user.fido_pk = str(fido_credential.public_key, "utf-8")
|
||||
if current_user.fido_uuid is None:
|
||||
current_user.fido_uuid = fido_uuid
|
||||
current_user.fido_sign_count = fido_credential.sign_count
|
||||
current_user.fido_credential_id = str(fido_credential.credential_id, "utf-8")
|
||||
|
||||
Fido.create(
|
||||
credential_id=str(fido_credential.credential_id, "utf-8"),
|
||||
uuid=fido_uuid,
|
||||
public_key=str(fido_credential.public_key, "utf-8"),
|
||||
sign_count=fido_credential.sign_count,
|
||||
name=fido_token_form.key_name.data,
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
LOG.d(
|
||||
f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}"
|
||||
)
|
||||
|
||||
flash("Security key has been activated", "success")
|
||||
if not RecoveryCode.query.filter_by(user_id=current_user.id).all():
|
||||
return redirect(url_for("dashboard.recovery_code_route"))
|
||||
else:
|
||||
return redirect(url_for("dashboard.fido_manage"))
|
||||
|
||||
# Prepare information for key registration process
|
||||
fido_uuid = str(uuid.uuid4())
|
||||
fido_uuid = (
|
||||
str(uuid.uuid4()) if current_user.fido_uuid is None else current_user.fido_uuid
|
||||
)
|
||||
challenge = secrets.token_urlsafe(32)
|
||||
|
||||
credential_create_options = webauthn.WebAuthnMakeCredentialOptions(
|
||||
|
@ -91,6 +112,16 @@ def fido_setup():
|
|||
registration_dict = credential_create_options.registration_dict
|
||||
del registration_dict["extensions"]["webauthn.loc"]
|
||||
|
||||
# Prevent user from adding duplicated keys
|
||||
for fido in fidos:
|
||||
registration_dict["excludeCredentials"].append(
|
||||
{
|
||||
"type": "public-key",
|
||||
"id": fido.credential_id,
|
||||
"transports": ["usb", "nfc", "ble", "internal"],
|
||||
}
|
||||
)
|
||||
|
||||
session["fido_uuid"] = fido_uuid
|
||||
session["fido_challenge"] = challenge.rstrip("=")
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from wtforms import StringField, validators
|
|||
from app.dashboard.base import dashboard_bp
|
||||
from app.extensions import db
|
||||
from app.models import RecoveryCode
|
||||
from app.dashboard.views.enter_sudo import sudo_required
|
||||
|
||||
|
||||
class OtpTokenForm(FlaskForm):
|
||||
|
@ -15,6 +16,7 @@ class OtpTokenForm(FlaskForm):
|
|||
|
||||
@dashboard_bp.route("/mfa_cancel", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@sudo_required
|
||||
def mfa_cancel():
|
||||
if not current_user.enable_otp:
|
||||
flash("you don't have MFA enabled", "warning")
|
||||
|
|
|
@ -7,6 +7,7 @@ from wtforms import StringField, validators
|
|||
from app.dashboard.base import dashboard_bp
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.dashboard.views.enter_sudo import sudo_required
|
||||
|
||||
|
||||
class OtpTokenForm(FlaskForm):
|
||||
|
@ -15,6 +16,7 @@ class OtpTokenForm(FlaskForm):
|
|||
|
||||
@dashboard_bp.route("/mfa_setup", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@sudo_required
|
||||
def mfa_setup():
|
||||
if current_user.enable_otp:
|
||||
flash("you have already enabled MFA", "warning")
|
||||
|
|
|
@ -121,6 +121,19 @@ class AliasGeneratorEnum(EnumE):
|
|||
uuid = 2 # aliases are generated based on uuid
|
||||
|
||||
|
||||
class Fido(db.Model, ModelMixin):
|
||||
__tablename__ = "fido"
|
||||
credential_id = db.Column(db.String(), nullable=False, unique=True, index=True)
|
||||
uuid = db.Column(
|
||||
db.ForeignKey("users.fido_uuid", ondelete="cascade"),
|
||||
unique=False,
|
||||
nullable=False,
|
||||
)
|
||||
public_key = db.Column(db.String(), nullable=False, unique=True)
|
||||
sign_count = db.Column(db.Integer(), nullable=False)
|
||||
name = db.Column(db.String(128), nullable=False, unique=False)
|
||||
|
||||
|
||||
class User(db.Model, ModelMixin, UserMixin):
|
||||
__tablename__ = "users"
|
||||
email = db.Column(db.String(256), unique=True, nullable=False)
|
||||
|
@ -151,9 +164,6 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||
|
||||
# Fields for WebAuthn
|
||||
fido_uuid = db.Column(db.String(), nullable=True, unique=True)
|
||||
fido_credential_id = db.Column(db.String(), nullable=True, unique=True)
|
||||
fido_pk = db.Column(db.String(), nullable=True, unique=True)
|
||||
fido_sign_count = db.Column(db.Integer(), nullable=True)
|
||||
|
||||
# whether user can use Fido
|
||||
can_use_fido = db.Column(
|
||||
|
|
|
@ -38,6 +38,7 @@ from app.log import LOG
|
|||
from app.models import (
|
||||
Client,
|
||||
User,
|
||||
Fido,
|
||||
ClientUser,
|
||||
Alias,
|
||||
RedirectUri,
|
||||
|
@ -143,8 +144,10 @@ def fake_data():
|
|||
otp_secret="base32secret3232",
|
||||
can_use_fido=True,
|
||||
intro_shown=True,
|
||||
fido_uuid=None,
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
user.trial_end = None
|
||||
|
||||
LifetimeCoupon.create(code="coupon", nb_used=10)
|
||||
|
|
|
@ -56,7 +56,7 @@ const transformCredentialRequestOptions = (
|
|||
const transformCredentialCreateOptions = (
|
||||
credentialCreateOptionsFromServer
|
||||
) => {
|
||||
let { challenge, user } = credentialCreateOptionsFromServer;
|
||||
let { challenge, user, excludeCredentials } = credentialCreateOptionsFromServer;
|
||||
user.id = Uint8Array.from(
|
||||
atob(
|
||||
credentialCreateOptionsFromServer.user.id
|
||||
|
@ -75,10 +75,17 @@ const transformCredentialCreateOptions = (
|
|||
(c) => c.charCodeAt(0)
|
||||
);
|
||||
|
||||
excludeCredentials = excludeCredentials.map((credentialDescriptor) => {
|
||||
let { id } = credentialDescriptor;
|
||||
id = id.replace(/\_/g, "/").replace(/\-/g, "+");
|
||||
id = Uint8Array.from(atob(id), (c) => c.charCodeAt(0));
|
||||
return Object.assign({}, credentialDescriptor, { id });
|
||||
});
|
||||
|
||||
const transformedCredentialCreateOptions = Object.assign(
|
||||
{},
|
||||
credentialCreateOptionsFromServer,
|
||||
{ challenge, user }
|
||||
{ challenge, user, excludeCredentials }
|
||||
);
|
||||
|
||||
return transformedCredentialCreateOptions;
|
||||
|
|
Loading…
Reference in New Issue