# Анатомия безопасности: как защитить веб-приложение с AWS Cognito

Источник: https://www.youtube.com/watch?v=ajExOgOCJXY
Канал: freeCodeCamp.org
Опубликовано: 13.03.2025

---

Популярные JWT-токены, защищающие миллионы современных веб-приложений, на самом деле вообще не скрывают ваши данные, а лишь кодируют их в Base64. Полагаться на классические серверные сессии в эпоху облачной инфраструктуры становится опасным анахронизмом, требующим перехода на более гибкие решения. Разбираемся, как развернуть надежную экосистему аутентификации с помощью AWS Cognito, защитить SPA от перехвата трафика через PKCE и организовать безопасное хранение токенов в HTTP-only куках.

## 🔐 Погружение в AWS Cognito: Архитектура идентификации и управление пользователями
[[JUMP:00:36]]

### Различие философий: AWS IAM против AWS Cognito
[[JUMP:03:20]]
При проектировании облачной архитектуры на платформе AWS разработчики часто сталкиваются с дилеммой выбора правильного инструмента управления доступом. Автор курса Риши Кумар Тари подчеркивает, что путаница обычно возникает из-за пересекающегося функционала AWS IAM (Identity and Access Management) и AWS Cognito. Однако их ключевое различие кроется в целевой аудитории и фокусе применения.

Сервис AWS IAM разработан строго для контроля доступа внутри самого облака AWS. Его основными сущностями выступают IAM-пользователи (IAM users) и IAM-роли (IAM roles), которые определяют, какой именно разработчик или внутренний сервис имеет право совершать действия с облачными ресурсами — например, записывать данные в базу DynamoDB или удалять объекты из хранилища S3. В качестве примера приводится сценарий, где одной службе Lambda нужно обратиться к DynamoDB: для этого ей временно назначается IAM-роль со строго прописанной политикой разрешений.

Напротив, AWS Cognito — это специализированный сервис класса CIAM (Customer Identity and Access Management), полностью ориентированный на конечных пользователей мобильных или веб-приложений. Cognito берет на себя всю рутину по регистрации, аутентификации и валидации учетных записей. Если ваше приложение развернуто на сервере EC2 и случайный пользователь запрашивает список задач, именно Cognito проверяет валидность его сессии и, при необходимости, перенаправляет на страницу авторизации.

### Анатомия Cognito: User Pools для пользователей и Identity Pools для ресурсов
[[JUMP:02:47]]
Платформа AWS Cognito предлагает две фундаментально разные сущности, совместное использование которых позволяет выстроить безопасную сквозную архитектуру: User Pools (пулы пользователей) и Identity Pools (пулы идентификации). 

User Pool представляет собой полноценную внутреннюю директорию или базу данных, где хранятся профили всех зарегистрированных пользователей вашего приложения. Главная задача пула пользователей — обеспечить надежные механизмы аутентификации и авторизации. В самом начале разговора автор курса упоминает архитектуру учебного приложения Share My File, демонстрируя возможности авторизации, однако детальная интеграция пользовательского интерфейса, настройка классического бэкенда и логика JWT-токенов будут подробно рассмотрены в последующих главах.

Identity Pool решает принципиально иную задачу — он предоставляет конечным пользователям временный и безопасный доступ непосредственно к ресурсам AWS в обход традиционного бэкенда. Это реализуется через создание своеобразного «моста» между пользователем и облачной инфраструктурой с помощью IAM-ролей. 

Механизм работы Identity Pool выглядит следующим образом:

* Пользователь обращается к провайдеру идентификации (это может быть Facebook, Google или собственный Cognito User Pool) и получает соответствующий токен.

* Полученный токен передается в Identity Pool, который проверяет его валидность на основе предварительно заданных настроек.

* Identity Pool обращается к службе временных токенов AWS STS (Security Token Service), генерирует краткосрочные учетные данные и возвращает их клиенту.

Полученный клиентом набор данных включает в себя Access ID, Secret Key, Session Token и точное время истечения действия прав (Expiration time). С этими временными ключами приложение пользователя может напрямую отправлять API-запросы, например, в корзину S3 для безопасного скачивания или загрузки файлов. При этом Identity Pools позволяют гибко разграничивать права: неавторизованные (guest) пользователи получают минимальный набор разрешений, тогда как вошедшие в систему клиенты — расширенный доступ.

### Практика в консоли: Настройка и возможности User Pools
[[JUMP:10:36]]
Практическое знакомство с возможностями User Pools начинается непосредственно в панели управления AWS Cognito, где пулы описываются как директории локальных и федеративных (зашедших через сторонние службы) профилей. Процесс создания пула жестко завязан на тип клиентского приложения. Например, выбор Single Page Application (SPA) автоматически генерирует так называемый публичный клиент (Public client). Если бы создавалось традиционное бэкенд-приложение или межсерверная интеграция (M2M), Cognito создал бы приватный клиент (Private client). Каждый пул может содержать несколько таких клиентов, а генерируемый Client ID становится главным идентификатором для взаимодействия приложения с базой Cognito. Ранее в разговоре они также вскользь касались связи параметров перенаправления с протоколом OAuth 2.0, который станет темой следующих глав.

Интерфейс Cognito предоставляет готовую страницу авторизации, которая в современных версиях называется Manage Login UI (ранее известная как Hosted UI). При создании тестового пользователя система автоматически высылает одноразовый пароль (OTP) на указанный email для верификации. Формат этих уведомлений полностью настраивается в разделе шаблонов сообщений, где вместо OTP можно выбрать подтверждение аккаунта по прямой ссылке. Каждому подтвержденному пользователю присваивается уникальный идентификатор — `sub` (User ID), который впоследствии передается внутри токенов доступа.

Помимо базовой регистрации, Cognito User Pools из коробки поддерживают обширный набор enterprise-функций:

* Группы пользователей: Возможность объединять профили в группы (например, admin или moderator) для последующего управления привилегиями на основе токенов.

* Квоты на отправку уведомлений: По умолчанию встроенный механизм Cognito позволяет отправлять не более 50 верификационных писем в день, а для масштабных приложений требуется обязательное подключение службы AWS SES (Simple Email Service).

* Расширенная безопасность и беспарольный вход: Администратор может гибко настроить политики сложности паролей, активировать беспарольный вход через волшебные ссылки (magic links) или одноразовые коды, а также включить поддержку современных ключей доступа Passkeys.

* Многофакторная аутентификация (MFA): Поддерживается обязательная или опциональная проверка через приложения-аутентификаторы (Google или Microsoft Authenticator). Стоит отметить технический нюанс настройки в консоли: для включения MFA через приложения необходимо предварительно отключить авторизацию по email OTP.

* Лямбда-триггеры (Extensions): Уникальная возможность внедрять кастомный код непосредственно в жизненный цикл аутентификации Cognito. С помощью функций AWS Lambda, выполняемых перед регистрацией (pre-sign-up trigger), разработчики могут программно фильтровать пользователей — например, блокировать нежелательные домены или разрешать регистрацию только определенным адресам.

## 🔐 Интеграция User Pool: управление доступом и настройка UI

[[JUMP:25:22]]

Управление пользователями в AWS Cognito начинается с глубокой настройки User Pool. Платформа позволяет гибко интегрировать бизнес-логику на этапе регистрации через Lambda-функции. Например, вы можете настроить проверку формата email-адреса и при необходимости выдавать ошибку, если пользователь ввел неверные данные, а также автоматически отправлять приветственные сообщения после успешной регистрации.

Кроме того, Cognito предоставляет широкие возможности кастомизации интерфейса Managed Login UI. Вы можете не только использовать домен AWS по умолчанию, но и привязать собственное доменное имя для страницы входа, что повышает доверие пользователей к вашему приложению. Настройки стиля позволяют полностью адаптировать внешний вид страницы: изменять цвета, шрифты, фоновые изображения и текстовые блоки, чтобы аутентификация органично вписывалась в дизайн вашего продукта.

### 🛠 Способы интеграции: OAuth 2.0 против SDK

[[JUMP:26:57]]

Существует два основных подхода к интеграции Cognito в приложение: использование протокола OAuth 2.0 или обращение напрямую к AWS SDK. Ранее в разговоре мы касались основ работы JWT-токенов, которые играют ключевую роль в обоих методах.

Выбор между ними часто зависит от архитектуры. Если мы работаем с фронтендом (например, React, Angular или чистый JavaScript), мы имеем дело с «публичным клиентом» (Public Client), так как у него отсутствует секретный ключ. Если же аутентификация происходит на бэкенде (Go, Node.js), мы имеем дело с «приватным клиентом» (Private Client), что позволяет безопасно хранить `client secret`.

### 🔑 Потоки авторизации (Grant Types)

[[JUMP:33:20]]

Cognito поддерживает несколько потоков OAuth 2.0 (Grant Types) для различных сценариев аутентификации:

*   **Authorization Code Grant:** Рекомендуемый и наиболее безопасный способ. Клиент перенаправляет пользователя на UI Cognito, после ввода данных получает `authorization code` в URL, а затем обменивает его на токены через защищенный запрос к серверу.
*   **Implicit Flow:** Ранее использовался во фронтенд-приложениях, но на текущий момент **не рекомендуется к использованию**. В этом потоке токены возвращаются напрямую в URL, что менее безопасно.
*   **Refresh Token Grant:** Позволяет реализовать «бесшовную» аутентификацию. Когда `access` и `id` токены истекают, клиент использует `refresh token` для получения новой пары токенов без необходимости перенаправлять пользователя на страницу логина.
*   **Client Credentials:** Используется для межсерверного взаимодействия (machine-to-machine), где нет участия пользователя, например, при выполнении фоновых задач или автоматизированных тестов.

Стоит помнить: если вы меняете настройки типа потока или `callback URL` в консоли Cognito, иногда требуется подождать около 5 минут до применения изменений, иначе вы можете столкнуться с ошибкой 400.

### 🛡 Безопасность: State и PKCE

[[JUMP:46:20]]

Для повышения безопасности Authorization Code Grant, AWS настоятельно рекомендует внедрять дополнительные механизмы, такие как `State` и `PKCE`.

*   **State-параметр:** Используется для предотвращения атак типа CSRF (Cross-Site Request Forgery). Клиент генерирует случайную строку, передает её в запросе и проверяет в `callback URL` — это подтверждает, что запрос был инициирован именно вашим приложением.
*   **PKCE (Proof Key for Code Exchange):** Это стандарт защиты, который гарантирует, что сущность, инициирующая запрос, и сущность, получающая токены, — одно и то же лицо. Механизм включает создание `code verifier` (случайное значение) и его хешированной версии — `code challenge`. Даже если кто-то перехватит ваш `authorization code`, он не сможет получить токены без валидного `code verifier`, известного только вашему приложению.

## 🔐 Протокол OAuth 2.0 и анатомия JWT-токенов в AWS Cognito
[[JUMP:50:58]]

### Анатомия JWT-токена: прозрачность данных и защита от подделки
[[JUMP:50:58]]
Глубокое понимание механизмов современной аутентификации невозможно без детального разбора структуры используемых токенов. Спикер подчеркивает важный нюанс: в экосистеме AWS Cognito как идентификационный токен (ID-токен), так и токен доступа (Access-токен) представляют собой JSON Web Tokens (JWT), в то время как рефреш-токен (Refresh-токен) структуру JWT не использует.

Визуально JWT выглядит как монолитная и запутанная строка из случайных символов, однако она строго разделена точками на три независимые части:

*   **Header (Заголовок)** — содержит техническую информацию о типе токена и алгоритме шифрования, например, HS256 или RSA256.

*   **Payload (Полезная нагрузка)** — само тело с данными, включающее дату создания токена `iat`, имя пользователя, права доступа и другие кастомные атрибуты.

*   **Signature (Цифровая подпись)** — математический блок, гарантирующий валидность первых двух частей и защищающий их от подделки.

Главное заблуждение начинающих разработчиков заключается в том, что JWT якобы скрывает или шифрует информацию от посторонних глаз. На самом деле данные находятся в абсолютно открытом виде. Первые две части токена — это обычный структурированный JSON, просто закодированный в формат Base64. Спикер наглядно демонстрирует это прямо в браузере, копируя полезную нагрузку токена и мгновенно декодируя её в консоли JavaScript с помощью стандартной функции `atob()`. Истинная ценность JWT заключается не в конфиденциальности, а в его абсолютной прозрачности и неизменяемости: цифровая подпись создается на основе секретного ключа, поэтому любое изменение payload на стороне клиента мгновенно разрушит валидность токена при проверке.

### Сессионная модель против JWT: переход к Stateless-архитектуре
[[JUMP:55:32]]
Чтобы в полной мере оценить необходимость внедрения протокола OAuth 2.0 и JWT, лектор предлагает вспомнить классическую сессионную авторизацию (Session-based login). В рамках традиционного подхода сервер после проверки пароля генерирует уникальный случайный идентификатор сессии, сохраняет его в базу данных и отправляет клиенту в куки. При каждом последующем запросе веб-сервер обязан обращаться к базе данных, чтобы проверить, существует ли такая сессия и активна ли она на текущий момент.

JWT-аутентификация полностью меняет этот паттерн, делая процесс авторизации stateless (без сохранения состояния на сервере):

*   Пользователь отправляет учетные данные на сервер аутентификации.

*   Сервер валидирует пароль и генерирует JWT, подписывая его своим ключом (симметричным или асимметричным).

*   Токен возвращается в браузер и надежно сохраняется на стороне клиента.

*   При каждом новом запросе приложение передает этот токен бэкенду, который просто верифицирует подпись с помощью ключа.

Ключевое архитектурное отличие заключается в полном отсутствии необходимости держать централизованную базу данных для хранения активных сессий. В современных распределенных системах и микросервисной архитектуре классические сессии превращаются в узкое горлышко: каждый микросервис вынужден постоянно запрашивать общую базу данных для проверки валидности сессии. С JWT авторизация становится децентрализованной — любому изолированному микросервису достаточно иметь ключ верификации, чтобы мгновенно подтвердить подлинность пользователя без лишних сетевых запросов и накладных расходов.

### Разделение обязанностей в OAuth 2.0: Access-токены против ID-токенов
[[JUMP:58:15]]
При интеграции OAuth 2.0 с AWS Cognito взаимодействие с бэкендом строится на базе асимметричного шифрования. Cognito выступает в роли выделенного сервера авторизации и использует свой приватный ключ для подписи выпускаемых JWT. Когда пользователь отправляет токен на бэкенд вашего приложения, у вашего сервера изначально нет встроенных механизмов для его проверки. По спецификации протокола OAuth 2.0, сервер авторизации всегда предоставляет публичный эндпоинт, где публикуются открытые ключи (public keys) для верификации подписей. Бэкенд-сервер заранее скачивает эти ключи из Cognito (их можно найти в панели User Pool под именем Token signing URL) и локально проверяет каждый входящий токен по алгоритму RSA 256.

Важной теоретической частью является разграничение обязанностей между ID-токеном и Access-токеном:

*   **Access Token (Токен доступа)** — стандартный элемент протокола OAuth 2.0. Он предназначен непосредственно для защиты API и содержит техническую информацию о правах доступа (Scopes) и группах пользователя.

*   **ID Token (Идентификационный токен)** — появился как часть надстройки OpenID Connect (OIDC), работающей поверх OAuth 2.0. Он предназначен для самого клиентского интерфейса (например, SPA-приложения на React) и несет информацию о профиле пользователя: email, имя, аватар.

Спикер проводит эксперимент, переключая тип ответа Cognito на прямую выдачу токенов (Implicit flow), чтобы наглядно разобрать их внутренности. Выясняется, что AWS Cognito частично размывает классические архитектурные границы. Согласно общепринятой конвенции бэкенд-сервисы и API-шлюзы должны принимать строго Access-токены, однако в инфраструктуре AWS (например, при интеграции с Amazon API Gateway) повсеместно требуется передача именно ID-токена для авторизации запросов. При этом информация о группах пользователей Cognito для удобства дублируется в обоих типах токенов.

Ранее в разговоре они касались основ работы с User Pools, а завершив теоретический разбор токенов, лектор переходит к практическому развертыванию React-приложения с использованием клиентских библиотек `oidc-client-ts` и `react-oidc-context`, наглядно демонстрируя генерацию параметров State и PKCE, детали реализации которых будут подробно рассмотрены в следующих главах.

## 🛡️ Безопасность аутентификации: PKCE и State-параметр

[[JUMP:1:15:28]]

В современных архитектурах аутентификации, таких как интеграция AWS Cognito с одностраничными приложениями (SPA), обеспечение безопасности процесса обмена кодов авторизации на токены является критически важной задачей. Основными инструментами для защиты от перехвата кодов и CSRF-атак (Cross-Site Request Forgery) выступают протокол PKCE (Proof Key for Code Exchange) и использование параметра `state`.

### Использование PKCE и State-параметра
[[JUMP:1:15:28]]

Процесс начинается с формирования запроса к эндпоинту авторизации. Клиентское приложение генерирует случайный параметр `state`, который помогает предотвратить CSRF-атаки, гарантируя, что ответ от сервера авторизации соответствует именно тому запросу, который был инициирован текущим пользователем.

Параллельно используется PKCE, который заменяет статический `client_secret` в публичных клиентах (как SPA) на динамически генерируемый `code_verifier`. 

*   **Code Verifier:** Случайная строка, которая создается приложением перед перенаправлением на Cognito.
*   **Защита:** Эта строка временно сохраняется в `sessionStorage` браузера. Когда Cognito возвращает код авторизации, приложение отправляет его вместе с `code_verifier` на эндпоинт получения токенов. Cognito сверяет эти значения, подтверждая, что запрос исходит от того же клиента, который инициировал авторизацию.

После завершения обмена кодов на токены (ID, access и refresh токенов), `code_verifier` удаляется из локального хранилища, а полученные токены сохраняются для последующего использования.

### Управление сессией и жизненный цикл токенов
[[JUMP:1:16:21]]

Важной частью реализации безопасности является автоматизация обновления сессии. Поскольку `access` и `ID` токены обладают ограниченным сроком действия, клиентская библиотека отслеживает их истечение. 

Как только время жизни `access` токена подходит к концу, библиотека автоматически использует `refresh` токен для получения новой пары токенов. Этот процесс позволяет пользователю оставаться в системе, не требуя повторного ввода учетных данных до момента истечения самого `refresh` токена. Хотя в примере использовались специфические библиотеки для работы с Cognito, аналогичная логика обмена и обновления токенов реализуема с помощью любого стандартного инструментария, поддерживающего OAuth 2.0 и OpenID Connect.

---
### Защита сессий в классических бэкенд-приложениях
[[JUMP:1:17:32]]

При переходе к традиционным серверным приложениям (например, на Node.js с использованием EJS), где сервер самостоятельно рендерит HTML, модель безопасности меняется. Ранее в разговоре они касались основ работы протокола OAuth 2.0 и JWT токенов, здесь же акцент смещается на сессионную модель.

В таком сценарии библиотека `openid-client` координирует рабочий процесс, используя параметры `nonce` и `state` для каждой попытки входа. После успешной аутентификации и получения токенов от Cognito, сервер создает сессию, используя Express-сессии. 

Важное предостережение: использование стандартного сессионного middleware в Node.js, который хранит данные в оперативной памяти, не рекомендуется для высоконагруженных систем. Для производственной среды необходимо использовать внешние хранилища сессий (например, DynamoDB или Redis), чтобы обеспечить масштабируемость и устойчивость.

## 🛠️ Интеграция React SPA с AWS Cognito: практическая реализация

[[JUMP:1:40:33]]

Настройка взаимодействия между React-приложением и AWS Cognito без использования классического бэкенда требует особого подхода к управлению токенами и состоянием. Поскольку HTTP-протокол stateless, разработчикам необходимо реализовать механизмы передачи и хранения данных аутентификации, которые обеспечат безопасность и корректный поток OAuth 2.0.

### Настройка React SPA и API-взаимодействия

[[JUMP:1:40:47]]

В контексте работы с React SPA, приложение взаимодействует с Node.js сервером, выступающим посредником в OAuth-потоке. Важным аспектом является настройка `fetch`-запросов: при выполнении cross-origin вызовов необходимо устанавливать конфигурационный параметр `credentials: true`. Это гарантирует, что cookies (включая ID и access-токены) будут автоматически включаться в заголовки запросов, обеспечивая сохранение сессии.

На стороне фронтенда логика аутентификации включает в себя:

*   **Инициализацию состояния:** Хранение данных пользователя и обработка состояний (например, отображение ошибки 401, если пользователь не авторизован).
*   **Функцию логина:** При клике на кнопку «Login» приложение запрашивает URL аутентификации у сервера, после чего инициирует редирект на страницу Cognito.
*   **Обработку callback:** Использование `useEffect` для перехвата параметров `code` и `state` из строки запроса, которые затем передаются на бэкенд для обмена на токены.

### Архитектура бэкенда и безопасность cookies

[[JUMP:1:45:01]]

Для обеспечения безопасности в связке с SPA, бэкенд использует middleware для работы с cookies, такие как `cookie-parser` и `cors` (с разрешением `credentials: true`). Важным решением является использование подписанных (signed) и `httpOnly` cookies. 

*   **Signed cookies:** Делают данные (например, `code verifier` и `state`) нечитаемыми и защищенными от модификации на стороне клиента.
*   **Stateless подход:** Поскольку полноценная сессия на сервере отсутствует, cookies становятся единственным инструментом хранения временных данных, необходимых для завершения OAuth-потока.
*   **Разделение токенов:** Access и refresh-токены хранятся в `httpOnly` cookies для предотвращения доступа через JavaScript, тогда как ID-токен может храниться в обычном cookie для удобства чтения информации о пользователе на фронтенде.

### Валидация JWT и работа с JWKS

[[JUMP:1:57:11]]

Аутентификация API-маршрутов на бэкенде опирается на проверку JWT-токенов. Процесс верификации включает в себя:

1.  **Получение ключей (JWKS):** Сервер при запуске обращается к эндпоинту Cognito, предоставляющему открытые ключи для проверки подписи токенов.
2.  **Конвертация в PEM:** Используя криптографическую библиотеку Node.js, полученный JSON Web Key преобразуется в формат PEM, необходимый для работы библиотек верификации.
3.  **Проверка подписи и срока действия:** Middleware проверяет наличие access-токена в cookies, после чего декодирует его, сверяя подпись с публичным ключом и проверяя время жизни токена.

Данная реализация позволяет серверу динамически верифицировать токены, при этом ключи могут кэшироваться глобально при старте приложения для оптимизации производительности. Ранее в разговоре они касались PKCE и state-параметра, которые являются критически важными компонентами этого процесса, обеспечивающими защиту от подмены запросов в потоке OAuth.

## 🔐 Серверная аутентификация: работа с сессиями и группами в Node.js

[[JUMP:02:05:38]]

Когда мы переходим от простых клиентских приложений к классической бэкенд-архитектуре, процесс аутентификации становится более строгим и защищенным. В этой модели бэкенд на Node.js берет на себя роль посредника между AWS Cognito и фронтендом, управляя жизненным циклом сессии через защищенные куки. Процесс начинается в тот момент, когда Cognito перенаправляет пользователя обратно на специальный маршрут (route) приложения после успешного входа [02:05:38].

### Обработка Callback и управление токенами в куках
[[JUMP:02:05:51]]

На стороне бэкенда основной задачей является обработка параметров `code` (код авторизации) и `state` (параметр состояния для защиты от CSRF), которые приходят в URL. Бэкенд логирует значение `state` и сравнивает его с тем, что было сохранено в сессии (ранее в курсе обсуждалась важность этого механизма для безопасности) [02:06:03]. После этого сервер выполняет обмен кода на токены.

Ключевым моментом здесь является стратегия хранения полученных токенов:

1.  **Access Token и Refresh Token**: Они устанавливаются как `HTTP-only` куки [02:06:28]. Это означает, что клиентский JavaScript не имеет к ним доступа, что практически исключает возможность их кражи через XSS-атаки.
2.  **ID Token**: Этот токен часто оставляется доступным для чтения клиентским кодом. Это позволяет фронтенду извлекать информацию о пользователе (имя, email) для отображения в интерфейсе, не делая лишних запросов к API [02:06:41].

В процессе реализации важно учитывать нюансы обработки данных. Например, при проверке наличия групп в токене разработчик может столкнуться с ошибками типа `cannot read properties of undefined (reading 'includes')`, если объект токена не был корректно десериализован или отсутствует [02:06:56]. Использование оператора опциональной цепочки (`?.`) в Node.js позволяет избежать падения сервера при обработке «сырых» данных из Cognito.

### Реализация авторизации через Cognito Groups
[[JUMP:02:07:40]]

Аутентификация — это лишь подтверждение личности пользователя, но для полноценного приложения необходима авторизация — контроль доступа к ресурсам. AWS Cognito предоставляет для этого механизм групп. В консоли управления можно создать группу, например `admin`, и добавить туда соответствующих пользователей [02:08:06].

Существует два основных способа классификации пользователей в Cognito:

*   **Группы (Groups)**: Нативный механизм, который автоматически добавляет поле `cognito:groups` в payload токена [02:08:50].
*   **Атрибуты пользователя (Attributes)**: Можно создать кастомный атрибут, например `is_admin = true`, и проверять его значение.

При использовании групп бэкенд на Node.js должен проанализировать содержимое Access или ID токена. В рамках демонстрации разработчик показывает, что токены — это не просто случайные строки, а Base64-кодированные JSON-объекты [02:10:47]. Хотя существуют специализированные библиотеки вроде `jsonwebtoken` (JWT), для базовой проверки можно использовать встроенный класс `Buffer` в Node.js для декодирования полезной нагрузки:

```javascript
// Пример логики на бэкенде
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
const isAdmin = payload['cognito:groups']?.includes('admin');
```

Если пользователь является членом группы `admin`, бэкенд может возвращать расширенный набор данных, например, «админские задачи» вместо обычного списка дел [02:11:54].

### Middleware и жизненный цикл сессии
[[JUMP:02:12:11]]

В классическом приложении проверка прав доступа выносится в отдельный слой — Middleware. Эта функция перехватывает запросы к защищенным маршрутам и проверяет валидность Access Token в куках. Если токен невалиден или просрочен, сервер отправляет статус `401 Unauthorized` [02:12:36].

Важной темой для развития бэкенд-части является работа с **Refresh Token**. Поскольку Access Token обычно имеет короткий срок жизни, использование Refresh токена позволяет автоматически получать новую пару токенов без необходимости перенаправлять пользователя на страницу логина Cognito [02:12:51]. Это значительно улучшает пользовательский опыт, минимизируя частоту повторных авторизаций.

Интересно заметить, что структура токенов Cognito включает в себя заголовок с параметром `kid` (Key ID) [02:10:20]. Бэкенд использует его для сопоставления с публичными ключами AWS, чтобы убедиться, что токен действительно подписан вашим User Pool и не был подделан. В последующих частях курса будет рассмотрено, как внешние провайдеры (например, Google) встраиваются в эту схему, создавая внешних пользователей внутри пула Cognito [02:14:41].

## 🌐 Гибридная архитектура в AWS: Защита API с помощью Cognito

[[JUMP:2:30:44]]

### Разделение ответственности: Фронтенд на S3 и Бэкенд на EC2

[[JUMP:2:30:44]]
Гибридная архитектура представляет собой один из наиболее популярных паттернов проектирования современных облачных веб-приложений. В рамках этого сценария клиентская часть представляет собой статическое одностраничное приложение (SPA), которое хостится в высокодоступном и экономичном хранилище AWS S3, а серверная логика функционирует на базе виртуальных серверов AWS EC2, выполняя роль защищенного API. Центральной задачей при такой организации инфраструктуры становится построение надежной системы аутентификации и авторизации, способной связать изолированный клиентский интерфейс и бэкенд.

В ходе практической демонстрации работы с API Cognito через Postman наглядно показывается, как устроен базовый механизм взаимодействия приложения с пулом пользователей. Для выполнения входа отправляется HTTP POST-запрос к эндпоинту Cognito в выбранном регионе с передачей специального заголовка `CognitoIdentityProviderService.InitiateAuth`. При использовании публичного клиента (Public Client) в теле запроса передаются идентификатор клиента (`client_id`), имя пользователя, пароль и тип авторизационного потока `USER_PASSWORD_AUTH`. В примере создается тестовый пользователь Mark с подтвержденным статусом, однако первая же попытка авторизации через API возвращает системный вызов `NEW_PASSWORD_REQUIRED`. Это критически важный аспект: в условиях гибридной архитектуры обработка подобных вызовов (через API `RespondToAuthChallenge`) может инициироваться фронтендом, но сам процесс валидации и управления сессиями должен быть жестко синхронизирован с бэкендом на EC2 для предотвращения компрометации данных.

### Безопасное управление сессиями: Cookies против JWT

[[JUMP:2:38:47]]
После того как авторизационный челендж успешно пройден, Cognito возвращает стандартный набор JWT-токенов: Access Token, ID Token и Refresh Token. В классическом клиентском приложении (например, на React с использованием встроенного JavaScript SDK v3) разработчики часто сохраняют эти токены непосредственно в браузере — в памяти приложения или `localStorage`. Однако в гибридной схеме, где фронтенд на S3 взаимодействует с Node.js бэкендом на EC2, такой подход создает серьезные риски и потенциальные уязвимости перед XSS-атаками (Cross-Site Scripting).

Для обеспечения максимального уровня безопасности в архитектуре SPA+Backend применяется стратегия передачи токенов через защищенные сессионные куки. Вместо того чтобы отдавать JWT-токены напрямую в клиентский JavaScript-код, Node.js бэкенд берет на себя роль посредника:

* Фронтенд отправляет учетные данные пользователя на защищенный эндпоинт Node.js бэкенда.

* Бэкенд на EC2 выполняет запрос к AWS Cognito API (`InitiateAuth`), выступая в роли доверенной стороны.

* Полученные от Cognito JWT-токены перехватываются сервером, шифруются и упаковываются в сессионную куку с флагами `HttpOnly`, `Secure` и `SameSite=Strict`.

* Браузер сохраняет куку и автоматически прикрепляет ее к каждому последующему API-запросу к серверу на EC2.

При такой реализации вредоносные скрипты на фронтенде физически не могут получить доступ к содержимому токенов, что сводит на нет риски утечки сессии, сохраняя при этом все преимущества бесшовного взаимодействия между S3 и EC2.

### Использование Private Client и сквозная верификация токенов

[[JUMP:2:48:19]]
Важнейшим архитектурным преимуществом разделения приложения на фронтенд и бэкенд является возможность полноценного использования приватного клиента Cognito (Private Client), поддерживающего секретный ключ — `client_secret`. Если публичный клиент в React-приложении не имеет возможности безопасно хранить секреты, то Node.js бэкенд, развернутый в закрыком контуре EC2, идеально подходит для безопасного хранения конфиденциальных данных авторизации.

Когда запрос от фронтенда (содержащий сессионную куку или токен) поступает на бэкенд, сервер должен самостоятельно выполнить процедуру валидации. Как отмечает автор курса, механизм проверки JWT всегда остается неизменным. Бэкенд выполняет декодирование токена (структуру которого можно детально изучить через такие сервисы, как `jwt.io`) и осуществляет строгую проверку по нескольким параметрам:

* Проверка подписи токена с использованием публичных ключей (JWKS) пула пользователей Cognito.

* Валидация URL издателя токена (Issuer URL), подтверждающая, что токен выпущен именно вашим пулем Cognito.

* Проверка поля аудитории (`aud`), значение которого должно строго совпадать с идентификатором клиента `client_id`.

Аналогичный подход применяется и при использовании других инфраструктурных компонентов AWS, таких как интеграция Cognito с HTTP API Gateway для защиты Lambda-функций, детально рассматриваемая далее в рамках курса. Если токен оказывается невалидным или срок его действия истек, бэкенд немедленно прерывает выполнение операции и возвращает клиенту стандартный ответ со статусом `Unauthorized`. Это гарантирует, что внутренние эндпоинты бэкенда и бизнес-логика приложения остаются полностью защищенными от несанкционированного доступа.

## 🌐 Интеграция сторонних провайдеров: подключение Google к Cognito Identity Pools
[[JUMP:2:55:44]]

### Архитектура Federated Identity и концепция сквозной авторизации
[[JUMP:3:16:29]]

После разбора механизмов защиты AWS API Gateway с помощью токенов Cognito и тестирования кастомных Node.js-скриптов для проверки прав доступа к S3-корзинам, логичным шагом эволюции архитектуры становится расширение способов аутентификации. Ранее в разговоре лектор подробно описывал фундаментальные различия между User Pools и Identity Pools, а также особенности разграничения прав для авторизованных и гостевых пользователей. Теперь, когда базовая инфраструктура проверена, пришло время связать созданный Identity Pool с внешним глобальным провайдером учетных записей — Google, реализовав полноценную схему Federated Identity.

Взаимодействие приложения с внешним провайдером в лице экосистемы Google строится по стандартной и уже знакомой схеме федеративного управления удостоверениями. Автор демонстрирует схему из статьи на Medium, ссылка на которую прикреплена к корню репозитория курса, и подчеркивает, что этот воркфлоу концептуально полностью идентичен работе с нативными пулами пользователей Cognito.

Логика обмена данными и авторизации выглядит следующим образом:

* Клиентское приложение (фронтенд) инициирует запрос к серверам Google для аутентификации пользователя.
* После успешного ввода логина и пароля клиент получает от Google валидный цифровой ID-токен.
* На следующем шаге этот ID-токен перенаправляется фронтендом напрямую в AWS Cognito Identity Pool.
* Проверив подлинность внешней подписи, Cognito находит существующий профиль (или создает новую идентичность) и обращается к AWS STS, чтобы сгенерировать временные IAM-учетные данные.
* Используя полученные ключи (Access Key, Secret Key и Session Token), подписанные по строгому протоколу AWS Signature Version 4, клиент получает легитимный доступ к облачным ресурсам, таким как хранилище S3.

Чтобы активировать эту цепочку, администратору необходимо зайти в консоль AWS Cognito, открыть созданный Identity Pool, перейти в раздел «User access» (Пользовательский доступ), выбрать вкладку «Identity providers» (Провайдеры идентичности) и нажать кнопку добавления нового провайдера, выбрав Google. Для завершения интеграции на стороне AWS система потребует ввести уникальный идентификатор — Google Client ID.

### Настройка Google Cloud Platform: создание проекта и OAuth-брендинга
[[JUMP:3:18:07]]

Для получения необходимых конфигурационных ключей лектор переходит в консоль Google Cloud Platform по адресу `console.cloud.google.com`. Он отдельно акцентирует внимание на том, что для выполнения этой задачи разработчикам не требуется привязывать платную подписку или активировать бизнес-аккаунт — всех возможностей бесплатного тарифа (free tier) более чем достаточно для полноценной интеграции. Чтобы продемонстрировать весь процесс в режиме «с чистого листа», в панели управления GCP создается абсолютно новый изолированный проект, получивший название «ID pool demo».

Первым обязательным шагом внутри созданного проекта становится настройка экрана согласия OAuth (OAuth consent screen), которая в современном интерфейсе Google также называется настройкой брендинга приложения. Мастер настройки запрашивает базовые метаданные: публичное имя приложения (автор использует лаконичное название «test») и поддерживаемый email-адрес разработчика для связи с пользователями. В качестве типа аудитории (User Type) выбирается вариант External (внешняя аудитория), поскольку приложение создается для интеграции через Cognito для широкого круга независимых пользователей. После указания финальной контактной информации и сохранения параметров система позволяет перейти к генерации технических ключей доступа.

### Настройка параметров веб-приложения и авторизационных URL
[[JUMP:3:19:44]]

Финальный этап работы в панели Google Cloud — создание учетных данных типа OAuth Client ID. В выпадающем списке типов приложений (Application type) лектор выбирает Web application (веб-приложение). На этом шаге автор делает важнейшее архитектурное замечание, указывающее на разницу между интеграцией внешних провайдеров в User Pools и в Identity Pools. При работе с User Pools сторонний провайдер должен отправлять данные на специальный callback-URL, принадлежащий самому Cognito (Hosted UI). Но в случае с Identity Pools вся ответственность за получение токена от Google и его последующий обмен ложится на плечи самого клиента (клиентского кода на фронтенде).

Поскольку клиент выполняет всю логику обмена самостоятельно, в поле разрешенных URI перенаправления (Authorized redirect URIs) нет необходимости прописывать адреса AWS. Вместо этого разработчик может указать свой локальный адрес для тестирования. Автор вводит URL `http://localhost:3000`. Это означает, что после успешной авторизации на серверах Google код авторизации (или токен) вернется на локальный порт 3000, где его подхватит запущенное SPA-приложение.

После нажатия кнопки «Create» (Создать) на экране появляется диалоговое окно со сгенерированным значением Client ID. Этот длинный строковый идентификатор копируется автором, так как именно его необходимо передать обратно в настройки AWS Cognito Identity Pool для завершения связывания федеративных учетных записей.

## 🛠️ Работа с AWS Cognito API, SDK и интеграция безопасности

[[JUMP:3:20:48]]

Для взаимодействия с AWS Cognito программно разработчики могут использовать как прямое обращение к API, так и специализированные AWS SDK. Работа с API Cognito подразумевает управление жизненным циклом пользователей, обмен кодов авторизации и получение токенов. Например, процесс получения токена через Google как провайдера идентичности требует выполнения `POST`-запроса с параметрами `code`, `grant_type` (установленным в `authorization_code`), `redirect_uri`, `client_id` и `client_secret`. Полученный после этого `ID Token` становится ключом для создания сущности в Identity Pool через API-вызов `GetId`.

Использование AWS SDK (например, JavaScript SDK) значительно упрощает этот процесс, переводя сложные REST-запросы в методы библиотек. Инициализация клиента в SDK требует передачи `IdentityPoolId` и настроек `Logins` для аутентифицированных пользователей, в то время как для гостевых пользователей этот параметр остается пустым. Все функции, доступные через CLI или прямой API, такие как `GetId` или `GetCredentialsForIdentity`, реализованы как методы в библиотеке `@aws-sdk/client-cognito-identity`.

### 🔒 Безопасность и контроль доступа к S3

[[JUMP:3:28:06]]

Одной из критических задач является разграничение доступа к ресурсам (например, S3-бакетам) в зависимости от статуса пользователя. Ранее в разговоре уже затрагивалась общая архитектура аутентификации. На уровне Identity Pool можно настроить гибкие правила доступа, используя утверждения (claims) из `ID Token`.

Для каждого провайдера идентичности (Cognito User Pool или Google) можно задать политики прав:

* **Роли по умолчанию:** Базовые права для аутентифицированных и гостевых (anonymous) пользователей.
* **Rule-based Access:** Возможность назначения специфических ролей на основе данных внутри токена. Например, если в токене присутствует `email` конкретного пользователя или информация о членстве в группе (`group: admin`), можно динамически привязать более привилегированную роль с правом записи в S3.

Такой подход позволяет реализовать сценарии, где, скажем, все пользователи могут читать файлы, но только определенные (по результатам проверки claims) имеют право на загрузку новых объектов.

### 🏗️ Практическая реализация: приложение «Share My Files»

[[JUMP:3:32:18]]

Архитектура приложения «Share My Files» строится на взаимодействии фронтенда (React) с сервисами AWS. Приложение поддерживает два типа сессий:

1.  **Аутентифицированные пользователи:** Входят через User Pool или стороннего провайдера (Google). Полученные учетные данные обмениваются в Identity Pool на временные IAM-кредиты, дающие право на чтение и запись в S3.
2.  **Гостевые пользователи:** Получают через Identity Pool ограниченные IAM-права, позволяющие только скачивать файлы по уникальному ключу.

Для реализации этого функционала в React-приложении используются такие библиотеки, как `oidc-client-ts` для управления потоком авторизации. В файле `utils.jsx` инкапсулированы ключевые функции:

* `GetId`: создание идентификатора в Identity Pool.
* `GetIAMCredentials`: получение временных прав от AWS STS, которые затем используются для взаимодействия с S3-бакетом.

При настройке ресурсов в консоли AWS важно убедиться, что для Identity Pool определены правильные политики, разрешающие действие `cognito-identity:GetCredentialsForIdentity`, которое является необходимым условием для работы механизма выдачи временных токенов через AWS STS.

## 🔐 Реализация безопасного обмена файлами через Identity Pools

[[JUMP:345:58]]

В завершающей части курса по AWS Cognito основное внимание уделяется практической реализации системы безопасного обмена файлами. Ключевым компонентом здесь выступает интеграция с Identity Pools, которая позволяет разграничить права доступа к ресурсам S3 для аутентифицированных пользователей и гостей через назначение специфических IAM-ролей.

### Настройка доступа к ресурсам S3
[[JUMP:346:38]]

Процесс начинается с создания и конфигурации Identity Pool, где указывается соответствующий клиент (в данном примере — «share my file»). После инициализации Identity Pool необходимо настроить права доступа для двух категорий пользователей: аутентифицированных и гостевых. Для этого используются политики IAM, определяющие доступ к объектам в бакете S3.

*   **Аутентифицированные пользователи:** Для них настраивается политика, разрешающая выполнение операций `GetObject` и `PutObject` для целевого S3-бакета.
*   **Гостевые пользователи:** Права ограничиваются только операцией `GetObject`, что позволяет им скачивать файлы, не имея возможности загружать новые.

После редактирования политик необходимо обновить имя целевого S3-бакета в конфигурациях обеих ролей, чтобы политики корректно применялись к создаваемому хранилищу.

### Настройка CORS для взаимодействия SPA и S3
[[JUMP:351:41]]

При попытке загрузить файл из React-приложения, работающего на `localhost:5173`, возникает ошибка CORS (Cross-Origin Resource Sharing), так как домен приложения отличается от домена, обслуживающего S3-бакет. Решением является обновление настроек CORS в политике S3-бакета. В разделе «Permissions» необходимо добавить правило, которое явно разрешает `AllowedOrigin` для используемого локального домена, а также методы `GET` и `PUT`. После внесения правок загрузка файлов через клиентское приложение проходит успешно.

### Архитектура безопасности: аутентификация и загрузка
[[JUMP:353:17]]

Система обмена файлами использует уникальный механизм формирования путей: имя файла включает хеш email-адреса загрузившего пользователя и дополнительную строку, выступающую в роли пароля. 

*   **Аутентифицированный пользователь:** После входа в систему через User Pool (ранее в курсе обсуждались основы работы с токенами и OAuth 2.0), приложение получает необходимые полномочия от Identity Pool для работы с S3.
*   **Гостевой доступ:** Пользователь, не имеющий учетной записи, может скачать файл, перейдя по прямой ссылке, при условии, что он знает точное имя файла, включающее парольную часть. При вводе неверного пароля сервер возвращает ошибку 403.

### Анализ идентичностей в Identity Pools
[[JUMP:401:45]]

Identity Browser в консоли AWS Cognito позволяет отслеживать активность пользователей. В системе четко разделяются два типа идентичностей:

1.  **Аутентифицированные:** Пользователи, вошедшие в систему через User Pool или через интегрированного стороннего провайдера (в данном случае — Google).
2.  **Неавторизованные (гости):** Пользователи, совершающие действия без входа в учетную запись (например, через режим инкогнито).

Такой подход позволяет администраторам видеть детальную статистику и контролировать поток доступа к защищенным ресурсам на уровне инфраструктуры.