Django+Djoser集成WebAuthn时签名验证失败问题
解决WebAuthn注册签名验证失败问题
核心问题定位
你遇到的Authentication rejected. Error: Invalid signature received.错误,主要是因为后端验证时缺少关键的user_id参数,导致签名验证不匹配。以下是具体修复步骤:
1. 补充WebAuthnRegistrationResponse的user_id参数
在SignUpView的post方法中,创建WebAuthnRegistrationResponse时必须传入user_id=co.ukey——因为前端注册时使用的user_id就是这个值,后端验证时需要对应上才能通过签名校验:
webauthn_registration_response = WebAuthnRegistrationResponse( rp_id=settings.DJOSER["WEBAUTHN"]["RP_ID"], origin=settings.DJOSER["WEBAUTHN"]["ORIGIN"], registration_response=request.data, challenge=co.challenge, user_id=co.ukey, # 新增此行,与注册请求时的user_id一致 none_attestation_permitted=True, )
2. 从CredentialOptions安全获取用户名
避免直接从request.data取用户名,改用已验证过的co.username,防止前端篡改:
# 替换原代码中的user获取逻辑 user = User.objects.get(username=co.username)
3. 修正凭证数据的存储格式(潜在问题)
直接对credential_id和public_key调用decode()会导致字节数据存储错误,改用base64 URL安全编码存储,和Djoser原实现保持一致:
import base64 # 替换原存储逻辑 co.credential_id = base64.urlsafe_b64encode(webauthn_credential.credential_id).decode("utf-8") co.public_key = base64.urlsafe_b64encode(webauthn_credential.public_key).decode("utf-8")
4. 验证前端传递的响应完整性
确保前端使用@simplewebauthn/browser返回的完整registrationResponse直接传递给后端,不要修改signature、userHandle等核心字段。示例前端代码:
import { startRegistration } from "@simplewebauthn/browser"; // 获取注册选项 const registrationOptions = await fetch("/webauthn/signup/request", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "你的用户名", display_name: "你的昵称" }), }).then(res => res.json()); // 发起WebAuthn注册 const registrationResponse = await startRegistration(registrationOptions); // 提交注册结果 await fetch(`/webauthn/signup/${registrationOptions.user.id}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(registrationResponse), });
5. 确认RP ID和Origin配置正确
检查settings.DJOSER["WEBAUTHN"]中的配置:
RP_ID必须是不带协议、端口的域名,例如"example.com"(开发环境用"localhost")ORIGIN必须是前端完整的源,例如"https://example.com"或"http://localhost:4200"
两者不匹配会直接导致签名验证失败。
内容的提问来源于stack exchange,提问作者thelittlewozniak
相关产品推荐
相关产品推荐

