소개
주문 애플리케이션에는 사용자가 로그인할 방법이 필요합니다. 사용자 디렉터리와 공개 앱 클라이언트를 만들고 테스트 사용자를 로그인시킨 다음 발급된 토큰을 검증하고 세션을 갱신한 후 정리합니다.
주문 API 구축 및 검증을 먼저 완료하세요. 이 새 환경에는 독립적인 공개 백엔드, 일회용 로그인 입력과 토큰 검증 예제가 제공됩니다. 비밀번호와 토큰 파일은 비공개로 유지하세요.
인증 시험 관련 주제
| 인증 | 시험 과제 | 실습 |
|---|---|---|
| Solutions Architect – Associate (SAA-C03) | 태스크 1.2 | Cognito 로그인, 토큰 검증과 세션 갱신. |
| Developer – Associate (DVA-C02) | 태스크 2.1 | Cognito 로그인, 토큰 검증과 세션 갱신. |
| Security – Specialty (SCS-C03) | 태스크 4.1 | 기초 실습: Cognito 로그인, 토큰 검증과 세션 갱신. |
실습 개요

애플리케이션 사용자 디렉터리 만들기
이 단계에서는 사용자 풀, 공개 앱 클라이언트와 확인된 테스트 사용자를 준비합니다.
작업 공간으로 이동하고 umask 077을 사용하여 새로 만드는 파일에 본인만 접근할 수 있도록 하세요. 이 설정은 파일 권한에 영향을 주며 API의 권한 부여 규칙에는 영향을 주지 않습니다.
cd /home/labex/project
umask 077
Cognito 사용자 풀은 애플리케이션의 사용자 디렉터리입니다. 사용자 풀의 사용자는 애플리케이션에 로그인하고, 준비된 IAM CLI 신원은 클라우드 리소스를 관리합니다. 위쪽 AWS View에서 이러한 네이티브 리소스를 비교하세요.
소유할 사용자 풀을 만드세요. $(...)는 반환된 풀 ID를 저장하여 이후 네이티브 명령에서 사용합니다.
POOL_ID=$(aws cognito-idp create-user-pool \
--pool-name labex-a04-users \
--query UserPool.Id \
--output text)
echo "$POOL_ID"
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에서 소유한 풀 ID, orders-client, 클라이언트 보안 암호가 없다는 점, 확인되고 활성화된 사용자를 비교하세요. 다음은 실제 네이티브 풀/클라이언트/사용자 상태의 예입니다.

관련 없는 labex-a04-reference 풀은 빈 상태로 유지됩니다. 아직 사용자 토큰은 요청하지 않았습니다.
로그인하고 애플리케이션 신원 읽기
이 단계에서는 확인된 사용자의 실제 토큰을 받고 잘못된 비밀번호가 거부되는 것을 관찰합니다.
USER_PASSWORD_AUTH를 사용하는 InitiateAuth는 애플리케이션의 사용자 이름과 비밀번호를 인증합니다. CLI의 IAM 관리자 신원을 통해 사용자에게 권한을 부여하지는 않습니다. 제공된 sign-in.json에는 이 데모의 테스트 사용자 이름/비밀번호 파라미터가 들어 있습니다.
AuthenticationResult만 비공개 파일에 저장하세요. 이 응답에는 베어러 토큰이 들어 있으므로 cat으로 출력하지 마세요.
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가 나와야 합니다. 이 명령은 의도적으로 0이 아닌 종료 코드를 반환합니다. 정확히 이 거부 메시지를 읽은 다음 다음 단계로 진행하세요. 연결 오류나 인증 전송 오류는 잘못된 비밀번호가 거부되었다는 증거가 아닙니다. 이 실패 결과로 유효한 토큰을 덮어쓰지 마세요. 이 단계의 자동 검사는 발급된 토큰과 실제 GetUser 결과를 독립적으로 검증합니다. 잘못된 비밀번호에 대한 특정 오류는 직접 관찰하세요.

서명된 토큰 검증 및 세션 갱신
이 단계에서는 토큰의 서명과 의도된 수신자를 검증하고 세션을 갱신하며 로그인과 API 라우트 보호를 구별합니다.
JWT에는 인코딩된 헤더와 클레임 데이터, 서명이 들어 있습니다. 단순히 디코딩하는 것만으로 발급자를 증명할 수는 없습니다. JWKS는 발급자의 공개 서명 키 집합입니다. RS256은 이 공개 키로 검증할 수 있는 RSA 서명을 사용합니다.
제공된 일반적인 애플리케이션 검증 예제를 읽어 보세요. 비밀번호나 토큰 리터럴은 들어 있지 않습니다.
cat verify_tokens.py
이 예제는 준비된 작업 공간 엔드포인트에서 풀의 공개 JWKS를 가져와 RS256과 키 ID를 검증한 다음 발급자, 현재 시간, token_use와 앱 클라이언트를 확인합니다. 서명된 발급자는 Cognito 풀 신원을 사용합니다. 액세스 토큰은 client_id로 클라이언트를 식별하고 ID 토큰은 aud를 사용합니다. 저장한 토큰 파일과 실제 풀/클라이언트 ID로 실행하세요.
/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"
같은 풀과 클라이언트에 대한 유효한 액세스/ID 요약이 나와야 합니다. 새로 고침 응답에는 다른 새로 고침 토큰이 포함되지 않을 수 있습니다. 정리할 때까지 원래 비공개 응답을 보관하세요.
로그인만으로 준비된 공개 API가 변경되지는 않습니다. 생성된 ID를 찾고 베어러 토큰 없이 공개 상태 확인 요청을 보내세요.
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 리소스 제거
이 단계에서는 관련 없는 참조 풀, 테이블과 로그를 유지하면서 소유한 로그인 리소스와 백엔드 준비물을 제거합니다.
인벤토리에는 pool-id.txt에 기록된 labex-a04-users, client-id.txt에 기록된 orders-client, labex-demo 사용자, api-id.txt의 API ID, 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 서명 및 클레임 검증; 세션 갱신 및 갱신 토큰 흐름.



