ES /docs

Cognito get_user_by_access_token failed: Aws::STS::Errors::Http408Error, #<Cupix::Auth::VerifiedAcce

RCA: Cognito get_user_by_access_token failed: Aws::STS::Errors::Http408Error

Overview#

What Happened#

2026-05-22 14:44:44 UTC에 cupixworks-api production (us-west-2)에서 Cognito 인증 과정 중 AWS STS assume_role 호출이 HTTP 408 (Request Timeout)으로 실패했다. 유효한 access token을 가진 사용자 1명의 API 요청이 인증 실패(AUTH20009)로 거부되었다. 단발성 이벤트로, 동일 에러는 3일간(May 20-23) 다른 발생 기록이 없다.

Quick Facts#

Field Value
exception.class Aws::STS::Errors::Http408Error
exception.message Cognito get_user_by_access_token failed: Aws::STS::Errors::Http408Error
top_frame lib/cupix/auth/verification.rb:82
env production, us-west-2
deploy production-us-west-2-20260522T0559Z0-3e770a15-cupixworks

Timeline#

  1. 2026-05-22T14:44:44.663Z — STS assume_role HTTP 408 timeout 발생, 사용자 인증 실패
  2. 2026-05-22T14:44:44.663Z — Error-sweeper가 에러 수집
  3. 2026-05-23 — RCA 완료

Error Log#

Datadog Logs

text
Cognito get_user_by_access_token failed: Aws::STS::Errors::Http408Error, #<Cupix::Auth::VerifiedAccessToken:0x00007fab07e22d18 @access_token="eyJraWQiOiJieDJJNzFWVFAxZkI1elk1eUVyZFFVMkRpSUJ0V3RoUG81WllhQnE0QkpZPSIsImFsZyI6IlJTMjU2In0...", @decoded_access_token=[{"origin_jti"=>"9722bb88-193e-4af6-bbb3-055b05b0e378", "sub"=>"267c2b38-67ad-445c-910b-05461bba48b9", "token_use"=>"access", ...}], @issuer=Cupix::Auth::Issuers::Cognito>

Impact#

  • Service: cupixworks-api
  • 발생 횟수: 1
  • 최초 발생: 2026-05-22T14:44:44.663Z
  • 최근 발생: 2026-05-22T14:44:44.663Z
  • 영향 범위: 단일 사용자(sub: 267c2b38-67ad-445c-910b-05461bba48b9)의 1회 API 요청 실패. 후속 요청은 정상 처리된 것으로 추정 (추가 에러 없음).

Root Cause Summary#

AWS STS assume_role API 호출이 일시적인 네트워크/서비스 지연으로 HTTP 408 (Request Timeout) 응답을 반환했다. Cupix::Aws::Cognito.client 메서드에서 STS client 생성 시 명시적 timeout 설정이 없어 AWS SDK 기본값을 사용하며, STS 서비스 측의 일시적 지연이 timeout으로 이어졌다. 이 에러는 get_user_by_access_token 내부의 rescue 블록에서 NotAuthorizedException만 처리하고 STS 에러는 처리하지 않아, 상위의 verify_authenticated_request!까지 전파되어 사용자에게 AUTH20009 (인증 실패)로 응답되었다.

Technical Analysis#

Code Path#

  1. Entry point: lib/cupix/auth/verification.rb:81 — 인증된 요청 검증 시 Cognito에서 사용자 정보 조회
lib/cupix/auth/verification.rb:81-91ruby
          user_response = Cupix::Aws::Cognito.get_user_by_access_token(access_token: access_token, sub: sub)
        rescue StandardError => e
          Cupix::Logger.warn("Cognito get_user_by_access_token failed: #{e.message}, #{verified_access_token.inspect}",
                             class: 'Verification',
                             function: __method__,
                             module: 'Cupix::Auth',
                             error_type: e.class.name,
                             decoded_token_present: !verified_access_token.decoded_access_token.nil?)

          raise Cupix::Errors::Unauthorized.new(code: 'AUTH20009', reason: 'AccessToken has verified but user not found', access_token: verified_access_token.access_token, module: 'Cupix::Auth', class: 'Verification', function: __method__)
  1. Cognito client 생성: lib/cupix/aws/cognito.rb:9-27 — STS assume_role 호출 (timeout 미설정)
lib/cupix/aws/cognito.rb:9-27ruby
        def client(region: nil)
          region ||= Cupix::Tesla.region

          sts_client = ::Aws::STS::Client.new(region: region)
          assumed_role = sts_client.assume_role(
            role_arn: $AWS.fetch(:cognito).fetch(:role),
            role_session_name: 'CognitoSession'
          )
          credentials = assumed_role[:credentials]

          ::Aws::CognitoIdentityProvider::Client.new(
            region: region,
            credentials: ::Aws::Credentials.new(
              credentials[:access_key_id],
              credentials[:secret_access_key],
              credentials[:session_token]
            )
          )
        end
  1. get_user_by_access_token: lib/cupix/aws/cognito.rb:203-217 — NotAuthorizedException만 catch, STS 에러 미처리
lib/cupix/aws/cognito.rb:203-217ruby
        def get_user_by_access_token(access_token: nil, sub: nil)
          sub ||= ::JWT.decode(access_token, nil, false).first['sub']

          Rails.cache.fetch(user_cache_key(sub), expires_in: 1.hour) do
            Cupix::Logger.info("Fetching an user from Cognito: #{sub}", function: __method__, module: 'Cupix::Aws', class: 'Cognito')

            response = client.get_user(access_token: access_token)
          rescue ::Aws::CognitoIdentityProvider::Errors::NotAuthorizedException
            raise Cupix::Errors::Unauthorized.new(code: 'AUTH20013', reason: 'Invalid access token')
          else
            Cupix::Aws::Cognito::UserResponse.new(
              user_response_hash(response)
            )
          end
        end
  1. Failure point: lib/cupix/aws/cognito.rb:12-13::Aws::STS::Client.newassume_role 호출에서 HTTP 408 반환

기대 동작: STS assume_role이 성공하여 임시 자격 증명을 반환하고, 이를 사용해 Cognito get_user를 호출. 실제 동작: STS가 HTTP 408 timeout을 반환하여 Aws::STS::Errors::Http408Error 예외 발생. 이 예외는 get_user_by_access_token의 rescue에서 잡히지 않고(NotAuthorizedException만 catch), verify_authenticated_request!rescue StandardError에서 잡혀 AUTH20009로 변환됨.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-api status:error @environment:production "Aws::STS::Errors::Http408Error"
text
service:cupixworks-api status:error @environment:production "Cognito get_user_by_access_token failed"

핵심 로그 항목:

json
{
  "timestamp": "2026-05-22T14:44:44.663Z",
  "service": "cupixworks-api",
  "status": "error",
  "host": "ip-10-1-144-228.us-west-2.compute.internal",
  "module": "Cupix::Auth",
  "class": "Verification",
  "function": "verify_authenticated_request!",
  "error_type": "Aws::STS::Errors::Http408Error",
  "decoded_token_present": true,
  "message": "Cognito get_user_by_access_token failed: Aws::STS::Errors::Http408Error, #<Cupix::Auth::VerifiedAccessToken:...>"
}

추가 조사 결과:

  • 동일 기간(May 20-23) 내 다른 STS 에러: 0건
  • 동일 기간 내 다른 Http408Error: 0건
  • token은 유효 (iat: 13:51:27Z, exp: 14:51:27Z, 에러 시점 14:44:44Z — 만료 7분 전)
  • decoded_token_present: true — JWT 디코딩은 성공, STS 호출 단계에서 실패

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 AWS STS 서비스의 일시적 지연/장애로 assume_role이 timeout HTTP 408은 서버 측 request timeout; 단발 1회 발생; 동일 시간대 다른 STS 에러 없음 (광범위 장애 아님) Confirmed
H2 만료된 access token으로 인한 인증 실패 token exp=14:51:27Z, 에러 시점=14:44:44Z (7분 전에 유효); decoded_token_present: true Rejected
H3 네트워크 문제로 EC2 → STS 연결 실패 단일 호스트(ip-10-1-144-228)에서만 발생 HTTP 408은 연결 실패가 아닌 서버 측 timeout 응답; 연결 실패라면 Seahorse::Client::NetworkingError 발생 Rejected
H4 STS client에 timeout/retry 미설정으로 인한 취약성 ::Aws::STS::Client.new(region: region) — timeout, retry_limit 파라미터 없음; AWS SDK 기본 retry는 3회이나 408은 retryable 에러 목록에 포함되지 않을 수 있음 단발 이벤트이므로 직접적 원인은 아님 (기여 요인) Inconclusive

Fix Recommendation#

즉시 조치 (Critical)#

  • 불필요 — 단발성 일시적 STS timeout으로 시스템적 문제가 아님. 로그 레벨은 이미 warn으로 적절.

단기 개선 (1주 이내)#

  • lib/cupix/aws/cognito.rb:12 — STS client 생성 시 retry 및 timeout 설정 추가. ::Aws::STS::Client.new(region: region, retry_limit: 3, retry_mode: 'adaptive', http_open_timeout: 5, http_read_timeout: 5) 형태로 명시적 설정을 통해 일시적 STS 지연 시 자동 재시도.
  • lib/cupix/aws/cognito.rb:203-217get_user_by_access_token에서 STS/네트워크 관련 예외(Aws::STS::Errors::ServiceError)도 catch하여 retry하거나 더 명확한 에러 메시지 반환.

장기 개선 (재발 방지)#

  • STS assume_role 결과를 캐싱하여 매 요청마다 STS를 호출하지 않도록 개선. 현재는 get_user_by_access_token이 호출될 때마다(캐시 미스 시) 새로운 STS client를 생성하고 assume_role을 호출한다. 임시 자격 증명의 유효 기간(기본 1시간) 동안 캐싱하면 STS 의존성을 크게 줄일 수 있음.
  • Circuit breaker 패턴 적용: STS 연속 실패 시 캐시된 자격 증명으로 fallback.

Monitoring#

  • STS timeout/에러 추적을 위한 Datadog 쿼리:
text
service:cupixworks-api @error_type:"Aws::STS::Errors::*" @environment:production
  • assume_role 호출 지연 메트릭 추가 (p99 latency 모니터링)
  • AUTH20009 에러 빈도가 임계치를 초과할 경우 알림 설정

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial
  • 단발성 AWS STS timeout으로 시스템적 영향 없음. 사용자는 다음 요청에서 정상 인증됨. 단기 개선(retry 설정)으로 향후 동일 문제 자동 복구 가능.