Merge pull request #5 from ai5590/codex/connect-ui-client-to-server-for-authentication-8njihj
Add AuthService, WS client and key-vault; implement session-based auth flow and update auth UI/pages
This commit is contained in:
commit
089146a137
@ -1,7 +1,7 @@
|
||||
import { navigate, getRoute, PRE_AUTH_PAGES } from './router.js?v=20260327192619';
|
||||
import { renderToolbar } from './components/toolbar.js?v=20260327192619';
|
||||
import { renderPageLabel } from './components/page-label.js?v=20260327192619';
|
||||
import { state, togglePageLabel } from './state.js?v=20260327192619';
|
||||
import { authService, authorizeSession, refreshSessions, state, togglePageLabel } from './state.js?v=20260327192619';
|
||||
|
||||
import * as startView from './pages/start-view.js?v=20260327192619';
|
||||
import * as entrySettingsView from './pages/entry-settings-view.js?v=20260327192619';
|
||||
@ -113,6 +113,21 @@ function renderApp() {
|
||||
}
|
||||
}
|
||||
|
||||
async function tryAutoLogin() {
|
||||
if (!state.session.login || !state.session.sessionId) return;
|
||||
try {
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
const resumed = await authService.resumeSession(state.session.login, state.session.sessionId);
|
||||
authorizeSession(resumed);
|
||||
await refreshSessions();
|
||||
} catch {
|
||||
// silent fallback to auth screens
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await tryAutoLogin();
|
||||
|
||||
if (!window.location.hash) {
|
||||
navigate(state.session.isAuthorized ? 'profile-view' : 'start-view');
|
||||
} else {
|
||||
@ -120,3 +135,6 @@ if (!window.location.hash) {
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', renderApp);
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@ -75,6 +75,7 @@ export function render({ navigate }) {
|
||||
try {
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
const result = await authService.createSessionForExistingUser(state.loginDraft.login, state.loginDraft.password);
|
||||
await authService.persistSessionMaterial(state.loginDraft.login, result.sessionMaterial);
|
||||
authorizeSession(result);
|
||||
await refreshSessions();
|
||||
setAuthInfo('Успешный вход выполнен.');
|
||||
|
||||
@ -4,10 +4,11 @@ import {
|
||||
exportEd25519PublicKeyB64,
|
||||
exportPkcs8B64,
|
||||
generateEd25519Pair,
|
||||
importPkcs8Ed25519,
|
||||
randomBase64,
|
||||
signBase64,
|
||||
} from './crypto-utils.js?v=20260327192619';
|
||||
import { saveEncryptedUserSecrets, saveSessionMaterial } from './key-vault.js?v=20260327192619';
|
||||
import { loadSessionMaterial, saveEncryptedUserSecrets, saveSessionMaterial } from './key-vault.js?v=20260327192619';
|
||||
|
||||
const BCH_SUFFIX = '001';
|
||||
|
||||
@ -158,6 +159,48 @@ export class AuthService {
|
||||
await saveSessionMaterial(login, sessionMaterial);
|
||||
}
|
||||
|
||||
|
||||
async resumeSession(login, preferredSessionId = '') {
|
||||
const cleanLogin = (login || '').trim();
|
||||
if (!cleanLogin) throw new Error('Нет login для авто-входа');
|
||||
|
||||
const sessionMaterial = await loadSessionMaterial(cleanLogin);
|
||||
if (!sessionMaterial?.sessionId || !sessionMaterial?.sessionKey || !sessionMaterial?.sessionPrivPkcs8) {
|
||||
throw new Error('На устройстве нет сохраненного ключа сессии');
|
||||
}
|
||||
|
||||
const targetSessionId = preferredSessionId || sessionMaterial.sessionId;
|
||||
const privateKey = await importPkcs8Ed25519(sessionMaterial.sessionPrivPkcs8);
|
||||
|
||||
const challengeResp = await this.ws.request('SessionChallenge', { sessionId: targetSessionId });
|
||||
if (challengeResp.status !== 200) throw opError('SessionChallenge', challengeResp);
|
||||
|
||||
const nonce = challengeResp?.payload?.nonce;
|
||||
if (!nonce) throw new Error('SessionChallenge: не вернулся nonce');
|
||||
|
||||
const timeMs = Date.now();
|
||||
const preimage = `SESSION_LOGIN:${targetSessionId}:${timeMs}:${nonce}`;
|
||||
const signatureB64 = await signBase64(privateKey, preimage);
|
||||
|
||||
const loginResp = await this.ws.request('SessionLogin', {
|
||||
sessionId: targetSessionId,
|
||||
sessionKey: sessionMaterial.sessionKey,
|
||||
timeMs,
|
||||
signatureB64,
|
||||
clientInfo: makeClientInfo(),
|
||||
});
|
||||
if (loginResp.status !== 200) throw opError('SessionLogin', loginResp);
|
||||
|
||||
const storagePwd = loginResp?.payload?.storagePwd;
|
||||
if (!storagePwd) throw new Error('SessionLogin: не вернулся storagePwd');
|
||||
|
||||
return {
|
||||
login: cleanLogin,
|
||||
sessionId: targetSessionId,
|
||||
storagePwd,
|
||||
};
|
||||
}
|
||||
|
||||
async listSessions() {
|
||||
const response = await this.ws.request('ListSessions', {});
|
||||
if (response.status !== 200) throw opError('ListSessions', response);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user