背景
最近由于业务需要, 要求在odoo中支持FIDO2的认证方式, 即在用户账户绑定FIDO2兼容的Authenticator(通常为USB key), 需要用户在正常的帐号密码登录之后, 使用USB key做二次身份确认.
调查研究
当前项目使用的odoo版本是14.0 community 版本, 而OCA 的14.0 版本的server-auth 项目并不包含FIDO2 或者U2F的认证支持, 不过在 12.0 版本 中发现有 auth_u2f 插件 实现, 所以决定从改造 12.0 版本的auth_u2f 开始, 最终目标是FIDO2 与 odoo14 的集成
过程
整体的改造过程如下:
准备工作
由于FIDO2认证需要网站工作在TLS/HTTPS 下, 所以本地测试需要准备:
通过openssl生成网站需要的证书和私钥:
certificate.pem以及key.pem, 方法请自行百(goo)度(gle) , 在nginx配置中会用到搭建一个nginx反向代理, 关键的配置如下, 其中的
proxy_set_header X-Forwarded-HTTPS on是解决odoo redirect 操作导致浏览器使用回HTTP请求的问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17server {
listen 443 ssl;
proxy_set_header X-Forwarded-Proto $scheme;
ssl_certificate /usr/local/nginx/cert/certificate.pem; #cert
ssl_certificate_key /usr/local/nginx/cert/key.pem; #key
ssl_session_timeout 5m; #time out
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #supported algorithm
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #supported tls version
ssl_prefer_server_ciphers on; #use server prefered
...
location / {
proxy_pass http://localhost:8069;
proxy_set_header X-Forwarded-HTTPS on;
}
}在odoo的系统参数中配置
web.base.url以及web.base.url.freezeweb.base.url -> https://localhost
web.base.url.freeze -> True
完成之后可以测试新的https . 接下来就可以开始改造认证部分
auth_u2f -> odoo14
这部分工作主要集中在 auth_u2f 到odoo 14
- 下载auth_u2f插件到本地的插件目录
- 按照插件的说明安装依赖包
pip install python-u2flib-server - 去掉方法上不支持的装饰器
@api.multi - 按照标准插件安装方式安装插件
不出其他意外的话, auth_u2f 应该可以在系统中运行起来, 唯一遇到的一个问题是, 给当前用户新绑定usb key的时候, 会导致当前session出现登录失败的情况, 可能导致设备绑定出现问题, 修改方式也很简单, 修改controller ,如果发现用户已经登录, 不强制验证密钥
1 |
|
测试后发现在 Firefox中能工作正常, 不过在chrome 中发现js错误, 由u2f-api.js 代码引起, 报extension: kmendfapggjehodndflmmgagdbamhnfd 不存在, 这个扩展应该是chrome 内置的扩展, 调查后发现应该和chrome放弃U2F支持有关 Deprecate U2F API (Cryptotoken), sighhhh! 还是Firefox耿直,所以接下来势必要转战 FIDO2
FIDO2 in odoo 14
和U2F的认证方式很类似, 包含注册部分
和认证部分
改造步骤如下:
替换对应的后端和前端U2F支持库到FIDO2的支持库, 这里选用的是 py_webauthn (server) 以及 SimpleWebAuthn (client) , 安装方式请参考对应项目文档.
后端
- device的模型中增加
credential(可选),credential_id, 以及credential_pub_key字段 - 在设备注册response verify通过后, 从对应的verify_registration_response 中解析出
credential_id和credential_pub_key并保存到对应的device record中 - 在登录时从device record中拿到creddential_id, 作为 allow_credentials 放到AuthenticationOption中
- 验证时从device记录中拿
credential_pub_key做验证
- device的模型中增加
前端的改动就比较简单, 替换auth_u2f.js 和auth_uf_frontend.js中对应的u2f-api的register 和 sign 方法为SimpleWebAuthn的startRegistration 和 startAuthentication即可
至此chrome 和 firefox都可以支持
相关网站连接
Some useful links
- awesome-webauthn: https://github.com/herrjemand/awesome-webauthn , 包含了很多关于WebAuthn的资源连接
- py_webauthn: https://github.com/duo-labs/py_webauthn 本文最终使用的 webauthn server 库
- SimpleWebAuthn: https://github.com/MasterKale/SimpleWebAuthn 本文最终使用的 webauthn client 库
- Auth0: https://webauthn.me/ Demo网站, 可以直观了解FIDO2的认证流程
- 谈谈WebAuthn: https://flyhigher.top/develop/2160.html