97 lines
4.5 KiB
HTML
97 lines
4.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Serverless FaceID Test</title>
|
|
<style>
|
|
body { font-family: -apple-system, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f5f5f7; }
|
|
.card { background: white; padding: 30px; border-radius: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); text-align: center; max-width: 320px; width: 100%; }
|
|
button { background: #0071e3; color: white; border: none; padding: 14px; font-size: 16px; border-radius: 12px; cursor: pointer; margin: 10px 0; width: 100%; font-weight: 500; }
|
|
button.secondary { background: #34c759; }
|
|
#status { margin-top: 20px; font-size: 14px; color: #86868b; word-break: break-word; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="card">
|
|
<h2>Безсерверный тест</h2>
|
|
<p>Проверка вызова Face ID на клиенте</p>
|
|
<button id="testFaceIdBtn">Вызвать Face ID</button>
|
|
<div id="status">Нажмите кнопку для теста</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Нам нужен фейковый challenge в виде ArrayBuffer (минимум 16 байт)
|
|
const fakeChallenge = new Uint8Array([
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
|
]).buffer;
|
|
|
|
// Фейковый ID пользователя (тоже ArrayBuffer)
|
|
const fakeUserId = new Uint8Array([
|
|
99, 98, 97, 96, 95, 94, 93, 92
|
|
]).buffer;
|
|
|
|
const log = (msg, isErr = false) => {
|
|
const el = document.getElementById('status');
|
|
el.innerText = msg;
|
|
el.style.color = isErr ? '#ff3b30' : '#1d1d1f';
|
|
};
|
|
|
|
document.getElementById('testFaceIdBtn').addEventListener('click', async () => {
|
|
log('Инициализация API...');
|
|
|
|
// 1. Проверяем, поддерживает ли вообще браузер/устройство биометрию
|
|
if (!window.PublicKeyCredential) {
|
|
log('WebAuthn не поддерживается в этом браузере.', true);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
log('Вызов сканера Face ID...');
|
|
|
|
// 2. Конфигурируем запрос к системе
|
|
const publicKeyCredentialCreationOptions = {
|
|
challenge: fakeChallenge,
|
|
rp: {
|
|
name: "Local Test PWA",
|
|
id: window.location.hostname // Автоматически подставит localhost или ваш домен
|
|
},
|
|
user: {
|
|
id: fakeUserId,
|
|
name: "[email protected]",
|
|
displayName: "Тестовый Пользователь"
|
|
},
|
|
pubKeyCredParams: [
|
|
{ type: "public-key", alg: -7 } // Алгоритм ES256, стандартный для Apple устройств
|
|
],
|
|
authenticatorSelection: {
|
|
authenticatorAttachment: "platform", // Заставляет iOS использовать именно встроенную биометрию
|
|
userVerification: "required" // Требует успешного сканирования (FaceID/TouchID/Пин-код)
|
|
},
|
|
timeout: 60000
|
|
};
|
|
|
|
// 3. Триггерим нативное окно iOS
|
|
const credential = await navigator.credentials.create({
|
|
publicKey: publicKeyCredentialCreationOptions
|
|
});
|
|
|
|
// Если промис успешно разрешился — Face ID отработал, данные получены
|
|
console.log('Успешный ответ от Face ID:', credential);
|
|
log('🎉 Успех! Нативное окно Face ID открылось и успешно подтвердило личность.');
|
|
|
|
} catch (err) {
|
|
console.error(err);
|
|
// Если пользователь нажал "Отмена" в шторке Face ID, сработает NotAllowedError
|
|
if (err.name === 'NotAllowedError') {
|
|
log('Действие отменено пользователем или таймаут.', true);
|
|
} else {
|
|
log('Ошибка: ' + err.message, true);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html> |