# Ключи OpenIddict (подпись и шифрование токенов) Токены доступа/обновления подписываются (и в проде шифруются) ключами OpenIddict. Конфигурация ключей — в `OpenIddictKeyConfigurator` (`src/food-market.api/Infrastructure/Security/`), вызывается из `Program.cs` внутри `AddServer(...)`. ## Development - Persistent RSA-ключ в `src/food-market.api/App_Data/openiddict-dev-key.xml` (один и тот же для подписи и шифрования). - Переживает рестарты — выданные токены остаются валидными между перезапусками. - **Шифрование access-token выключено** (`DisableAccessTokenEncryption`) — токен это обычный 3-сегментный JWT, удобно дебажить (можно прочитать на jwt.io). - Файл `App_Data/` в `.gitignore` — ключ не коммитится. ## Production / Stage - Отдельные **X509-сертификаты** для подписи и шифрования. Access-token шифруется (5-сегментный JWE). - Путь к сертификатам — из конфигурации: | Ключ конфига | Env-переменная | Назначение | Дефолт | |---|---|---|---| | `OpenIddict:SigningCertPath` | `OpenIddict__SigningCertPath` | сертификат подписи | `App_Data/openiddict-signing.pfx` | | `OpenIddict:EncryptionCertPath` | `OpenIddict__EncryptionCertPath` | сертификат шифрования | `App_Data/openiddict-encryption.pfx` | | `OpenIddict:CertPassword` | `OpenIddict__CertPassword` | пароль PFX (опц.) | — | - **Если файла нет** — генерируется persistent self-signed сертификат (RSA 2048, срок 5 лет) и сохраняется по пути. При следующем старте берётся тот же файл, поэтому ранее выданные токены остаются валидными (нет dev-ephemeral поведения, при котором каждый рестарт инвалидировал бы все токены). - `App_Data` смонтирован как volume (`api-data:/app/App_Data` в `docker-compose.yml`), поэтому сертификаты переживают пересоздание контейнера. ### Принести собственные сертификаты Положить готовые `.pfx` (с приватным ключом) по путям из конфига и, при наличии пароля, задать `OpenIddict__CertPassword`. Приложение их подхватит вместо генерации self-signed. ```bash # пример: смонтировать каталог с сертификатами и указать пути OpenIddict__SigningCertPath=/run/secrets/oidc-signing.pfx OpenIddict__EncryptionCertPath=/run/secrets/oidc-encryption.pfx OpenIddict__CertPassword=<пароль или пусто> ``` ### Ротация 1. Заменить/удалить `.pfx` файлы (или указать новые пути). 2. Рестарт API: при отсутствии файла сгенерируется новый сертификат. 3. **Важно:** ротация ключа подписи/шифрования инвалидирует все ранее выданные токены — пользователям потребуется перелогиниться. Планировать на окно обслуживания. ### Проверка (smoke) ```bash # 5 сегментов = JWE (шифрование включено) — норма для прода curl -s -X POST $API/connect/token -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&username=...&password=...&client_id=food-market-web&scope=api" \ | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'].count('.')+1,'сегментов')" ``` Проверено локально (2026-05-27): prod-режим генерирует оба сертификата в `App_Data`, выдаёт 5-сегментный JWE, `/api/me` → 200; после рестарта сертификаты те же (fingerprint совпадает), токен, выданный до рестарта, остаётся валиден.