cd1d662572
Фаза 7, AGENT.md 7.1: регистрация ключа в настройках безопасности с обязательным step-up, вход по ключу без пароля (в т.ч. без ввода почты — обнаруживаемый ключ), несколько ключей на аккаунт, отзыв и переименование, события безопасности и аудит. Сервер: github.com/go-webauthn/webauthn (BSD-3-Clause), RP ID и Origin берутся из конфига домена; если домен — IP-адрес (стенд без домена), passkeys честно выключены (auth.passkey_unsupported). Церемонии живут в памяти процесса 5 минут и одноразовые: повторная отправка challenge отклоняется. Миграция 00018 пересобирает неиспользуемую таблицу webauthn_credentials под полную запись credential в JSON. Новые ручки входа ограничены по IP (10/мин). Клиент: тонкая обёртка над navigator.credentials без тяжёлых SDK, раздел «Ключи доступа» в настройках безопасности и кнопка «Войти по ключу» на экране входа; понятные сообщения для браузеров без поддержки WebAuthn и при отмене диалога. Тесты: Go — регистрация/вход с эмулятором аутентификатора (реальная проверка подписи P-256), отказ при чужом challenge, одноразовость церемонии, обязательный step-up при управлении ключами, запрет входа забаненному, ограничение allowCredentials при входе с почтой, лимит и валидация имени; web — 12 vitest с моком navigator.credentials; Playwright — живой сценарий с виртуальным аутентификатором Chromium.
37 lines
1.9 KiB
SQL
37 lines
1.9 KiB
SQL
-- +goose Up
|
|
-- Фаза 7: passkeys (WebAuthn), AGENT.md 7.1.
|
|
--
|
|
-- Таблица webauthn_credentials создана в 00002 «на вырост» и до Фазы 7 не
|
|
-- использовалась ни одной строкой кода, поэтому пересобираем её в том виде,
|
|
-- который нужен рабочей реализации: полная запись credential хранится в JSON
|
|
-- (credential_json) — так структура не разъезжается с версиями библиотеки
|
|
-- go-webauthn, а отдельные колонки нужны только для списка ключей.
|
|
-- PII здесь нет: имя ключа задаёт сам пользователь.
|
|
|
|
DROP TABLE webauthn_credentials;
|
|
CREATE TABLE webauthn_credentials (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
credential_id TEXT NOT NULL UNIQUE,
|
|
name TEXT NOT NULL DEFAULT '',
|
|
credential_json TEXT NOT NULL,
|
|
sign_count INTEGER NOT NULL DEFAULT 0,
|
|
transports TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
last_used_at TEXT
|
|
);
|
|
CREATE INDEX webauthn_credentials_user_idx ON webauthn_credentials (user_id);
|
|
|
|
-- +goose Down
|
|
DROP TABLE webauthn_credentials;
|
|
CREATE TABLE webauthn_credentials (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
credential_id TEXT NOT NULL UNIQUE,
|
|
public_key BLOB NOT NULL,
|
|
sign_count INTEGER NOT NULL DEFAULT 0,
|
|
transports TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|