接入 MFA

多因素身份验证(MFA)是一种安全系统,是为了验证一项操作合法性而实行多种身份验证。例如银行的 U 盾,异地登录要求手机短信验证。阅读本教程后,你可以基于 Authing 的 MFA 功能进行定制化开发。

前期准备

  1. 建议安装 http-server 用于快速启动 http 服务器来查看 html 文件。npm install -g http-server

所需技能

  1. 基础 HTML 知识

  2. 中级 Javascript 知识

新建一个空白 html 文件用于编写 Authing 程序

本教程只是为了演示,因此我们没选择高级框架,这可以让我们专注于 MFA 逻辑本身。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Authing MFA Example</title>
</head>
<body></body>
</html>

添加基本控件

为我们的 html 添加四个输入框和三个按钮。用于输入用户名、密码、验证码、MFA 口令等信息。我们使用 jquery 的插件来生成 MFA 令牌的二维码。

<div class="row">
<label>用户名:<input id="username"/></label>
<label>密码:<input id="password"/></label>
<label>验证码:<input id="verify-code"/><img id="verify-img" style="vertical-align: bottom;"/></label>
</div>
<div id="mfa" style="visibility: hidden;" class="row">
<label>MFA 口令:<input type="number" id="mfa-code"/></label>
</div>
<div class="row">
<input type="button" value="登录" id="login" onclick="handleLogin()" />
<input
type="button"
value="注册"
id="register"
onclick="handleRegister()"
/>
<input
type="button"
value="开启 MFA"
id="register"
onclick="handleChangeMFA()"
/>
</div>
<div id="qrcode" class="row"></div>
<div id="qrcode-info" style="visibility: hidden;" class="row">
请使用 MFA 令牌管理工具扫码添加令牌
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/authing-js-sdk"></script>

初始化 SDK

浏览器端初始化时需传入用户池 ID

<script>
const auth = new Authing({
userPoolId: 'your_userpool_id',
});
authing.login({...}).then(info => {})
authing.register({...}).then(info => {})
</script>

创建一个用户

将此函数绑定到登录按钮的点击事件上,用随机信息注册一个用户。

// 使用随机信息注册一个用户
async function handleRegister() {
let rand = Math.random()
.toString(36)
.slice(2);
let res = await validAuth.register({
email: rand + "@test.com",
password: "123456"
});
alert(JSON.stringify(res));
document.getElementById("username").value = res.email;
document.getElementById("password").value = "123456";
window.userId = res._id;
}

开启指定用户的 MFA

操作用户的 MFA 开启状态之前,必须要先获得用户的 token。可以通过 validAuth.login 函数以用户的身份登录,或者直接使用用户的 token 初始化。

// 开启用户的 MFA
async function handleChangeMFA() {
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let verifyCode = document.getElementById("verify-code").value;
try {
let res = await validAuth.login({
email: username,
password: password,
verifyCode: verifyCode
});
// 或者使用用户的 token 初始化
// authing.initUserClient('用户的 JWT token');
let mfa = await validAuth.changeMFA({
userId: res._id,
userPoolId: "5c95905578fce5000166f853",
enable: true
});
alert(JSON.stringify(mfa));
jQuery("#qrcode").qrcode(
`otpauth://totp/Authing:Test?secret=${mfa.shareKey}&period=30&digits=6&issuer=Authing`
);
document.getElementById("qrcode-info").style.visibility = "visible";
} catch (err) {
alert("失败消息:" + JSON.stringify(err));
}
}

生成 MFA 令牌二维码

社区有许多可以生成二维码的库,这里使用了 jquery 的二维码插件生成 MFA 二维码。

jQuery("#qrcode").qrcode(
`otpauth://totp/Authing:Test?secret=${mfa.shareKey}&period=30&digits=6&issuer=Authing`
);

处理用户登录逻辑

当调用 validAuth.login 函数进行登录时,如果检测到用户开启了 MFA 二次验证,而没有携带 MFA 口令,会抛出一个错误,错误代码为 1635。在错误处理函数中,携带 MFA 口令再次调用 validAuth.login 即可。

// 登录逻辑
async function handleLogin() {
// validAuth.login({email: })
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let verifyCode = document.getElementById("verify-code").value;
if (isMFAShow) {
let MFACode = document.getElementById("mfa-code").value;
try {
let res = await validAuth.login({
email: username,
password: password,
MFACode: MFACode,
verifyCode: verifyCode
});
alert("成功消息:" + JSON.stringify(res));
} catch (err) {
alert("失败消息:" + JSON.stringify(err));
if(err.message.code === 2000 || err.message.code === 2001 || err.message.code === 2006) {
console.log(err.message.data.url)
document.getElementById("verify-img").src = err.message.data.url
}
}
} else {
try {
let res = await validAuth.login({
email: username,
password: password,
verifyCode: verifyCode
});
alert("成功消息:" + JSON.stringify(res));
} catch (err) {
alert("失败消息:" + JSON.stringify(err));
if (err.message.code === 1635) {
showMFAInput();
}
if(err.message.code === 2000 || err.message.code === 2001 || err.message.code === 2006) {
console.log(err.message.data.url)
document.getElementById("verify-img").src = err.message.data.url
}
}
}
}

完整代码

Github: mfa-demo

运行方法

双击打开 index.html 文件。

或在项目目录启动一个 http 服务器。

$ npm install -g http-server
$ http-server

然后访问 127.0.0.1:8080。