JWT 가이드

JWT audience mismatch를 제가 추적하는 방법

JWT aud claim이 API의 expected audience와 맞지 않을 때 ID token과 access token, 단일/배열 aud, 환경 설정, azp까지 확인하는 실무 디버깅 흐름입니다.

작성: JWTDecoder.tools마지막 확인 2026-09-13

편집 문체 안내: 1인칭 표현은 JWTDecoder.tools의 문서화된 검증 워크플로를 뜻하며, 게시자와 조직 저자는 JWTDecoder.tools입니다.

token aud ∋ expected aud
token aud ∋ expected aud

audience mismatch가 발생하면 저는 먼저 token을 다시 발급받기보다 이 token이 누구를 위해 발급됐는지부터 확인합니다. JWT의 aud claim은 resource server가 자기 자신을 이 token의 intended audience로 인식할 수 있는지 판단하는 핵심 값입니다.

JWT Decoder에서 payload를 열어 aud를 확인하고, API 설정의 expected audience와 정확히 비교합니다.

aud claim이 의미하는 것

RFC 7519에서 aud는 JWT를 받아들이도록 의도된 대상(principal)을 나타냅니다. 값은 하나의 문자열일 수도 있고 배열일 수도 있습니다.

{ "aud": "https://api.example.com" }

또는:

{
  "aud": ["https://api.example.com", "secondary-service"]
}

저는 verifier가 예상하는 값이 배열 안에 실제로 존재하는지 확인합니다. 단순 문자열 비교만 구현하면 multi-audience token을 잘못 거부할 수 있습니다.

제가 가장 먼저 확인하는 네 가지

  1. 현재 요청에 access token을 보내고 있는가?
  2. token의 aud에 현재 API가 포함되어 있는가?
  3. API의 expected audience 설정이 현재 환경과 맞는가?
  4. issuer가 이 API용 token을 실제로 발급하도록 설정되어 있는가?

이 네 가지 중 하나가 어긋나는 경우가 대부분입니다.

ID token과 access token을 혼동하지 않습니다

브라우저 login 이후 두 token이 모두 JWT처럼 보일 수 있습니다. ID token의 aud가 OAuth/OIDC client ID인 경우가 많기 때문에 이를 API에 보내면 resource server의 expected audience와 맞지 않을 수 있습니다.

예:

{
  "aud": "my-web-client-id"
}

API가 기대하는 값이:

https://api.example.com

이면 signature가 정상이어도 audience validation은 실패해야 합니다.

저는 JWT라는 형식만 보고 token 용도를 결정하지 않습니다. provider 문서와 발급 flow를 확인합니다.

환경 mismatch를 찾습니다

local/staging/production에서 audience가 다르게 설정된 경우가 흔합니다.

local      → api://local
staging    → api://staging
production → api://production

staging token을 production API에 보내면 token이 cryptographically valid해도 audience mismatch가 정상적인 결과입니다.

이때 저는 frontend 환경 변수와 backend verifier 설정을 둘 다 비교합니다. 한쪽만 고치면 같은 문제가 다시 생깁니다.

문자열 normalize를 함부로 하지 않습니다

다음 두 값은 사람이 보기에는 비슷하지만 verifier 관점에서는 다를 수 있습니다.

https://api.example.com
https://api.example.com/

issuer와 audience를 임의로 lower-case 처리하거나 slash를 제거해 맞추기보다, identity provider가 발급하는 canonical value와 API 설정을 정확히 맞춥니다.

aud 배열은 어떻게 검증하는가

제가 기대하는 방식은 대략 다음과 같습니다.

function audienceMatches(aud, expected) {
  if (typeof aud === "string") return aud === expected;
  if (Array.isArray(aud)) return aud.includes(expected);
  return false;
}

실제 production에서는 직접 구현하기보다 검증 라이브러리의 audience 옵션을 사용합니다. 중요한 것은 “token에 어떤 aud든 하나 있으면 통과”가 아니라 내 expected audience가 포함되어야 한다는 점입니다.

azp도 확인할 때가 있습니다

OIDC ID Token처럼 여러 audience가 존재하는 특정 context에서는 azp(authorized party)가 의미를 가질 수 있습니다. 저는 azp를 보편적인 aud 대체물처럼 사용하지 않고, 해당 프로토콜과 provider의 validation 규칙에 따라 확인합니다.

즉 aud mismatch가 났다고 무조건 azp를 대신 비교하지 않습니다.

Signature와 audience는 서로 다른 검사입니다

제가 가장 중요하게 분리하는 부분입니다.

signature verified
≠
audience accepted

신뢰하는 issuer가 실제로 서명한 token이어도 다른 API를 위해 발급됐을 수 있습니다. 그래서 signature 검증을 통과한 뒤에도 audience validation이 필요합니다.

JWKS Validator에서 expected audience를 지정하면 signature와 claim policy를 함께 확인할 수 있습니다.

audience mismatch가 401로 보일 때

많은 API가 audience mismatch를 401 Unauthorized로 표현합니다. 이때 JWT 401 Unauthorized 가이드의 전체 순서를 적용합니다.

제가 확인하는 순서:

Authorization header
→ exp/nbf
→ signature
→ issuer
→ audience
→ scope/role

이렇게 해야 audience 하나에만 집착해서 실제 signature 또는 expiration 문제를 놓치지 않습니다.

제가 하지 않는 해결책

Audience validation을 끈다

가장 빠르게 “해결”되는 것처럼 보이지만 다른 resource용 token이 내 API에서 받아들여질 수 있습니다.

aud가 없으면 그냥 통과한다

API가 audience를 요구하는 설계라면 missing audience도 실패로 처리합니다.

모든 환경 audience를 한 verifier에 무조건 허용한다

production endpoint에서 staging/local audience까지 허용하면 환경 경계가 약해집니다.

token payload의 임의 claim을 audience 대신 사용한다

provider와 API 계약에서 정의하지 않은 claim으로 trust policy를 우회하지 않습니다.

재현 테스트를 만들 때

저는 최소 네 개 fixture를 준비합니다.

1. correct aud → pass
2. wrong aud → fail
3. aud array containing expected → pass
4. missing aud → policy에 따라 fail

그리고 signature와 issuer는 동일하게 유지합니다. 그래야 audience 정책만 독립적으로 테스트할 수 있습니다.

제 최종 체크리스트

  1. access token인지 확인합니다.
  2. aud의 실제 타입이 string인지 array인지 봅니다.
  3. API expected audience를 확인합니다.
  4. 두 값을 exact match 규칙으로 비교합니다.
  5. staging/production 설정이 뒤섞이지 않았는지 봅니다.
  6. signature와 issuer validation도 별도로 통과했는지 확인합니다.
  7. OIDC context에서 필요한 경우에만 azp 규칙을 적용합니다.
  8. audience validation을 끄는 것으로 문제를 숨기지 않습니다.

제가 audience mismatch를 볼 때 핵심 질문은 하나입니다. “이 token은 정말 지금 요청을 받는 이 API를 위해 발급됐는가?” 그 답이 명확해야 signature가 정상인 token도 안전하게 받아들일 수 있습니다.

출처 및 참고자료

관련 가이드