Source: GitHub / [OneTimePassword] 一次性密碼演算法:簡介HOTP、TOTP和Google Authenticator | 全端開發人員天梯
<!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>OTP Demo</title>
</head>
<body>
<h1>Options</h1>
<p>
<label for="secret">Secret Key</label>
<input name="secret" id="secret" size="64" />
<button id="generateSecret">Generate Secret</button>
</p>
<h2>HOTP</h2>
<p>
<label for="counter">Counter (Message)</label>
<input name="counter" id="counter" size="64" value="" placeholder="any number...">
<button id="generateHoptPassword">Generate HOPT password</button>
</p>
<p>
HOPT Password:<input id="hotpPassword" />
</p>
<p>
<button id="verifyHotp">Verify</button> Result: <span id="verifyHotpResult"></span>
</p>
<h2>TOTP</h2>
<p>
<label for="tc">TC</label>
<input name="tc" id="tc" value="30">
</p>
<p>
TOTP Password:<input id="totpPassword" />
</p>
<p>
<span id="timeLeft"></span> sec left.
</p>
<p>
<button id="verifyTotp">Verify</button> Result: <span id="verifyTotpResult"></span>
</p>
<h2>Google Authenticator</h2>
<p>
<label for="user">User</label>
<input name="user" id="user" value="test user name">
</p>
<p>
<label for="service">Service Name</label>
<input name="service" id="service" value="Awesome OTP">
</p>
<p>
<img id="qrcode" />
</p>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/otplib@^6.0.0/otplib-browser.js"></script>
<script>
$(document).ready(function () {
var qrCodeApi = 'https://chart.googleapis.com/chart?cht=qr&chs=500x500&chl=';
function generateHoptPassword() {
var token = otplib.hotp.generate($('#secret').val(), $('#counter').val());
$('#hotpPassword').val(token);
}
function generateTotpPassword() {
otplib.authenticator.options = {
step: parseInt($('#tc').val())
};
var token = otplib.authenticator.generate($('#secret').val());
$('#totpPassword').val(token);
var authParam = otplib.authenticator.keyuri(
encodeURIComponent($('#user').val()),
encodeURIComponent($('#service').val()),
$('#secret').val());
var url = qrCodeApi + encodeURIComponent(authParam);
$('#qrcode').attr('src', url);
}
function checkTimeLeft() {
var tc = parseInt($('#tc').val());
var epoch = Math.floor(new Date().getTime() / 1000);
var count = epoch % tc;
var timeLeft = tc - count;
$('#timeLeft').text(timeLeft);
if (timeLeft === tc) {
generateTotpPassword();
}
}
var refreshTotpHandle = setInterval(checkTimeLeft, 1000);
generateTotpPassword();
checkTimeLeft();
$('#generateSecret').click(function () {
var secret = otplib.authenticator.generateSecret();
$('#secret').val(secret);
generateHoptPassword();
generateTotpPassword();
});
$('#generateHoptPassword').click(function () {
generateHoptPassword();
});
$('#verifyHotp').click(function () {
var result = otplib.hotp.verify({
token: $('#hotpPassword').val(),
secret: $('#secret').val(),
counter: $('#counter').val()
});
if (result) {
$('#verifyHotpResult').text('pass');
} else {
$('#verifyHotpResult').text('fail');
}
});
$('#verifyTotp').click(function () {
var result = otplib.authenticator.verify({
secret: $('#secret').val(),
token: $('#totpPassword').val()
});
if (result) {
$('#verifyTotpResult').text('pass');
} else {
$('#verifyTotpResult').text('fail');
}
})
$('#generateSecret').trigger('click');
});
</script>
</body>
</html>