wiki(ingest): concepts/verdaccio-token-lifecycle — restart trap + JWT fix
Постмортем утреннего фикса verdaccio: ephemeral secret (нет `secret:` в конфиге) → инвалидация всех токенов при каждом рестарте; max_users:-1 + pnpm login → 409; web-UI login как обход; JWT config fix (пинуем secret, api.jwt 900d). Recipe рефреша для yarn classic vs berry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
114
.wiki/concepts/verdaccio-token-lifecycle.md
Normal file
114
.wiki/concepts/verdaccio-token-lifecycle.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Verdaccio token lifecycle — restart trap + JWT fix
|
||||
type: concept
|
||||
tags: [verdaccio, npm, pnpm, yarn, auth, jwt, gotcha, postmortem]
|
||||
sources: []
|
||||
updated: 2026-06-11
|
||||
---
|
||||
|
||||
# Verdaccio token lifecycle — restart trap + JWT fix
|
||||
|
||||
## Root cause: auto-generated secret
|
||||
|
||||
Verdaccio без явного `secret:` в конфиге генерирует случайный секрет **при каждом запуске**. Все токены (legacy opaque и JWT) подписаны этим секретом — перезапуск контейнера = мгновенная инвалидация всех выданных токенов у всех клиентов.
|
||||
|
||||
**Симптом:** `401 Unauthorized` после рестарта VDS/контейнера, несмотря на то что токен в `.npmrc` визуально «есть».
|
||||
|
||||
**Фикс (применён 2026-06-11):** явный `secret:` в `config.yaml` → токены переживают рестарты.
|
||||
|
||||
## `max_users: -1` + pnpm login → 409
|
||||
|
||||
Стандартный workaround «перелогинься» (`pnpm login --registry ...`) не работает при `max_users: -1`.
|
||||
|
||||
Verdaccio htpasswd plugin v6 проверяет `max_users` **первым** в `adduser()`, до проверки существования пользователя:
|
||||
|
||||
```
|
||||
if (max_users === -1) → return 409 "user registration disabled"
|
||||
```
|
||||
|
||||
`pnpm login` отправляет `PUT /-/user/org.couchdb.user:<name>` — это registration-path. Verdaccio блокирует даже для существующих юзеров, даже если пароль верный.
|
||||
|
||||
Изменение `max_users` на положительное число решает проблему, но открывает регистрацию всем — нежелательно.
|
||||
|
||||
## Правильный способ получить токен при `max_users: -1`
|
||||
|
||||
Использовать **web-UI login endpoint**, который не проходит через `adduser()`:
|
||||
|
||||
```powershell
|
||||
$body = '{"username":"vitya","password":"..."}'
|
||||
$r = Invoke-RestMethod -Uri "https://verdaccio.kzntsv.site/-/verdaccio/sec/login" `
|
||||
-Method POST -Body $body -ContentType "application/json"
|
||||
$r.token # JWT, 7d expiry
|
||||
```
|
||||
|
||||
Полученный токен — в `~/.npmrc`:
|
||||
|
||||
```ini
|
||||
//verdaccio.kzntsv.site/:_authToken=eyJ...
|
||||
```
|
||||
|
||||
## Конфиг-фикс (2026-06-11)
|
||||
|
||||
Добавлено в `/opt/stacks/verdaccio/config/config.yaml` на [vds-kzntsv](../entities/vds-kzntsv.md):
|
||||
|
||||
```yaml
|
||||
secret: <32-byte-hex-permanent>
|
||||
|
||||
security:
|
||||
api:
|
||||
jwt:
|
||||
sign:
|
||||
expiresIn: 30d
|
||||
notBefore: 0
|
||||
web:
|
||||
sign:
|
||||
expiresIn: 7d
|
||||
verify: {}
|
||||
```
|
||||
|
||||
До фикса: нет `secret` и нет `security` → legacy tokens, привязанные к ephemeral secret.
|
||||
После: web-UI и npm-API используют один JWT формат, подписанный постоянным секретом.
|
||||
|
||||
> **Примечание:** `security.api.jwt` меняет формат API-токенов с legacy opaque (`5a3X...==`) на JWT (`eyJ...`). Старые legacy-токены в `.npmrc` у всех клиентов перестают работать после смены конфига — нужен refresh.
|
||||
|
||||
## Срок жизни токенов
|
||||
|
||||
| Endpoint | Expiry | Используется для |
|
||||
|---|---|---|
|
||||
| `/-/verdaccio/sec/login` | 7d (web.sign) | Ручной refresh, агенты |
|
||||
| npm API (pnpm/yarn/npm login) | 30d (api.jwt.sign) | После решения `max_users` проблемы |
|
||||
|
||||
## Клиенты: где хранится токен
|
||||
|
||||
### npm / pnpm / yarn classic (1.x)
|
||||
|
||||
Читают `~/.npmrc` (user-level) или `.npmrc` в корне проекта:
|
||||
|
||||
```ini
|
||||
//verdaccio.kzntsv.site/:_authToken=eyJ...
|
||||
@snollajs:registry=https://verdaccio.kzntsv.site/
|
||||
```
|
||||
|
||||
Yarn classic читает тот же `~/.npmrc` — update токена в одном файле покрывает всех.
|
||||
|
||||
### yarn berry (2.x+)
|
||||
|
||||
Не читает `~/.npmrc`. Конфиг в `.yarnrc.yml`:
|
||||
|
||||
```yaml
|
||||
npmRegistries:
|
||||
"https://verdaccio.kzntsv.site":
|
||||
npmAuthToken: "eyJ..."
|
||||
npmAlwaysAuth: true
|
||||
|
||||
npmScopes:
|
||||
snollajs:
|
||||
npmRegistryServer: "https://verdaccio.kzntsv.site"
|
||||
```
|
||||
|
||||
Refresh токена = обновить `npmAuthToken` в `.yarnrc.yml` каждого проекта, либо использовать `yarn npm login --scope snollajs` (тоже обходит `max_users: -1` через web UI path).
|
||||
|
||||
## Связанные страницы
|
||||
|
||||
- [verdaccio-prune-semantics](verdaccio-prune-semantics.md) — storage layout и GC
|
||||
- [vds-kzntsv](../entities/vds-kzntsv.md) — хост где крутится инстанс
|
||||
@@ -50,6 +50,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
||||
- [vbox-windows-stability-tuning](concepts/vbox-windows-stability-tuning.md) — VirtualBox + Windows-гость — нюансы стабильности cross-hypervisor миграции
|
||||
- [vds-kzntsv-dhcp-outage-2026-05-28](concepts/vds-kzntsv-dhcp-outage-2026-05-28.md) — DHCP outage 2026-05-28 на vds-kzntsv — диагностика + recovery runbook (link UP но без IPv4 → статика + тикет хостеру)
|
||||
- [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published
|
||||
- [verdaccio-token-lifecycle](concepts/verdaccio-token-lifecycle.md) — restart trap (ephemeral secret) + max_users:-1 pnpm 409 + JWT fix + yarn clients
|
||||
- [winacme-iis-owin-catchall-http01](concepts/winacme-iis-owin-catchall-http01.md) — win-acme HTTP-01 авто-renewal LE на IIS под OWIN-catch-all CMS — challenge в отдельном No-Managed-Code приложении + патч шаблона; заменяет ручной PFX-импорт
|
||||
- [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18
|
||||
- [windows-server-2025-core-bootstrap](concepts/windows-server-2025-core-bootstrap.md) — Win Server 2025 Core (RUVDS) — bootstrap для IIS-хоста: default-blockers + transfer-методов матрица
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
Append-only log of wiki operations (ingests, promotions, lints, migrations).
|
||||
|
||||
## [2026-06-11] ingest | verdaccio-token-lifecycle — restart trap (ephemeral secret → 401), max_users:-1 + pnpm 409 постмортем, JWT config fix, yarn classic vs berry token refresh recipe.
|
||||
## [2026-06-11] update | MSSQL backup gap closed — `/opt/stacks/backup/scripts/run.sh` расширен: 5 prod DBs через `docker exec mssql sqlcmd BACKUP DATABASE ... WITH COPY_ONLY, INIT` (Express, без COMPRESSION); .bak в bind-mount → mv в DUMP_DIR → rsync kreknin. Обновлён [[concepts/mssql-on-vds]] (TODO→реализовано).
|
||||
|
||||
## [2026-06-11] lint | 10 issues found, 8 fixed inline
|
||||
|
||||
Reference in New Issue
Block a user