Добавление входа в приложение с помощью Cognito

AWSBeginner
Практиковаться сейчас

Введение

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

Сначала выполните Создание и проверка API заказов. Эта свежая среда предоставляет свою общедоступную серверную часть, временные входные данные для входа и пример проверки токенов. Сохраняйте файлы паролей и токенов приватными.

Связь с сертификацией

Сертификация Задача экзамена Практика
Solutions Architect – Associate (SAA-C03) Задача 1.2 Вход через Cognito, проверка токенов и обновление сеанса.
Developer – Associate (DVA-C02) Задача 2.1 Вход через Cognito, проверка токенов и обновление сеанса.
Security – Specialty (SCS-C03) Задача 4.1 Базовая практика: Вход через Cognito, проверка токенов и обновление сеанса.

Обзор лабораторной работы

Концептуальная схема: клиент входа использует пул пользователей Cognito и публичный клиент приложения для получения, проверки и обновления токенов сеанса; сам вход не защищает отдельный публичный API проверки состояния.

Создайте каталог пользователей приложения

На этом шаге вы создадите пул пользователей, общедоступный клиент приложения и подтверждённого синтетического пользователя.

Перейдите в рабочую папку и используйте umask 077, чтобы новые файлы были доступны только вашему пользователю. Это влияет на разрешения файлов, а не на правила авторизации API:

cd /home/labex/project
umask 077

Пул пользователей Cognito — каталог пользователей вашего приложения. Его пользователи входят в приложение; ваша подготовленная идентичность IAM CLI управляет облачными ресурсами. Посмотрите на AWS View вверху для сравнения этих собственных ресурсов сервиса.

Создайте пул пользователей работы. $(...) сохраняет возвращённый идентификатор пула для последующих команд сервиса:

POOL_ID=$(aws cognito-idp create-user-pool \
  --pool-name labex-a04-users \
  --query UserPool.Id \
  --output text)
echo "$POOL_ID"

Запишите его идентификатор; > записывает значение в файл списка ресурсов:

printf '%s\n' "$POOL_ID" > pool-id.txt

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

CLIENT_ID=$(aws cognito-idp create-user-pool-client \
  --user-pool-id "$POOL_ID" \
  --client-name orders-client \
  --no-generate-secret \
  --explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
  --query UserPoolClient.ClientId \
  --output text)
echo "$CLIENT_ID"
printf '%s\n' "$CLIENT_ID" > client-id.txt

Изучите безопасную конфигурацию клиента, не запрашивая секретные значения:

aws cognito-idp describe-user-pool-client \
  --user-pool-id "$POOL_ID" \
  --client-id "$CLIENT_ID" \
  --query '{Name:UserPoolClient.ClientName,Flows:UserPoolClient.ExplicitAuthFlows}'

Создайте демонстрационного пользователя с отключённой отправкой электронной почты. Это действие подготовки администратором, отдельное от последующего входа пользователя:

aws cognito-idp admin-create-user \
  --user-pool-id "$POOL_ID" \
  --username labex-demo \
  --message-action SUPPRESS \
  --user-attributes Name=email,Value=demo@example.test \
  --query '{Username:User.Username,Status:User.UserStatus}'

Начальный статус — FORCE_CHANGE_PASSWORD. Только для этого временного тестового пользователя примените предоставленный входной файл постоянного пароля через CLI. Файл содержит поля Username, Password и Permanent; синтаксис file:// читает их без вывода пароля:

aws cognito-idp admin-set-user-password \
  --user-pool-id "$POOL_ID" \
  --cli-input-json file://demo-password.json

Изучите только безопасный статус пользователя:

aws cognito-idp admin-get-user \
  --user-pool-id "$POOL_ID" \
  --username labex-demo \
  --query '{Username:Username,Status:UserStatus,Enabled:Enabled}'

Ожидайте labex-demo, CONFIRMED и true. В AWS View сравните идентификатор пула работы, orders-client, отсутствие секрета клиента и подтверждённого включённого пользователя. Пример фактического состояния пула, клиента и пользователя сервиса:

Пул Cognito, общедоступный клиент приложения и подтверждённый демонстрационный пользователь

Независимый пул labex-a04-reference остаётся пустым. Токен пользователя ещё не запрашивался.

Войдите и прочитайте идентичность приложения

На этом шаге вы получите реальные токены для подтверждённого пользователя и наблюдаете отклонение неверного пароля.

InitiateAuth с USER_PASSWORD_AUTH аутентифицирует имя пользователя и пароль приложения. Он не авторизует пользователя через идентичность администратора IAM вашего CLI. Предоставленный sign-in.json содержит синтетические параметры имени пользователя и пароля для этой демонстрации.

Сохраните только AuthenticationResult в приватном файле. Не используйте cat для этого ответа; он содержит bearer-токены:

aws cognito-idp initiate-auth --client-id "$CLIENT_ID" --auth-flow USER_PASSWORD_AUTH --auth-parameters file://sign-in.json --query AuthenticationResult --output json > tokens.json

Покажите только несекретные поля ответа с помощью jq. Выражение наличия токенов возвращает логическое значение вместо значений токенов:

jq '{TokenType,ExpiresIn,TokensPresent:(has("AccessToken") and has("IdToken") and has("RefreshToken"))}' tokens.json

Ожидайте Bearer, положительный срок действия и TokensPresent: true. Токен доступа авторизует соответствующие запросы пользователя или API, токен ID содержит информацию об идентичности для приложения, а токен обновления может получать новые токены доступа и ID. У них разные назначения, и они не взаимозаменяемы.

Извлеките токены доступа и ID в приватные транспортные файлы. jq -r записывает строки без кавычек, а перенаправление не даёт им попасть в терминал:

jq -r '.AccessToken' tokens.json > access-token.txt
jq -r '.IdToken' tokens.json > id-token.txt

GetUser аутентифицируется токеном доступа. Запросите в выводе только имя пользователя:

aws cognito-idp get-user \
  --access-token "$(cat access-token.txt)" \
  --query Username \
  --output text

Ожидайте labex-demo. Это живой ответ Cognito на выданный токен, а не доказательство, выведенное только из локального файла JSON.

Попробуйте предоставленные параметры неверного пароля, перенаправив любой неожиданный ответ с токенами в отдельный приватный файл:

aws cognito-idp initiate-auth --client-id "$CLIENT_ID" --auth-flow USER_PASSWORD_AUTH --auth-parameters file://wrong-sign-in.json --query AuthenticationResult --output json > rejected-login.json

Ожидайте NotAuthorizedException: Incorrect username or password. Команда намеренно завершается с ненулевым кодом; продолжайте следующий шаг после чтения именно этого отказа. Ошибка подключения или транспорта аутентификации не доказывает отклонение неверного пароля. Не перезаписывайте действительные токены этим неудачным результатом. Автоматическая проверка шага независимо проверяет выданные токены и живой результат GetUser; конкретную ошибку неверного пароля наблюдайте вручную.

Клиент приложения и подтверждённый пользователь принадлежат одному пулу; успешный вход возвращает токены.

Проверьте подписанные токены и обновите сеанс

На этом шаге вы проверите подпись токена и предполагаемого получателя, обновите сеанс и отличите вход от защиты маршрутов API.

JWT содержит закодированные данные заголовка и утверждений и подпись. Одно декодирование не доказывает, кто его выдал. JWKS — набор открытых ключей подписи издателя; RS256 использует подпись RSA, которую можно проверить этими открытыми ключами.

Прочитайте предоставленный обычный пример проверки в приложении; он не содержит буквальных паролей или токенов:

cat verify_tokens.py

Пример получает открытый JWKS вашего пула из подготовленной конечной точки рабочей среды, проверяет RS256 и идентификатор ключа, затем проверяет издателя, текущее время, token_use и клиент приложения. Подписанный издатель использует идентичность пула Cognito. Токен доступа определяет клиент через client_id; токен ID использует aud. Запустите пример с сохранённым файлом токенов и фактическими идентификаторами пула и клиента:

/home/labex/project/client-venv/bin/python verify_tokens.py tokens.json "$POOL_ID" "$CLIENT_ID"

Ожидайте безопасные сводки для access и id, каждая с RS256 и current_time_valid: true. Пример также меняет один байт подписи в памяти и подтверждает отказ до принятия утверждений; изменённый токен не сохраняется. Область токена доступа aws.cognito.signin.user.admin разрешает операции самообслуживания пользователя Cognito. Эта область не является доступом администратора AWS, правилом владения заказом или собственной бизнес-областью.

Создайте приватные параметры обновления из исходного токена обновления. Это записывает файл и не раскрывает значение:

jq '{REFRESH_TOKEN:.RefreshToken}' tokens.json > refresh-parameters.json

Получите новые токены доступа и ID без повторной отправки пароля пользователя:

aws cognito-idp initiate-auth --client-id "$CLIENT_ID" --auth-flow REFRESH_TOKEN_AUTH --auth-parameters file://refresh-parameters.json --query AuthenticationResult --output json > refreshed.json

Проверьте обновлённые токены теми же проверками открытых ключей и утверждений:

/home/labex/project/client-venv/bin/python verify_tokens.py refreshed.json "$POOL_ID" "$CLIENT_ID"

Ожидайте действительные сводки access/ID для того же пула и клиента. Ответ обновления не обязан содержать ещё один токен обновления; сохраните исходный приватный ответ до очистки.

Один вход не меняет подготовленный общедоступный API. Найдите его сгенерированный идентификатор и отправьте общедоступный запрос состояния без bearer-токена:

API_ID=$(aws apigatewayv2 get-apis \
  --query "Items[?Name=='labex-a04'].ApiId | [0]" \
  --output text)
echo "$API_ID"
printf '%s\n' "$API_ID" > api-id.txt
curl -i "http://127.0.0.1:8081/api/$API_ID/health"

Ожидайте HTTP 200 и healthy: true. AWS View всё ещё показывает авторизацию NONE для всех маршрутов и отсутствие элементов заказов. Аутентификация пользователя успешна, но API ещё не требует токен. Следующая работа добавляет авторизацию маршрутов JWT; действительный токен всё ещё не устанавливает автоматически владение конкретным заказом.

Обновление продолжает сеанс входа для того же клиента приложения. Оно не меняет настройки авторизации маршрута API и не предоставляет доступ к заказу. Разделяйте эти решения, сопоставляя обновлённые сводки идентичности с по-прежнему публичным ответом проверки состояния.

Удалите ресурсы входа и API

На этом шаге вы удалите ресурсы входа работы и подготовленную серверную часть, сохранив независимые эталонные пул, таблицу и журналы.

Ваш список ресурсов — labex-a04-users из pool-id.txt, orders-client из client-id.txt, пользователь labex-demo, идентификатор API в api-id.txt, labex-a04-orders-api, его группа журналов функции, labex-a04-orders и политика роли OrdersData. Удалите только эти именованные ресурсы.

Удалите синтетического пользователя, затем клиент приложения и пул работы:

aws cognito-idp admin-delete-user --user-pool-id "$POOL_ID" --username labex-demo
aws cognito-idp delete-user-pool-client --user-pool-id "$POOL_ID" --client-id "$CLIENT_ID"
aws cognito-idp delete-user-pool --user-pool-id "$POOL_ID"

Удалите подготовленный рабочий API и ресурсы его серверной части:

aws apigatewayv2 delete-api --api-id "$API_ID"
aws lambda delete-function --function-name labex-a04-orders-api
aws logs delete-log-group --log-group-name /aws/lambda/labex-a04-orders-api
aws dynamodb delete-table \
  --table-name labex-a04-orders \
  --query TableDescription.TableName \
  --output text
aws iam delete-role-policy --role-name labex-a04-execution --policy-name OrdersData

Успешно запросите собственные списки сервисов; транспортные сбои не доказывают отсутствие:

aws cognito-idp list-user-pools --max-results 60 --query 'UserPools[].Name'
aws apigatewayv2 get-apis --query 'Items[].Name'
aws lambda list-functions --query 'Functions[].FunctionName'
aws dynamodb list-tables --query TableNames

Ожидайте только эталонный пул и эталонную таблицу при пустых списках API и функций. AWS View должен показывать то же состояние и сохранять эталонный элемент и INFO platform reference keep unchanged. Подготовленная роль выполнения сохраняет FunctionLogs.

Удалите приватные файлы аутентификации, включая предоставленные входные данные синтетического пароля. rm -f удаляет только явно перечисленные локальные файлы:

rm -f tokens.json access-token.txt id-token.txt refreshed.json refresh-parameters.json rejected-login.json sign-in.json wrong-sign-in.json demo-password.json

Одно удаление локальных файлов не является отзывом сеанса на сервере. Здесь синтетические пользователь, клиент и пул также удалены; эта работа не заявляет общий механизм выхода или отзыва JWT. Сохраните независимые эталонные ресурсы до очистки автором и выключения VM.

Резюме

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

Подробнее см. в документации AWS: Назначение токенов пула пользователей; Подписи JWT и проверка утверждений; Обновление сеанса и потоки токенов обновления.