Реализация аутентификации через Passkeys (WebAuthn) на сайте
Passkeys — стандарт FIDO2/WebAuthn для беспарольной аутентификации. Пользователь входит через биометрию устройства (Touch ID, Face ID, Windows Hello) или аппаратный ключ (YubiKey). Никакого пароля, никакого SMS — криптографическая пара ключей хранится в безопасном хранилище устройства.
Passkeys поддерживаются в Chrome 108+, Safari 16+, Firefox 122+. iOS 16+ и Android 9+ синхронизируют ключи через iCloud Keychain и Google Password Manager.
Архитектура WebAuthn
Процесс регистрации: Сервер генерирует challenge, браузер передаёт его устройству. Устройство проверяет биометрию, создаёт ключевую пару, возвращает публичный ключ и attestation. Сервер сохраняет публичный ключ.
Процесс аутентификации: Сервер генерирует challenge, браузер передаёт его устройству. Устройство подписывает challenge приватным ключом. Сервер верифицирует подпись публичным ключом.
Приватный ключ никогда не покидает устройство. Сервер хранит только публичный ключ.
Серверная библиотека
[37;44m INFO [39;49m No publishable resources for tag [1m[webauthn-migrations][22m.
[41;1m Illuminate\Database\QueryException [49;22m
[39;1m SQLSTATE[08006] [7] could not connect to server: Connection refused (0x0000274D/10061) Is the server running on host "127.0.0.1" and accepting TCP/IP connections on port 5433? (SQL: select * from information_schema.tables where table_catalog = truetech and table_schema = public and table_name = migrations and table_type = 'BASE TABLE')[39;22m
at [32mD:\domains\atlas.loc\vendor\laravel\framework\src\Illuminate\Database\Connection.php[39m:[32m760[39m 756▕ // If an exception occurs when attempting to run a query, we'll format the error 757▕ // message to include the bindings with SQL, which will make this exception a 758▕ // lot more helpful to the developer instead of just the database's errors. 759▕ catch (Exception $e) { ➜ 760▕ throw new QueryException( 761▕ $query, $this->prepareBindings($bindings), $e 762▕ ); 763▕ } 764▕ }
[33m1 [39m[39;1mD:\domains\atlas.loc\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php[39;22m:[39;1m70[39;22m [90m PDOException::("SQLSTATE[08006] [7] could not connect to server: Connection refused (0x0000274D/10061) Is the server running on host "127.0.0.1" and accepting TCP/IP connections on port 5433?")[39m
[33m2 [39m[39;1mD:\domains\atlas.loc\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php[39;22m:[39;1m70[39;22m [90m PDO::__construct("pgsql:host=127.0.0.1;dbname='truetech';port=5433;sslmode=prefer", "truetech", Object(SensitiveParameterValue), [])[39m
Регистрация Passkey
Шаг 1: Challenge от сервера
Шаг 2: Браузер создаёт ключевую пару
Шаг 3: Сервер верифицирует и сохраняет публичный ключ
Аутентификация через Passkey
Получить challenge:
Браузер подписывает challenge:
Верификация на сервере:
Схема БД
Discoverable Credentials
Современные Passkeys поддерживают режим без ввода email — браузер предлагает все доступные ключи:
На сервере идентификатор пользователя извлекается из в ответе.
Управление устройствами
Показывать список зарегистрированных Passkeys с именем устройства, датой последнего использования и кнопкой удаления. После удаления записи пользователь не сможет войти через этот ключ.
Fallback
Всегда предоставлять альтернативный способ входа на случай потери всех устройств: email+пароль или Magic Link.
Сроки работ
| Этап | Время |
|---|---|
| Установка, конфигурация | 1 день |
| Регистрация: backend + frontend | 2 дня |
| Аутентификация: backend + frontend | 2 дня |
| Управление устройствами + fallback | 1 день |
| Тестирование на реальных устройствах | 1–2 дня |
Итого: 7–9 рабочих дней.







