Files
glchat/internal/meta/meta.go
T
grendervill cd1d662572 feat(auth): вход по ключу доступа (passkeys, WebAuthn)
Фаза 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.
2026-09-26 15:12:43 +03:00

55 lines
1.7 KiB
Go

package meta
import "glchat/internal/config"
const APIVersion = "v1"
type Features struct {
RegistrationEnabled bool `json:"registration_enabled"`
AntiBotEnabled bool `json:"anti_bot_enabled"`
VoiceEnabled bool `json:"voice_enabled"`
// VoiceURL — адрес LiveKit для клиента (ws/wss), пусто при выключенном голосе.
VoiceURL string `json:"voice_url,omitempty"`
WebPushEnabled bool `json:"web_push_enabled"`
OAuthEnabled bool `json:"oauth_enabled"`
PasskeysEnabled bool `json:"passkeys_enabled"`
}
type Response struct {
Name string `json:"name"`
Version string `json:"version"`
Commit string `json:"commit"`
BuildDate string `json:"build_date"`
APIVersion string `json:"api_version"`
BaseURL string `json:"base_url"`
FilesURL string `json:"files_url"`
GatewayURL string `json:"gateway_url"`
RTCPath string `json:"rtc_path"`
MaxUploadSize int64 `json:"max_upload_size"`
Features Features `json:"features"`
}
func New(cfg config.Config) Response {
return Response{
Name: cfg.InstanceName,
Version: cfg.Version,
Commit: cfg.Commit,
BuildDate: cfg.BuildDate,
APIVersion: APIVersion,
BaseURL: cfg.BaseURL(),
FilesURL: cfg.FilesURL(),
GatewayURL: cfg.GatewayURL(),
RTCPath: "/rtc",
MaxUploadSize: cfg.MaxUploadSize,
Features: Features{
RegistrationEnabled: true,
AntiBotEnabled: false,
VoiceEnabled: cfg.VoiceEnabled(),
VoiceURL: cfg.LiveKitURL,
WebPushEnabled: false,
OAuthEnabled: false,
PasskeysEnabled: cfg.WebAuthnEnabled(),
},
}
}