UI: скрыть пароль при режиме 12 слов
This commit is contained in:
parent
dd35e56029
commit
ecc9efd434
@ -34,8 +34,8 @@ import {
|
|||||||
|
|
||||||
import * as startView from './pages/start-view.js?v=202606142105';
|
import * as startView from './pages/start-view.js?v=202606142105';
|
||||||
import * as entrySettingsView from './pages/entry-settings-view.js?v=202606161240';
|
import * as entrySettingsView from './pages/entry-settings-view.js?v=202606161240';
|
||||||
import * as registerView from './pages/register-view.js';
|
import * as registerView from './pages/register-view.js?v=202606201650';
|
||||||
import * as registrationFaqView from './pages/registration-faq-view.js';
|
import * as registrationFaqView from './pages/registration-faq-view.js?v=202606201650';
|
||||||
import * as registrationPaymentView from './pages/registration-payment-view.js?v=202606180940';
|
import * as registrationPaymentView from './pages/registration-payment-view.js?v=202606180940';
|
||||||
import * as registrationKeysView from './pages/registration-keys-view.js';
|
import * as registrationKeysView from './pages/registration-keys-view.js';
|
||||||
import * as registrationDraftKeysView from './pages/registration-draft-keys-view.js';
|
import * as registrationDraftKeysView from './pages/registration-draft-keys-view.js';
|
||||||
@ -44,7 +44,7 @@ import * as devnetTopupView from './pages/devnet-topup-view.js';
|
|||||||
import * as loginView from './pages/login-view.js?v=202606150110';
|
import * as loginView from './pages/login-view.js?v=202606150110';
|
||||||
import * as loginCameraView from './pages/login-camera-view.js';
|
import * as loginCameraView from './pages/login-camera-view.js';
|
||||||
import * as loginOtherDeviceView from './pages/login-other-device-view.js?v=202606180940';
|
import * as loginOtherDeviceView from './pages/login-other-device-view.js?v=202606180940';
|
||||||
import * as loginPasswordView from './pages/login-password-view.js';
|
import * as loginPasswordView from './pages/login-password-view.js?v=202606201650';
|
||||||
import * as keyStorageView from './pages/key-storage-view.js';
|
import * as keyStorageView from './pages/key-storage-view.js';
|
||||||
|
|
||||||
import * as profileView from './pages/profile-view.js';
|
import * as profileView from './pages/profile-view.js';
|
||||||
|
|||||||
@ -53,7 +53,7 @@ function createWordsLayout({ words, onInput }) {
|
|||||||
const preview = document.createElement('p');
|
const preview = document.createElement('p');
|
||||||
preview.className = 'status-line';
|
preview.className = 'status-line';
|
||||||
|
|
||||||
section.append(grid, hint, preview);
|
section.append(grid, hint);
|
||||||
return { section, inputs, preview };
|
return { section, inputs, preview };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,24 +119,13 @@ export function render({ navigate }) {
|
|||||||
hint.className = 'meta-muted';
|
hint.className = 'meta-muted';
|
||||||
hint.textContent = 'Введите логин. На следующем шаге сохраните ключи на устройстве.';
|
hint.textContent = 'Введите логин. На следующем шаге сохраните ключи на устройстве.';
|
||||||
|
|
||||||
const advanced = document.createElement('details');
|
|
||||||
advanced.className = 'card stack';
|
|
||||||
advanced.innerHTML = `
|
|
||||||
<summary>Расширенные</summary>
|
|
||||||
<p class="meta-muted">Схема деривации: логин нормализуется как trim().toLowerCase(). При непустом пароле используется Argon2id, затем из результата строится секрет.</p>
|
|
||||||
<p class="meta-muted">Из секрета строятся root key, blockchain key и device key. Обычно можно не вникать в это подробно и просто хранить всё на своём устройстве.</p>
|
|
||||||
<p class="meta-muted">Режим 12 слов ничего не меняет в протоколе: слова просто склеиваются в один обычный пароль длиной до 256 символов.</p>
|
|
||||||
<p class="meta-muted">Если пароль пустой — используется прежний детерминированный режим совместимости.</p>
|
|
||||||
<p class="meta-muted">Профиль Argon2id сейчас фиксированный: t=2, m=65536 KiB (64 MiB), p=1, dkLen=32.</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const status = document.createElement('p');
|
const status = document.createElement('p');
|
||||||
status.className = 'status-line is-unavailable';
|
status.className = 'status-line is-unavailable';
|
||||||
status.style.display = 'none';
|
status.style.display = 'none';
|
||||||
|
|
||||||
const testLoginsHint = document.createElement('p');
|
let passwordField = null;
|
||||||
testLoginsHint.className = 'meta-muted';
|
const passwordLengthText = document.createElement('p');
|
||||||
testLoginsHint.textContent = 'Основные тестовые логины: 1, 2, 3 (вход без пароля).';
|
passwordLengthText.className = 'status-line';
|
||||||
|
|
||||||
function getCurrentPassword() {
|
function getCurrentPassword() {
|
||||||
return passwordMode === 'words' ? composePasswordFromWords(passwordWords) : String(passwordInput.value || '');
|
return passwordMode === 'words' ? composePasswordFromWords(passwordWords) : String(passwordInput.value || '');
|
||||||
@ -150,15 +139,17 @@ export function render({ navigate }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateWordsPreview() {
|
function updateWordsPreview() {
|
||||||
const password = composePasswordFromWords(passwordWords);
|
const password = getCurrentPassword();
|
||||||
const nonEmptyCount = normalizePasswordWords(passwordWords).filter((word) => word.trim()).length;
|
const text = `Итоговая длина пароля: ${password.length} символов.`;
|
||||||
wordsPreview.textContent = `Заполнено слов: ${nonEmptyCount} из 12 · итоговая длина пароля: ${password.length} символов.`;
|
wordsPreview.textContent = text;
|
||||||
|
passwordLengthText.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePasswordModeVisibility() {
|
function updatePasswordModeVisibility() {
|
||||||
const wordsMode = passwordMode === 'words';
|
const wordsMode = passwordMode === 'words';
|
||||||
wordsSection.hidden = !wordsMode;
|
wordsSection.style.display = wordsMode ? 'grid' : 'none';
|
||||||
passwordInput.parentElement.hidden = wordsMode;
|
if (passwordField) passwordField.style.display = wordsMode ? 'none' : 'grid';
|
||||||
|
passwordInput.style.display = wordsMode ? 'none' : '';
|
||||||
updateWordsPreview();
|
updateWordsPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,8 +158,9 @@ export function render({ navigate }) {
|
|||||||
<label class="stack"><span class="field-label">Пароль</span></label>
|
<label class="stack"><span class="field-label">Пароль</span></label>
|
||||||
`;
|
`;
|
||||||
form.children[0].append(loginInput);
|
form.children[0].append(loginInput);
|
||||||
form.children[1].append(passwordInput);
|
passwordField = form.children[1];
|
||||||
form.append(passwordModeToggle, wordsSection, hint, advanced, status, testLoginsHint);
|
passwordField.append(passwordInput);
|
||||||
|
form.append(passwordModeToggle, wordsSection, passwordLengthText, hint, status);
|
||||||
updatePasswordModeVisibility();
|
updatePasswordModeVisibility();
|
||||||
syncDraftState();
|
syncDraftState();
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
PASSWORD_MAX_LENGTH,
|
PASSWORD_MAX_LENGTH,
|
||||||
PASSWORD_WORDS_COUNT,
|
PASSWORD_WORDS_COUNT,
|
||||||
} from '../services/password-words.js';
|
} from '../services/password-words.js';
|
||||||
import { openRegistrationFaq, REGISTRATION_FAQ_TOPICS } from './registration-faq-view.js';
|
import { openRegistrationFaq } from './registration-faq-view.js';
|
||||||
|
|
||||||
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
|
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ function createWordsLayout({ words, onInput }) {
|
|||||||
const preview = document.createElement('p');
|
const preview = document.createElement('p');
|
||||||
preview.className = 'status-line';
|
preview.className = 'status-line';
|
||||||
|
|
||||||
section.append(grid, hint, preview);
|
section.append(grid, hint);
|
||||||
return { section, inputs, preview };
|
return { section, inputs, preview };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,35 +137,20 @@ export function render({ navigate }) {
|
|||||||
|
|
||||||
const faqText = document.createElement('p');
|
const faqText = document.createElement('p');
|
||||||
faqText.className = 'meta-muted';
|
faqText.className = 'meta-muted';
|
||||||
faqText.textContent = 'Нажмите на вопрос, чтобы открыть отдельный экран с кратким объяснением.';
|
faqText.textContent = 'Если хотите подробнее понять схему деривации, ключи, первый сервер и формат 12 слов, откройте отдельный экран с вопросами.';
|
||||||
|
|
||||||
const faqButtons = document.createElement('div');
|
const faqButton = document.createElement('button');
|
||||||
faqButtons.className = 'registration-faq-grid';
|
faqButton.className = 'ghost-btn';
|
||||||
REGISTRATION_FAQ_TOPICS.forEach((topic) => {
|
faqButton.type = 'button';
|
||||||
const button = document.createElement('button');
|
faqButton.textContent = 'Частые вопросы';
|
||||||
button.className = 'ghost-btn';
|
faqButton.addEventListener('click', () => openRegistrationFaq(navigate, 'key-derivation'));
|
||||||
button.type = 'button';
|
|
||||||
button.textContent = topic.shortTitle;
|
faqCard.append(faqTitle, faqText, faqButton);
|
||||||
button.addEventListener('click', () => openRegistrationFaq(navigate, topic.id));
|
|
||||||
faqButtons.append(button);
|
|
||||||
});
|
|
||||||
faqCard.append(faqTitle, faqText, faqButtons);
|
|
||||||
|
|
||||||
const formError = document.createElement('p');
|
const formError = document.createElement('p');
|
||||||
formError.className = 'status-line is-unavailable';
|
formError.className = 'status-line is-unavailable';
|
||||||
formError.style.display = 'none';
|
formError.style.display = 'none';
|
||||||
|
|
||||||
const advanced = document.createElement('details');
|
|
||||||
advanced.className = 'card stack';
|
|
||||||
advanced.innerHTML = `
|
|
||||||
<summary>Расширенные</summary>
|
|
||||||
<p class="meta-muted">Схема деривации: логин и пароль проходят через Argon2id, после чего получается главный секрет.</p>
|
|
||||||
<p class="meta-muted">Из этого секрета строятся три ключа: root key для основной публичной записи и важных изменений, blockchain key для подписания действий SHiNE в блокчейне, device key для входа и работы конкретного устройства.</p>
|
|
||||||
<p class="meta-muted">Разделение нужно, чтобы можно было аккуратнее выдавать права устройствам. Но если у вас нет большой суммы на счёте и нет повышенного риска, обычно можно просто хранить всё на своём устройстве.</p>
|
|
||||||
<p class="meta-muted">Режим 12 слов не меняет формат пароля и не меняет API: слова просто склеиваются в одну строку длиной до 256 символов.</p>
|
|
||||||
<p class="meta-muted">Профиль Argon2id сейчас фиксированный: t=2, m=65536 KiB (64 MB), p=1, dkLen=32.</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const checkButton = document.createElement('button');
|
const checkButton = document.createElement('button');
|
||||||
checkButton.className = 'ghost-btn';
|
checkButton.className = 'ghost-btn';
|
||||||
checkButton.type = 'button';
|
checkButton.type = 'button';
|
||||||
@ -185,6 +170,9 @@ export function render({ navigate }) {
|
|||||||
nextButton.type = 'button';
|
nextButton.type = 'button';
|
||||||
nextButton.textContent = 'Далее';
|
nextButton.textContent = 'Далее';
|
||||||
|
|
||||||
|
let passwordField = null;
|
||||||
|
const passwordLengthText = document.createElement('p');
|
||||||
|
passwordLengthText.className = 'status-line';
|
||||||
let lastCheckedLogin = '';
|
let lastCheckedLogin = '';
|
||||||
let lastCheckedFree = false;
|
let lastCheckedFree = false;
|
||||||
let lastCheckedClassName = '';
|
let lastCheckedClassName = '';
|
||||||
@ -195,15 +183,17 @@ export function render({ navigate }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateWordsPreview() {
|
function updateWordsPreview() {
|
||||||
const password = composePasswordFromWords(passwordWords);
|
const password = getCurrentPassword();
|
||||||
const nonEmptyCount = normalizePasswordWords(passwordWords).filter((word) => word.trim()).length;
|
const text = `Итоговая длина пароля: ${password.length} символов.`;
|
||||||
wordsPreview.textContent = `Заполнено слов: ${nonEmptyCount} из 12 · итоговая длина пароля: ${password.length} символов.`;
|
wordsPreview.textContent = text;
|
||||||
|
passwordLengthText.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePasswordModeVisibility() {
|
function updatePasswordModeVisibility() {
|
||||||
const wordsMode = passwordMode === 'words';
|
const wordsMode = passwordMode === 'words';
|
||||||
wordsSection.hidden = !wordsMode;
|
wordsSection.style.display = wordsMode ? 'grid' : 'none';
|
||||||
passwordInput.parentElement.hidden = wordsMode;
|
if (passwordField) passwordField.style.display = wordsMode ? 'none' : 'grid';
|
||||||
|
passwordInput.style.display = wordsMode ? 'none' : '';
|
||||||
updateWordsPreview();
|
updateWordsPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,9 +355,11 @@ export function render({ navigate }) {
|
|||||||
<label class="stack"><span class="field-label">Логин</span></label>
|
<label class="stack"><span class="field-label">Логин</span></label>
|
||||||
<label class="stack registration-password-single"><span class="field-label">Пароль</span></label>
|
<label class="stack registration-password-single"><span class="field-label">Пароль</span></label>
|
||||||
`;
|
`;
|
||||||
form.children[0].append(loginInput);
|
const loginField = form.children[0];
|
||||||
form.children[1].append(passwordInput);
|
passwordField = form.children[1];
|
||||||
form.append(passwordModeToggle, wordsSection, serverNotice, checkButton, statusText, faqCard, advanced, formError);
|
loginField.append(loginInput);
|
||||||
|
passwordField.append(passwordInput);
|
||||||
|
form.append(passwordModeToggle, wordsSection, passwordLengthText, serverNotice, checkButton, statusText, faqCard, formError);
|
||||||
actions.innerHTML = '';
|
actions.innerHTML = '';
|
||||||
actions.append(backButton, nextButton);
|
actions.append(backButton, nextButton);
|
||||||
updatePasswordModeVisibility();
|
updatePasswordModeVisibility();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user