如何解决DUO-LABS WebAuthn:服务器验证凭据失败:注册失败错误:注册被拒绝错误:无法验证来源
我尝试使用 DUO-lab 的 Python 的 webauthn
包实现基于指纹的身份验证。但是我遇到了这个错误:
server validation of credential Failed: registration Failed. error: registration rejected. error: unable to verify origin..
当我检查包的源代码时,我注意到这个错误 unable to verify origin..
可能是由于您的身份验证器配置不正确而引发的。
有没有一种方法可以明确说明我只需要 platform
身份验证器而不是 roaming
身份验证器,而无需使用包的源代码?如果有,请包含 Flask
的完整工作代码(这是我在错误将我赶出 Django 之后现在使用的代码)。我目前的配置是:
RP_ID = 'nacesdecide.herokuapp.com' #The app is currently hosted on heroku
RP_NAME = 'nacesdecides nacesdecide'
ORIGIN = 'https://nacesdecide.herokuapp.com/'
该应用程序目前在 heroku 上,可以通过 naces register 实时访问。我希望应用程序单独使用 platform authenticators
。
更新:
客户端代码的某些部分(从 duo-lab's python webauthn flask demon js 起草,是:
/**
* REGISTRATION FUNCTIONS
*/
/**
* Callback after the registration form is submitted.
* @param {Event} e
*/
const didClickRegister = async (e) => {
e.preventDefault();
// gather the data in the form
const form = document.querySelector("#register-form");
const formData = new FormData(form);
// post the data to the server to generate the PublicKeyCredentialCreateOptions
let credentialCreateOptionsFromServer;
try {
credentialCreateOptionsFromServer = await getCredentialCreateOptionsFromServer(
formData
);
} catch (err) {
showErrorAlert(`Failed to generate credential request options: ${err}`);
return console.error("Failed to generate credential request options:",err);
}
// convert certain members of the PublicKeyCredentialCreateOptions into
// byte arrays as expected by the spec.
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(
credentialCreateOptionsFromServer
);
// request the authenticator(s) to create a new credential keypair.
let credential;
*try {
credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreateOptions,});*
} catch (err) {
showErrorAlert(`Error creating credential: ${err}`);
return console.error("Error creating credential:",err);
}
// we Now have a new credential! We Now need to encode the byte arrays
// in the credential into strings,for posting to our server.
const newAssertionForServer = transformNewAssertionForServer(credential);
// post the transformed credential data to the server for validation
// and storing the public key
let assertionValidationResponse;
try {
assertionValidationResponse = await postNewAssertionToServer(
newAssertionForServer
);
} catch (err) {
showErrorAlert(`Server validation of credential Failed: ${err}`);
return console.error("Server validation of credential Failed:",err);
}
// reload the page after a successful result
setTimeout(function () {
window.location.href = Flask.url_for("accounts.login");
},1000);
// window.location.reload();
};
在服务器端,我们有:
def webauthn_begin_activate():
# MakeCredentialOptions
username = request.form.get('register_username')
display_name = request.form.get('register_display_name')
if not util.validate_username(username):
return make_response(jsonify({'fail': 'Invalid username.'}),401)
if not util.validate_display_name(display_name):
return make_response(jsonify({'fail': 'Invalid display name.'}),401)
if User.query.filter_by(username=username).first():
return make_response(jsonify({'fail': 'User already exists.'}),401)
#clear session variables prior to starting a new registration
session.pop('register_ukey',None)
session.pop('register_username',None)
session.pop('register_display_name',None)
session.pop('challenge',None)
session['register_username'] = username
session['register_display_name'] = display_name
challenge = util.generate_challenge(32)
ukey = util.generate_ukey()
# We strip the saved challenge of padding,so that we can do a byte
# comparison on the URL-safe-without-padding challenge we get back
# from the browser.
# We will still pass the padded version down to the browser so that the JS
# can decode the challenge into binary without too much trouble.
session['challenge'] = challenge.rstrip('=')
session['register_ukey'] = ukey
*make_credential_options = webauthn.WebAuthnMakeCredentialOptions(
challenge,RP_NAME,RP_ID,ukey,username,display_name,'https://example.com')*
return jsonify(make_credential_options.registration_dict)
这个函数可能也很有趣:
def verify_credential_info():
challenge = session['challenge']
username = session['register_username']
display_name = session['register_display_name']
ukey = session['register_ukey']
registration_response = request.form
trust_anchor_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)),TRUST_ANCHOR_DIR)
trusted_attestation_cert_required = True
self_attestation_permitted = True
none_attestation_permitted = True
webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
RP_ID,ORIGIN,registration_response,challenge,trust_anchor_dir,trusted_attestation_cert_required,self_attestation_permitted,none_attestation_permitted,uv_required=False) # User Verification
try:
webauthn_credential = webauthn_registration_response.verify()
except Exception as e:
return jsonify({'fail': 'Registration Failed. Error: {}'.format(e)})
# Step 17.
#
# Check that the credentialId is not yet registered to any other user.
# If registration is requested for a credential that is already registered
# to a different user,the Relying Party SHOULD fail this registration
# ceremony,or it MAY decide to accept the registration,e.g. while deleting
# the older registration.
credential_id_exists = User.query.filter_by(
credential_id=webauthn_credential.credential_id).first()
if credential_id_exists:
return make_response(
jsonify({
'fail': 'Credential ID already exists.'
}),401)
existing_user = User.query.filter_by(username=username).first()
if not existing_user:
if sys.version_info >= (3,0):
webauthn_credential.credential_id = str(
webauthn_credential.credential_id,"utf-8")
webauthn_credential.public_key = str(
webauthn_credential.public_key,"utf-8")
user = User(
ukey=ukey,username=username,display_name=display_name,pub_key=webauthn_credential.public_key,credential_id=webauthn_credential.credential_id,sign_count=webauthn_credential.sign_count,rp_id=RP_ID,icon_url='https://example.com')
db.session.add(user)
db.session.commit()
else:
return make_response(jsonify({'fail': 'User already exists.'}),401)
flash('Successfully registered as {}.'.format(username))
return jsonify({'success': 'User successfully registered.'})
第二次更新:下面是我得到的完整日志:
webauthn.js:101
{id: "ATdDPQneoYF3tA6HYW8_dr2eBDy53VNoEIHRWUDfnmT2URKIs0SQ_lQ7BujdmcfM9Hc2xNH8bvLf4k3lQJ-7RX4",rawId: "ATdDPQneoYF3tA6HYW8_dr2eBDy53VNoEIHRWUDfnmT2URKIs0SQ_lQ7BujdmcfM9Hc2xNH8bvLf4k3lQJ-7RX4",type: "public-key",attObj: "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFD32HDgTSvc6zIlggmLLxXTKQyiabSwuLWNiTpJ3WQfmMoC_qX_QTuWPWHo4",clientData: "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIj9pZFBhY2thZ2VOYW1lIjoiY29tLmFuZHJvaWQuY2hyb21lIn0", …}
attObj: "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFD32HDgTSvcJxUiuiT6ViS4biCWKTR25PIW3beO9V5NdFAAAAALk_2WHy5kYvsSKCACJH3ngAQQE3Qz0J3qGBd7QOh2FvP3a9ngQ8ud1TaBCB0VlA355k9lESiLNEkP5UOwbo3ZnHzPR3NsTR_G7y3-JN5UCfu0V-pQECAyYgASFYID93HTRf5UtMsCsW9D5TyWQDSgMW2MDhiYWKnz3sq16zIlggmLLxXTKQyiabSwuLWNiTpJ3WQfmMoC_qX_QTuWPWHo4"
clientData: "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoidFNOS3g5RnVyWFI4dlhVdVBkVms5azhDcEhlMWMydnlrbkdwYUhseXZKYyIsIm9yaWdpbiI6Imh0dHBzOlwvXC9uYWNlc2RlY2lkZS5oZXJva3VhcHAuY29tIiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoiY29tLmFuZHJvaWQuY2hyb21lIn0"
id: "ATdDPQneoYF3tA6HYW8_dr2eBDy53VNoEIHRWUDfnmT2URKIs0SQ_lQ7BujdmcfM9Hc2xNH8bvLf4k3lQJ-7RX4"
rawId: "ATdDPQneoYF3tA6HYW8_dr2eBDy53VNoEIHRWUDfnmT2URKIs0SQ_lQ7BujdmcfM9Hc2xNH8bvLf4k3lQJ-7RX4"
registrationClientExtensions: "{}"
type: "public-key"__proto__: Object
webauthn.js:107 Server validation of credential Failed: Registration Failed. Error: Registration rejected. Error: Unable to verify origin..
didClickRegister @ webauthn.js:107
async function (async)
didClickRegister @ webauthn.js:68
解决方法
我认为问题在于您的 ORIGIN
值有一个尾部斜杠。
Peering into the attestation response's cliendDataJSON,来源报告为"https://nacesdecide.herokuapp.com"
:
Looking at how the Duo WebAuthn library verifies this response,基本来源比较失败,因为您的 ORIGIN
的 "https://nacesdecide.herokuapp.com/"
不等于响应的来源:
Response: "https://nacesdecide.herokuapp.com"
ORIGIN: "https://nacesdecide.herokuapp.com/"
如果您删除尾部斜杠,那么我敢打赌一切都会按预期验证。
,@IAmKale 的回答解决了最初的问题。但是,重要的是要注意您可能会遇到 server error: unexpected token < in JSON at position 0
。我还没有找到具体的解决方案,但确保使用不同的 username
进行注册修复了它。此外,似乎多次注册需要不同的设备 - 每次注册一台设备。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。