Implement OTP page in config
a
This commit is contained in:
@@ -766,8 +766,8 @@ namespace confighttp {
|
||||
}
|
||||
|
||||
outputTree.put("otp", nvhttp::request_otp(it->second));
|
||||
outputTree.put("statue", true);
|
||||
outputTree.put("message", "OTP created, effective within 1 minute.");
|
||||
outputTree.put("status", true);
|
||||
outputTree.put("message", "OTP created, effective within 3 minutes.");
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "OTP creation failed: "sv << e.what();
|
||||
|
||||
@@ -619,10 +619,10 @@ namespace nvhttp {
|
||||
tree.put("root.<xmlattr>.status_code", 503);
|
||||
tree.put("root.<xmlattr>.status_message", "OTP auth not available.");
|
||||
} else {
|
||||
auto hash = util::hex(crypto::hash(one_time_pin + ptr->second.async_insert_pin.salt + otp_passphrase));
|
||||
auto hash = util::hex(crypto::hash(one_time_pin + ptr->second.async_insert_pin.salt + otp_passphrase), true);
|
||||
|
||||
if (hash.to_string_view() == it->second) {
|
||||
getservercert(ptr->second, tree, one_time_pin);
|
||||
pin(one_time_pin, deviceName);
|
||||
one_time_pin.clear();
|
||||
otp_passphrase.clear();
|
||||
return;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace nvhttp {
|
||||
*/
|
||||
constexpr auto PORT_HTTPS = -5;
|
||||
|
||||
constexpr auto OTP_EXPIRE_DURATION = 60s;
|
||||
constexpr auto OTP_EXPIRE_DURATION = 180s;
|
||||
|
||||
/**
|
||||
* @brief Start the nvhttp server.
|
||||
|
||||
@@ -3,23 +3,49 @@
|
||||
|
||||
<head>
|
||||
<%- header %>
|
||||
|
||||
<style scoped type="text/css">
|
||||
.content-container {
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.pin-tab-bar {
|
||||
margin-bottom: 2em !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body id="app" v-cloak>
|
||||
<Navbar></Navbar>
|
||||
<div id="content" class="container">
|
||||
<h1 class="my-4 text-center">{{ $t('pin.pin_pairing') }}</h1>
|
||||
<form class="form d-flex flex-column align-items-center" id="form" @submit.prevent="registerDevice">
|
||||
<div id="content" class="container content-container">
|
||||
<ul class="nav nav-pills nav-fill pin-tab-bar">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" :class="{active: currentTab === 'OTP'}" @click="currentTab = 'OTP'">{{ $t('pin.otp_pairing') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" :class="{active: currentTab === 'PIN'}" @click="currentTab = 'PIN'">{{ $t('pin.pin_pairing') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form v-if="currentTab === 'OTP'" class="form d-flex flex-column align-items-center" @submit.prevent="requestOTP">
|
||||
<div class="card flex-column d-flex p-4 mb-4">
|
||||
<h1 class="my-4 text-center">{{ otp && otp || '????' }}</h1>
|
||||
<input type="text" pattern="[0-9a-zA-Z]{4,}" :placeholder="`${$t('pin.otp_passphrase')}`" v-model="passphrase" required autofocus class="form-control my-4" />
|
||||
<button class="btn btn-primary">{{ $t('pin.generate_pin') }}</button>
|
||||
</div>
|
||||
<div v-if="otpMessage" class="alert" :class="['alert-' + otpStatus]">{{ otpMessage }}</div>
|
||||
<div class="alert alert-info">{{ $t('pin.otp_msg') }}</div>
|
||||
</form>
|
||||
<form v-if="currentTab === 'PIN'" class="form d-flex flex-column align-items-center" id="form" @submit.prevent="registerDevice">
|
||||
<div class="card flex-column d-flex p-4 mb-4">
|
||||
<input type="text" pattern="\d*" :placeholder="`${$t('navbar.pin')}`" autofocus id="pin-input" class="form-control mt-2" required />
|
||||
<input type="text" :placeholder="`${$t('pin.device_name')}`" id="name-input" class="form-control my-4" />
|
||||
<button class="btn btn-primary">{{ $t('pin.send') }}</button>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<b>{{ $t('_common.warning') }}</b> {{ $t('pin.warning_msg') }}
|
||||
</div>
|
||||
<div id="status"></div>
|
||||
</form>
|
||||
<div class="alert alert-warning">
|
||||
<b>{{ $t('_common.warning') }}</b> {{ $t('pin.warning_msg') }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -28,11 +54,22 @@
|
||||
import { initApp } from './init'
|
||||
import Navbar from './Navbar.vue'
|
||||
|
||||
let resetOTPTimeout = null;
|
||||
|
||||
let app = createApp({
|
||||
components: {
|
||||
Navbar
|
||||
},
|
||||
inject: ['i18n'],
|
||||
data() {
|
||||
return {
|
||||
currentTab: 'OTP',
|
||||
otp: '',
|
||||
passphrase: '',
|
||||
otpMessage: '',
|
||||
otpStatus: 'warning'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
registerDevice(e) {
|
||||
let pin = document.querySelector("#pin-input").value;
|
||||
@@ -54,6 +91,29 @@
|
||||
).innerHTML = `<div class="alert alert-danger" role="alert">${this.i18n.t('pin.pair_failure')}</div>`;
|
||||
}
|
||||
});
|
||||
},
|
||||
requestOTP() {
|
||||
fetch(`/api/otp?passphrase=${this.passphrase}`)
|
||||
.then(resp => resp.json())
|
||||
.then(resp => {
|
||||
if (resp.status !== 'true') {
|
||||
this.otpMessage = resp.message
|
||||
this.otpStatus = 'danger'
|
||||
return
|
||||
}
|
||||
|
||||
this.otp = resp.otp
|
||||
this.otpStatus = 'success'
|
||||
this.otpMessage = this.i18n.t('pin.otp_success')
|
||||
|
||||
if (resetOTPTimeout !== null) clearTimeout(resetOTPTimeout)
|
||||
resetOTPTimeout = setTimeout(() => {
|
||||
this.otp = this.i18n.t('pin.otp_expired')
|
||||
this.otpMessage = this.i18n.t('pin.otp_expired_msg')
|
||||
this.otpStatus = 'warning'
|
||||
resetOTPTimeout = null
|
||||
}, 3 * 60 * 1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -373,7 +373,14 @@
|
||||
"pair_success": "Success! Please check Moonlight to continue",
|
||||
"pin_pairing": "PIN Pairing",
|
||||
"send": "Send",
|
||||
"warning_msg": "Make sure you have access to the client you are pairing with. This software can give total control to your computer, so be careful!"
|
||||
"warning_msg": "Make sure you have access to the client you are pairing with. This software can give total control to your computer, so be careful!",
|
||||
"otp_pairing": "OTP Pairing",
|
||||
"generate_pin": "Generate PIN",
|
||||
"otp_passphrase": "One Time Passphrase",
|
||||
"otp_expired": "EXPIRED",
|
||||
"otp_expired_msg": "OTP expired. Please request a new one.",
|
||||
"otp_success": "PIN request success, the PIN is available within 3 minutes.",
|
||||
"otp_msg": "OTP pairing is only available for Artemis clients. Please use legacy pairing method for other clients."
|
||||
},
|
||||
"resource_card": {
|
||||
"github_discussions": "GitHub Discussions",
|
||||
|
||||
@@ -374,7 +374,14 @@
|
||||
"pair_success": "成功!请检查 Moonlight 以继续",
|
||||
"pin_pairing": "PIN 码配对",
|
||||
"send": "发送",
|
||||
"warning_msg": "请确保您可以掌控您正在配对的客户端。该软件可以完全控制您的计算机,请务必小心!"
|
||||
"warning_msg": "请确保您可以掌控您正在配对的客户端。该软件可以完全控制您的计算机,请务必小心!",
|
||||
"otp_pairing": "OTP 配对",
|
||||
"generate_pin": "生成 PIN",
|
||||
"otp_passphrase": "一次性口令",
|
||||
"otp_expired": "已过期",
|
||||
"otp_expired_msg": "口令已过期,请重新请求。",
|
||||
"otp_success": "一次性 PIN 请求成功,3分钟内有效。",
|
||||
"otp_msg": "一次性口令目前仅支持 Artemis 客户端使用。其他客户端请使用传统配对方式。"
|
||||
},
|
||||
"resource_card": {
|
||||
"github_discussions": "Github 讨论区",
|
||||
|
||||
Reference in New Issue
Block a user