Files
admin/.wiki/concepts/verdaccio-token-lifecycle.md
vitya 1fb7fc2e90 wiki(link): verdaccio-token-lifecycle → shared runbook verdaccio-token-usage
Cross-ref the practical auth runbook (projects-wiki concepts/verdaccio-token-usage)
from the local postmortem. lifecycle = why, token-usage = how.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 08:37:26 +03:00

5.2 KiB
Raw Blame History

title, type, tags, sources, updated
title type tags sources updated
Verdaccio token lifecycle — restart trap + JWT fix concept
verdaccio
npm
pnpm
yarn
auth
jwt
gotcha
postmortem
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():

$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:

//verdaccio.kzntsv.site/:_authToken=eyJ...

Конфиг-фикс (2026-06-11)

Добавлено в /opt/stacks/verdaccio/config/config.yaml на vds-kzntsv:

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 в корне проекта:

//verdaccio.kzntsv.site/:_authToken=eyJ...
@snollajs:registry=https://verdaccio.kzntsv.site/

Yarn classic читает тот же ~/.npmrc — update токена в одном файле покрывает всех.

yarn berry (2.x+)

Не читает ~/.npmrc. Конфиг в .yarnrc.yml:

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).

Связанные страницы

  • Общая вики: concepts/verdaccio-token-usage (репо projects-wiki) — практический runbook аутентификации: два гейта, конфиг клиента, диагностика 401, stale-parent-process trap, чеклист. Эта страница — детальный postmortem про почему; runbook — про как пользоваться.
  • verdaccio-prune-semantics — storage layout и GC
  • yarn-npm-minimal-age-gate — гейт #2: YN0016 на свежих версиях (Yarn ≥4.16), независим от auth
  • vds-kzntsv — хост где крутится инстанс