TrustedDeviceLogin API и настройки входа через устройство

Что сделано:\n- публичный API сценария входа через доверенное устройство переведён на TrustedDeviceLogin\n- добавлен GetTrustedDeviceLoginSettings\n- отсутствие записи настроек на сервере теперь трактуется как enabled=true и hasPassword=false\n- ttlSeconds убран из клиентского API, TTL заявки фиксирован на сервере: 300 секунд\n- в shine-UI добавлен отдельный экран настроек входа через устройство и статус на основном экране\n- browser wallet переведён на новые TrustedDeviceLogin операции\n- в wallet добавлен выбор rootKey/deviceKey для будущего запроса подписи\n- документация API обновлена\n\nЧто ещё не проверено вручную end-to-end:\n- полный сценарий UI/plugin после этого деплоя не прогонялся руками до конца\n- сам signaling подписи в wallet всё ещё не реализован
This commit is contained in:
AidarKC
2026-06-18 14:19:31 +04:00
parent cf2152dcfc
commit 56db6d0add
29 changed files with 689 additions and 241 deletions
@@ -39,8 +39,8 @@ export class ShineApiClient {
return response.payload || {};
}
async startEspPairing({ login, passwordHash, requesterSessionKey, payloadType = 1 }) {
const response = await this.ws.request('StartEspPairing', {
async startTrustedDeviceLogin({ login, passwordHash, requesterSessionKey, payloadType = 1 }) {
const response = await this.ws.request('StartTrustedDeviceLogin', {
login: String(login || '').trim(),
passwordHash: String(passwordHash || '').trim(),
requesterSessionKey: String(requesterSessionKey || '').trim(),
@@ -48,24 +48,24 @@ export class ShineApiClient {
requesterClientPlatform: 'Chrome Extension Wallet',
payloadType: Number(payloadType) || 1,
});
if (response.status !== 200) throw opError('StartEspPairing', response);
if (response.status !== 200) throw opError('StartTrustedDeviceLogin', response);
return response.payload || {};
}
async getEspPairingStatus(pairingId) {
const response = await this.ws.request('GetEspPairingStatus', {
async getTrustedDeviceLoginStatus(pairingId) {
const response = await this.ws.request('GetTrustedDeviceLoginStatus', {
pairingId: String(pairingId || '').trim(),
});
if (response.status !== 200) throw opError('GetEspPairingStatus', response);
if (response.status !== 200) throw opError('GetTrustedDeviceLoginStatus', response);
return response.payload || {};
}
async cancelEspPairing(pairingId, requesterSessionKey) {
const response = await this.ws.request('CancelEspPairing', {
async cancelTrustedDeviceLogin(pairingId, requesterSessionKey) {
const response = await this.ws.request('CancelTrustedDeviceLogin', {
pairingId: String(pairingId || '').trim(),
requesterSessionKey: String(requesterSessionKey || '').trim(),
});
if (response.status !== 200) throw opError('CancelEspPairing', response);
if (response.status !== 200) throw opError('CancelTrustedDeviceLogin', response);
return response.payload || {};
}
@@ -204,6 +204,25 @@ export async function resolveShineServerByServerLogin(serverLogin, solanaEndpoin
};
}
export async function resolveShineServerByUserLogin(login, solanaEndpoint = SOLANA_ENDPOINT_DEFAULT) {
const cleanLogin = normalizeServerLogin(login);
if (!cleanLogin) throw new Error('Не указан логин пользователя.');
const parsed = await fetchUserPda(cleanLogin, solanaEndpoint);
const serverLogin = normalizeServerLogin(parsed.accessServers?.[0] || '');
if (!serverLogin) {
throw new Error(`У пользователя @${cleanLogin} в PDA не найден первый сервер доступа.`);
}
const resolved = await resolveShineServerByServerLogin(serverLogin, solanaEndpoint);
return {
login: cleanLogin,
accessServers: parsed.accessServers,
serverLogin: resolved.serverLogin,
serverAddress: resolved.serverAddress,
serverHttp: resolved.serverHttp,
serverUrl: resolved.serverUrl,
};
}
export async function readWalletProfileByLogin(login, solanaEndpoint = SOLANA_ENDPOINT_DEFAULT) {
const cleanLogin = normalizeServerLogin(login);
const parsed = await fetchUserPda(cleanLogin, solanaEndpoint);