Two-factor OTP authentication
For additional security, it is possible to turn on two-factor one-time-password authentication for the specified or all users in the setup.
Note
Two-factor authentication is not supported by the default mobile client evaHI. Consider creating dedicated mobile user accounts.
Contents
System setup
Services
Two-factor authentication is provided by OTP 2nd-Factor authentication service and supported by:

To start using OTP, the above services require otp_svc configuration parameter set to the deployed OTP service ID. Note that if the default domain is configured in Active directory auth service, user and user@domain are processed as two different users.
Excluding user accounts from OTP
Certain user accounts can be excluded from 2FA with:
exclude field of OTP 2nd-Factor authentication service configuration
by deploying additional authentication services, without otp_svc configured.
Resetting OTP for a user account
If a user has lost his OTP secret, the following service RPC call can be used to reset it. E.g. let us use eva-shell:
eva svc call eva.aaa.otp otp.destroy i=USER
User authentication
API
Use generic authentication functions to authenticate users. No special RPC/HTTP calls are required.
If a user is correctly authenticated with the password, but there is no OTP code provided, the authentication service returns an error with code -32022 (ACCESS_DENIED_MORE_DATA_REQUIRED) and the following message:
|OTP|<SVC_ID>|PAYLOAD
where <SVC_ID> is the OTP service ID and the payload is:
REQ OTP code is required (not provided)
INVALID OTP code is provided but invalid
SETUP=<SECRET> OTP setup is required, use the secret provided
To finish authentication, repeat the login process again, with an extra parameter in the payload:
{
// HTTP API or EAPI payload
"xopts": { "otp": "CODE" }
}
Authenticator programs
OTP 2nd-Factor authentication service provides the default HMAC-based Time-OTP with SHA-1 checksums and 6-digit code length.
Such TOTP is fully supported by the following programs out-of-the-box:
and other compatible.
HMI application example
OTP is supported out-of-the-box by EVA JS Framework, starting from the version 0.3.38.
Here is an example of two-factor login logic in HMI web application:
<html>
<head><script type="text/javascript" src="eva.min.js"></script>
<body>
<div id="status"></div>
<div id="qr_container" style="width: 200px; height: 200px; display: none">
<canvas id="qr"></canvas>
</div>
<div id="login_form">
<form onsubmit="process_login(event)">
User: <input type="text" id="login"><br>
Password: <input type="password" id="password"><br>
<input type="submit" value="Login">
</form>
</div>
<div id="otp_form" style="display: none">
<form onsubmit="process_otp(event)">
OTP: <input type="text" id="otp_code"><br>
<input type="submit" value="Continue">
</form>
</div>
<script type="text/javascript">
function process_login(event) {
event.preventDefault();
set_status("Logging in...");
$eva.login = document.getElementById("login").value;
$eva.password = document.getElementById("password").value;
hide("login_form");
$eva.start();
return false;
}
function process_otp(event) {
event.preventDefault();
set_status("Logging in...");
$eva.login_xopts = { otp: document.getElementById("otp_code").value };
hide("otp_form");
$eva.start();
return false;
}
function hide(id) {
document.getElementById(id).style.display = "none";
}
function show(id) {
document.getElementById(id).style.display = "block";
}
function focus(id) {
document.getElementById(id).focus();
}
function clear(id) {
document.getElementById(id).value = "";
}
function set_status(msg) {
document.getElementById("status").innerHTML = msg;
}
focus("login");
$eva.on("login.success", () => {
// hide QR container in case if OTP setup process was going
hide("qr_container");
set_status("Logged in as " + $eva.server_info.aci.u);
// do not forget to clear the password in DOM and framework variables
clear("password");
$eva.password = "";
$eva.login_xopts = null;
});
$eva.on("login.failed", (err) => {
set_status(`Login failed: ${err.message} ${err.code}`);
show("login_form");
clear("password");
focus("password");
});
$eva.on("login.otp_required", () => {
show("otp_form");
set_status("OTP code required");
focus("otp_code");
});
$eva.on("login.otp_invalid", () => {
show("otp_form");
set_status("Invalid OTP code entered");
clear("otp_code");
focus("otp_code");
});
$eva.on("login.otp_setup", (msg) => {
show("qr_container");
show("otp_form");
set_status("Scan this code with an authenticator app");
$eva.otpQR("qr", msg.value);
focus("otp_code");
});
</script>
</body>
</html>