Cognito 로그인 코드가 실패하는 이유: App Client에서 해당 Auth Flow를 허용하지 않았기 때문
요약
AWS Cognito 사용 시 AI 어시스턴트가 생성한 코드가 스테이징 환경에서 실패하는 원인을 분석합니다. 앱 클라이언트의 인증 플로우 설정, 클라이언트 시크릿 유무, MFA 설정 등이 코드 작동에 미치는 영향을 설명합니다.
핵심 포인트
- 앱 클라이언트의 허용된 인증 플로우(Allowed auth flows) 설정 확인 필요
- 클라이언트 시크릿 사용 시 SECRET_HASH 포함 필수
- MFA 설정 시 ChallengeName과 세션 처리 로직 구현 필요
- 토큰 유효성 단위(분/일)에 따른 갱신 로직 검토
로컬 환경에서는 로그인 화면이 잘 작동합니다. 하지만 스테이징 (staging) 사용자 풀 (user pool)을 가리키도록 설정하면, 모든 로그인 시도가 비밀번호 확인 단계에 도달하기도 전에 에러를 반환합니다.
당신의 AI 어시스턴트가 작성한 코드는 AuthFlow: 'USER_PASSWORD_AUTH'를 사용하여 InitiateAuth를 호출합니다. 대부분의 Cognito 튜토리얼에서 사용하는 방식이기에 합리적인 추측일 수 있습니다. 하지만 스테이징의 앱 클라이언트 (app client)는 explicit_auth_flows = ["ALLOW_USER_SRP_AUTH", "ALLOW_REFRESH_TOKEN_AUTH"]로 설정된 Terraform 모듈에 의해 생성되었으며, 해당 클라이언트는 시크릿 (secret)도 가지고 있습니다. 따라서 이 호출은 두 가지 이유로 실패합니다. 첫째, 이 클라이언트에 해당 플로우 (flow)가 활성화되어 있지 않고, 둘째, 요청에 SECRET_HASH가 누락되었기 때문입니다.
이 중 어떤 것도 소스 파일에서는 확인할 수 없습니다. 어시스턴트는 당신의 저장소 (repo)를 읽었지만 답을 찾지 못했고, 인터넷에서 통계적으로 가장 흔한 Cognito 코드 스니펫 (snippet)을 생성한 것입니다.
앱 클라이언트가 설정의 핵심이며, 당신의 저장소에는 없습니다
Cognito의 실패 모드 (failure modes)는 대부분 사용자 풀 (user pool) 설정이 아닌 앱 클라이언트 (app client) 설정에 따라 결정됩니다. 다음 네 가지 요소가 생성된 코드를 즉시 중단시킵니다:
허용된 인증 플로우 (Allowed auth flows). ExplicitAuthFlows는 화이트리스트 (whitelist)입니다. 만약 ALLOW_USER_PASSWORD_AUTH가 여기에 포함되어 있지 않다면, 사용자 이름과 비밀번호가 올바른지 여부와 상관없이 USER_PASSWORD_AUTH는 거부됩니다. 개발 (dev) 환경의 클라이언트 A는 이를 허용할 수 있지만, 운영 (prod) 환경의 클라이언트 B는 허용하지 않을 수 있으며, 이로 인해 동일한 핸들러 (handler) 코드가 한 환경에서는 작동하고 다른 환경에서는 작동하지 않게 됩니다.
클라이언트 시크릿 (Client secret). GenerateSecret: true로 설정된 클라이언트는 모든 인증 호출 시 SECRET_HASH를 포함해야 합니다. 이는 클라이언트 시크릿을 키로 사용하여 username + clientId를 base64 HMAC-SHA256으로 인코딩한 값입니다. 시크릿의 존재를 모르는 어시스턴트는 이 필드를 절대 생성하지 않을 것이며, 호출은 자격 증명 (credentials)이 아닌 시크릿 검증 단계에서 실패하게 됩니다.
MFA (다요소 인증). MFA가 ON으로 설정되어 있으면, InitiateAuth는 일반적으로 토큰 (tokens) 대신 ChallengeName과 세션 (session)을 반환합니다. 정상적인 경로 (happy path)만을 가정하고 작성된 코드는 response.AuthenticationResult.IdToken을 읽으려 시도하다가 undefined를 받게 되고, 실제 원인으로부터 세 단계나 떨어진 곳에서 에러를 던지게 됩니다.
토큰 유효성 단위 (Token validity units). AccessTokenValidity: 60 자체만으로는 아무런 의미가 없습니다. TokenValidityUnits.AccessToken = 'minutes'(분)라면 1시간을 의미하지만, 'days'(일)라면 두 달을 의미합니다. 잘못된 단위를 기반으로 구축된 갱신(Refresh) 로직은 토큰 엔드포인트(token endpoint)를 과도하게 호출하거나 세션을 종료시켜 버립니다.
OAuth 설정 (OAuth settings). 직접 인증 대신 호스팅된 UI (hosted UI)를 사용하는 경우, 클라이언트는 자체적인 AllowedOAuthFlows, AllowedOAuthScopes, CallbackURLs를 가집니다. 콜백 리스트에 없는 URL로 리다이렉트(redirect)를 생성하거나 클라이언트가 허용하지 않는 스코프(scope)를 요청하면, Cognito는 앱이 무엇인가를 확인하기도 전에 권한 부여 엔드포인트(authorize endpoint)에서 요청을 거부합니다. 리다이렉트를 작성하는 어시스턴트는 스테이징 클라이언트가 https://staging.example.com/callback만 등록되어 있고, 로컬 개발 URL은 추가되지 않았다는 사실을 알 방법이 없습니다.
이러한 설정들은 모두 저장소(repository)가 아닌 AWS 내에 존재합니다. 사용자 풀(user pool)이 Terraform으로 정의되어 있더라도, 어시스턴트는 적절한 모듈을 찾아 변수를 해결하고 실행 중인 서비스가 실제로 어떤 클라이언트를 사용하는지 알아내야 합니다. 실제로 어시스턴트는 이를 수행하지 못하므로 추측을 하게 됩니다.
실제 클라이언트 설정 읽기
Infrawise는 이를 추출하여 MCP를 통해 어시스턴트에게 전달합니다. src/adapters/aws/services.ts에 있는 Cognito 추출기(extractor)는 네 가지 읽기 전용 호출을 수행합니다: 모든 풀에 대한 ListUserPools, 각 풀에 대한 DescribeUserPool, ListUserPoolClients, 그리고 각 클라이언트에 대한 DescribeUserPoolClient입니다. 두 목록 조회 모두 NextToken을 사용한 페이지네이션(pagination)이 적용되어 있으므로, 80개의 앱 클라이언트를 가진 풀이라도 첫 페이지에서 조용히 잘리는 일이 발생하지 않습니다.
각 클라이언트에 대해, 호출 방식을 결정짓는 필드들만 정확히 유지합니다:
clientName, clientId
authFlows <- ExplicitAuthFlows
oauthFlows <- AllowedOAuthFlows
...
generatesSecret에 유의하세요. DescribeUserPoolClient는 시크릿(secret) 값을 반환하지만, infrawise는 추출 시점에 이를 불리언(boolean) 값으로 변환합니다. 이 값은 그래프에 저장되지 않으며, 캐싱되지도 않고, 어떤 도구(tool)에 의해서도 반환되지 않습니다. 어시스턴트는 시크릿을 직접 보지 않고도 시크릿이 존재한다는 사실과 SECRET_HASH가 필수적이라는 사실을 학습합니다. 사용자(user)의 경우도 마찬가지입니다. infrawise는 어떠한 사용자 API도 호출하지 않습니다. 이 도구가 도와주는 것은 로그인 코드를 작성하는 것이지, 사용자 디렉토리를 읽는 것이 아닙니다.
get_cognito_overview MCP 도구는 전체 내용을 반환합니다:
{
"total": 1,
"note": "Client secret values and user data are never included.",
...
이것이 어시스턴트가 단순히 USER_PASSWORD_AUTH를 추측하는 것과, 화이트리스트(whitelist)와 시크릿 플래그(secret flag)가 컨텍스트(context)에 바로 놓여 있기 때문에 SECRET_HASH를 포함한 SRP를 작성하는 것 사이의 차이점입니다.
src/server/index.ts에 등록된 도구 설명(tool description)은 모델에게 언제 이 도구를 사용해야 하고 언제 사용하지 말아야 하는지를 알려줍니다. 즉, 로그인(sign-in), 회원가입(sign-up), 또는 토큰 갱신(token-refresh) 코드를 작성하기 전에 호출하되, 사용자를 조회하거나 토큰을 찾기 위해 호출하지는 말라고 명시합니다. 이 마지막 조항은 보이는 것보다 훨씬 중요합니다. 도구 설명은 에이전트(agent)가 어떤 도구를 선택할지 결정하는 유일한 길잡이이며, 사용자 디렉토리처럼 들리는 도구는 잘못된 이유로 호출될 수 있기 때문입니다.
활성화하기
Cognito는 기본적으로 꺼져 있습니다. infrawise start는 cognito: { enabled: false }가 포함된 infrawise.yaml 파일을 작성하는데, 대부분의 리포지토리(repo)에는 Cognito가 없으며 이를 위해 API를 호출할 이유가 없기 때문입니다. 인증(Auth) 작업을 수행하려면 키 하나만 바꾸면 됩니다:
cognito:
enabled: true
IAM 정책은 네 가지 읽기 작업(read actions)뿐이며, 그 외에는 아무것도 없습니다:
cognito-idp:ListUserPools
cognito-idp:DescribeUserPool
cognito-idp:ListUserPoolClients
...
그 다음:
infrawise start --claude
이 도구는 환경을 조사하고, 분석을 실행하며, .mcp.json 파일을 작성하여 향후 모든 실행 시 에디터가 다시 연결되도록 하고, 21개의 도구를 모두 사용할 수 있는 상태로 Claude Code를 실행합니다. 그 이후부터는 단순히 claude만 실행하면 됩니다. 결과는 24시간 동안 캐시되며, get_infra_overview는 분석 시령(age)을 담은 freshness 객체와 stale 플래그를 보고하여, 어시스턴트가 자신이 어제의 정보를 보고 있는지 판단할 수 있게 합니다.
"스테이징 풀(staging pool)을 위한 로그인 핸들러를 작성해줘"라고 요청하면, 더 이상 흐름(flow)을 추측할 필요가 없습니다. 어시스턴트는 get_cognito_overview를 호출하여 ALLOW_USER_SRP_AUTH와 generatesSecret: true를 확인하고, 첫 번째 실행 시 시크릿 해시(secret hash)를 포함한 SRP를 작성합니다.
이것이 실제로 제공하는 가치
여기서 발생하는 버그 유형은 지루한 종류이며, 바로 그 점 때문에 많은 시간을 잡아먹습니다. 빌드 타임에는 아무것도 충돌하지 않습니다. 타입(Types)도 문제없습니다. Cognito 클라이언트를 모킹(mock)한 테스트도 통과합니다. 실패는 실제 사용자 풀(user pool)을 대상으로 할 때만 나타나는데, 앱 클라이언트가 허용하지 않았다는 내용이 아니라 흐름 이름(flow name)에 관한 메시지를 담은 예외(exception)로 나타납니다. 그리고 해결책은 콘솔 탭에 가서 직접 읽어봐야 하는 설정값입니다.
Cognito는 이러한 일반적인 패턴의 한 사례일 뿐입니다. 올바른 코드를 작성하는 데 필요한 정보가 리포지토리(repo)와 클라우드 계정 사이에 분산되어 있으며, 어시스턴트는 그중 절반만 가지고 있습니다. Infrawise는 이 격차를 결정론적(deterministically)으로 메웁니다. 추출 경로에 LLM을 사용하지 않고, 오직 SDK 호출, AST 파싱, 그리고 MCP 도구가 읽을 수 있는 그래프를 생성하는 규칙 기반 분석기(rule-based analyzers)만을 사용합니다.
핵심 요약
- Cognito 오류는 사용자 풀 (User Pool) 단위가 아니라 앱 클라이언트 (App Client) 단위로 발생합니다. 동일한 코드가 같은 풀 내의 한 클라이언트에서는 성공하고 다른 클라이언트에서는 실패할 수 있습니다.
- 호스팅 UI (Hosted UI) 리다이렉트를 구축하기 전에
callbackUrls와oauthScopes를 확인하십시오. 등록되지 않은 URL은 권한 부여 (Authorize) 엔드포인트에서 거부됩니다. generatesSecret이true인 경우, 모든 인증 호출에는SECRET_HASH가 필요합니다. 비밀값의 존재를 모르는 어시스턴트는 이를 절대 생성하지 않을 것입니다.AccessTokenValidity는TokenValidityUnits없이는 의미가 없습니다. 단위에 따라 60은 1시간이 될 수도 있고 2개월이 될 수도 있습니다.infrawise.yaml에서cognito: enabled: true로 설정하고 (기본값은false임), 4가지cognito-idp읽기 작업 (Read actions)에 대한 권한을 부여하십시오.- 로그인, 회원가입 또는 토큰 갱신 (Refresh) 코드를 작성하기 전에
get_cognito_overview를 호출하십시오. 이 함수는 클라이언트 비밀값 (Client secret values)이나 사용자 데이터를 절대 반환하지 않습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기