Cognito get_user_by_access_token failed: Access token decoded but payload is empty, #<Cupix::Auth::V
RCA: Cognito get_user_by_access_token failed: Access token decoded but payload is empty
Error Log#
Cognito get_user_by_access_token failed: Access token decoded but payload is empty, #<Cupix::Auth::VerifiedAccessToken:0x00007fcb7c6e7ea0 @access_token="eyJhbGciOiJIUzI1NiJ9.eyJ0b2tlbiI6Im5jcXBkdjdnNzAzbiIsImlzcyI6Imh0dHBzOi8vZmxpbnRjby5jdXBpeC53b3JrcyIsImlhdCI6MTc3NTU4OTE0MSwiZXhwIjoxNzc1NTkyNzQxLCJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwic2NvcGUiOm51bGx9.t7KPbjgXLs0bshRI7-HBgDdmigBmfUU1Yna4w6eNKUg", @decoded_access_token=nil, @issuer=nil>
Impact#
- Service:
cupixworks-api - 발생 횟수: 1 (이 클러스터), 48시간 내 동일 패턴 4건
- 최초 발생: 2026-04-07T19:12:33.575Z
- 최근 발생: 2026-04-07T19:12:33.575Z
Root Cause Summary#
Cross-region 인증 요청에서 JWT issuer 불일치로 인해 토큰 검증이 실패합니다. 토큰은 US 서버($APP_HOST=cupix.works)에서 발급되어 issuer가 https://flintco.cupix.works로 설정되었으나, EU 서버($APP_HOST=cupix-eu.works)에서 인증을 시도할 때 Cupixworks.match? regex가 cupix-eu.works 패턴만 허용하므로 매칭에 실패합니다. Cognito issuer 패턴도 매칭되지 않아, verify_jwt_access_token!이 decoded_access_token=nil, issuer=nil인 VerifiedAccessToken을 반환하고, 이후 "Access token decoded but payload is empty" 에러가 발생합니다.
Technical Analysis#
Code Path#
- Entry point:
lib/cupix/auth/verification.rb:20—verify_authenticated_request! - JWT 유효성 체크:
lib/cupix/auth/verification.rb:45—Cupix::Auth::Jwt.valid_jwt?호출로 JWT 포맷 확인 - 핵심 검증:
lib/cupix/auth/verification.rb:55—verify_jwt_access_token!호출
# lib/cupix/auth/verification.rb:208-234
def self.verify_jwt_access_token!(access_token: nil)
jwt = JWT.decode(access_token, nil, false)
iss = jwt[0]['iss'] # => "https://flintco.cupix.works"
# ...
registered_issuers = [Cupix::Auth::Issuers::Cognito, Cupix::Auth::Issuers::Cupixworks]
decoded_token = nil
_issuer = nil
registered_issuers.each do |issuer|
next unless issuer.match?(iss) # 두 issuer 모두 매칭 실패 → skip
decoded_token = issuer.verify!(access_token)
_issuer = issuer
end
# decoded_token=nil, _issuer=nil 상태로 반환
::Cupix::Auth::VerifiedAccessToken.new(access_token: access_token, decoded_access_token: decoded_token, issuer: _issuer)
end
- Issuer 매칭 실패 원인 —
Cupixworks.match?:
# lib/cupix/auth/issuers/cupixworks.rb:94-101
def match?(issuer)
protocol = Rails.env.development? ? 'https?' : 'https'
regex_pattern = "^#{protocol}://[a-z0-9-]+\.#{Regexp.escape($APP_HOST)}$"
regex = Regexp.new(regex_pattern, Regexp::IGNORECASE)
!!issuer.match(regex)
end
# EU 서버에서 $APP_HOST='cupix-eu.works' → regex: ^https://[a-z0-9-]+\.cupix\-eu\.works$
# issuer 'https://flintco.cupix.works'는 이 패턴에 매칭 안 됨
Cognito.match?도 당연히 실패:
# lib/cupix/auth/issuers/cognito.rb:38-41
def match?(issuer)
regex = %r{^https://cognito-idp\.[a-z0-9-]+\.amazonaws\.com/[a-z0-9-_]+$}i
!!issuer.match(regex)
end
# 'https://flintco.cupix.works'는 Cognito 패턴 아님
- Failure point:
lib/cupix/auth/verification.rb:63-68—decoded_access_token.nil?체크에서 에러 발생
# lib/cupix/auth/verification.rb:61-69
begin
if verified_access_token.decoded_access_token.nil?
raise Cupix::Errors::Unauthorized.new(
code: 'AUTH20008',
reason: 'Access token decoded but payload is empty',
access_token: verified_access_token.access_token
)
end
- 에러가 rescue에서 잡혀 로깅됨:
lib/cupix/auth/verification.rb:82-88
# lib/cupix/auth/verification.rb:82-88
rescue StandardError => e
Cupix::Logger.error("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?)
- 토큰 발급 시 issuer 결정 로직 —
$APP_HOST사용:
# app/models/concerns/properties/team.rb:21-23
def app_url
"#{$APP_PROTOCOL}://#{self.domain}.#{$APP_HOST}"
end
# lib/cupix/auth/jwt.rb:23-36
def issuer(any_entity)
Rails.cache.fetch("cupix:auth:jwt:issuer:#{any_entity.class.name}:#{any_entity.id}", expires_in: 1.week) do
case any_entity.class.name
when 'Team'
any_entity.app_url # => "https://flintco.cupix.works" (US 서버에서)
# ...
end
end
end
기대 동작: JWT의 iss 클레임이 인증 서버의 $APP_HOST와 매칭되어 Cupixworks issuer가 토큰을 검증하고, signin! 흐름으로 진행.
실제 동작: Cross-region 요청에서 토큰의 issuer 도메인(cupix.works)과 서버의 $APP_HOST(cupix-eu.works)가 불일치하여, 어떤 registered issuer도 매칭되지 않고, VerifiedAccessToken이 nil 값으로 반환됨.
Log Evidence#
48시간 범위에서 동일 패턴 4건 확인 — 모두 cross-region 인증 시도:
Datadog query: service:cupixworks-api "get_user_by_access_token"
Time range: 2026-04-06T00:00:00Z to 2026-04-08T00:00:00Z
| Timestamp (UTC) | Host Region | JWT Issuer (tenant domain) | Token |
|---|---|---|---|
| 2026-04-07T19:12:33.575Z | eu-central-1 | https://flintco.cupix.works |
ncqpdv7g703n |
| 2026-04-06T05:49:08.305Z | eu-central-1 | https://tokiomarinehd.cupix-jp.works |
txh82brvmsoe |
| 2026-04-06T05:43:11.461Z | ap-northeast-1 | https://byuk.cupix-eu.works |
v1e4tai27eeu |
| 2026-04-06T05:42:25.823Z | eu-central-1 | https://structon.cupix-sg.works |
obfgsbdcper5 |
모든 로그의 공통 속성:
{
"class": "Verification",
"function": "verify_authenticated_request!",
"module": "Cupix::Auth",
"error_type": "Cupix::Errors::Unauthorized",
"decoded_token_present": false
}
패턴 분석:
flintco.cupix.works(US 도메인) → EU 서버에서 실패 ($APP_HOST=cupix-eu.works)tokiomarinehd.cupix-jp.works(JP 도메인) → EU 서버에서 실패byuk.cupix-eu.works(EU 도메인) → JP 서버에서 실패 ($APP_HOST=cupix-jp.works)structon.cupix-sg.works(SG 도메인) → EU 서버에서 실패
모든 실패는 토큰 발급 region과 인증 시도 region이 다른 경우에 발생합니다.
$APP_HOST 설정 (config/environments/production.rb:138):
$APP_HOST = ENV['APP_HOST'] || 'cupix.works'
region별 배포에서 APP_HOST 환경 변수가 각각 다르게 설정됨:
- US:
cupix.works - EU:
cupix-eu.works - JP:
cupix-jp.works - SG:
cupix-sg.works
Fix Recommendation#
즉시 조치 (Critical)#
Cupixworks.match? 메서드(lib/cupix/auth/issuers/cupixworks.rb:94-101)를 수정하여 모든 region의 도메인을 인식하도록 변경해야 합니다.
현재 $APP_HOST 하나만 사용하여 매칭하지만, 모든 유효한 Cupix 도메인(cupix.works, cupix-eu.works, cupix-jp.works, cupix-sg.works, cupix-ca.works)을 허용하는 allowlist 기반 매칭이 필요합니다.
수정 대상: lib/cupix/auth/issuers/cupixworks.rb:94-101
단기 개선 (1주 이내)#
verify_jwt_access_token!(lib/cupix/auth/verification.rb:221-230)에서 어떤 issuer도 매칭되지 않았을 때 명시적으로 에러를 발생시켜야 합니다. 현재는nil값이 silent하게 반환되어 에러 메시지가 "payload is empty"로 표시되므로, 실제 원인(issuer 불일치)을 숨기게 됩니다.- 에러 로그 메시지(
lib/cupix/auth/verification.rb:83)도 "Cognito get_user_by_access_token failed"가 아닌 실제 실패 원인을 반영하도록 개선 필요 — Cognito와 무관한 토큰에 대해서도 이 메시지가 출력되어 혼동을 줍니다.
장기 개선 (재발 방지)#
- Multi-region 토큰 검증 아키텍처 재설계: 토큰의
iss클레임을 기반으로 region 판별 후 적절한 secret으로 서명 검증하는 방식으로 전환 - Region-aware issuer registry를 도입하여 모든 유효한 Cupix 도메인을 중앙에서 관리
- JWT issuer 캐시(
cupix:auth:jwt:issuer:*)가 region 이동 시에도 올바르게 동작하는지 검증
Monitoring#
- Cross-region 인증 실패를 추적하는 메트릭 추가:
service:cupixworks-api "Access token decoded but payload is empty" | count by @host
- Issuer 불일치 발생 시 별도 경고 알림 설정:
service:cupixworks-api "get_user_by_access_token failed" @decoded_token_present:false | count > 5 in 1h
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard —
Cupixworks.match?수정은 비교적 단순하나, 보안 영향(유효하지 않은 도메인 허용 가능성)을 신중히 검토해야 합니다. Cross-region 인증이 의도된 동작인지 비즈니스 확인이 필요합니다.