Безопасность модуля passport/auth (PassportAuthMiddleware)

Документ описывает middleware аутентификации: как извлекается токен, как валидируется JWT, как работает refresh и как устанавливается YiiIdentity.

1. Место в пайплайне

config/web.php:

'on beforeRequest' => static function (): void {
    Yii::$container->get(PassportAuthMiddleware::class)->handle();  // сначала
    Yii::$container->get(RbacMiddleware::class)->handle();          // потом
},

PassportAuthMiddleware выполняется до RBAC.

2. Порядок работы handle()

flowchart TD
    A[Запрос] --> B{OPTIONS или public route?}
    B -- да --> Z[Пропуск]
    B -- нет --> C{Есть access token?}
    C -- нет --> D[tryRefresh]
    C -- да --> E{validateJwt}
    D --> E
    D -- нет токена --> F[401 Unauthorized]
    E -- ошибка --> G{expired?}
    G -- да --> D
    G -- нет --> F
    E -- ок --> H{claims корректны?}
    H -- нет --> F
    H -- да --> I[setIdentity YiiIdentity]
    I --> Z

3. Извлечение токена

extractAccessToken():

  1. Заголовок Authorization: Bearer <token> (регэксп).
  2. Иначе кука taskflow_access_token.

4. Валидация JWT (validateJwt)

ШагПроверка
Структураровно 3 части (header.payload.signature)
Headeralg === 'RS256'
Ключфайл OAUTH2_PUBLIC_KEY_PATH существует и читается
Подписьopenssl_verify с публичным ключом, OPENSSL_ALGO_SHA256
expчисловая и > now
nbfесли есть — ≤ now
client_idесли есть в payload — должен совпадать с нашим client_id
audдолжен содержать наш client_id

При любой ошибке — RuntimeException с текстом; middleware преобразует в 401 Unauthorized (кроме статуса «Access token expired» — пытается refresh).

5. Refresh (tryRefresh)

flowchart TD
    A[нет access / expired] --> B{есть refresh cookie?}
    B -- нет --> Z[null]
    B -- да --> C[PassportAuthPort::refresh]
    C -- успех --> D[setTokenCookies: access + refresh]
    D --> E[вернуть новый access]
    C -- ошибка --> F[deleteTokenCookies]
    F --> Z

6. Проверка claims и установка identity

Токен должен содержать:

Устанавливается:

Yii::$app->user->setIdentity(new YiiIdentity(
    (string)$sub,
    (string)($clientId ?? $audience),
    (string)$tokenId,
    array_values(array_map('strval', $scopes)),
    'user',
));

7. Public routes

Из modules/passport/auth/config/params.php:

'public_routes' => [
    'GET /',
    'GET /auth/*',
    'GET /gii',
    'GET /debug',
    'GET /debug/*',
    'GET /docs',
    'GET /docs/*',
    'OPTIONS *',
],

Сопоставление: 'МЕТОД шаблон', поддерживаются точное совпадение, *-суффикс, <param>-плейсхолдеры.

8. Куки (повтор)

ИмяНазначениеФлаги (жёстко в коде)
taskflow_access_tokenaccess JWThttpOnly, Secure, SameSite=LAX, path=/
taskflow_refresh_tokenrefresh JWThttpOnly, Secure, SameSite=LAX, path=/
taskflow_oauth_stateзащита SSOhttpOnly, Secure, SameSite=LAX, path=/auth

Имена кук — параметры модуля, но сами флаги httpOnly/Secure/SameSite=LAX зашиты в код middleware и контроллера.

9. Потенциальные нюансы (см. known-issues)