47 lines
3.2 KiB
Markdown
47 lines
3.2 KiB
Markdown
---
|
|
title: interview-prep / jwt-resource-server-fine-grained-error-classification
|
|
source_type: interview-prep
|
|
status: raw
|
|
related_branches: [feature-security-operational-baseline]
|
|
related_projects: [ca-skeleton]
|
|
tags: [interview-prep, ca-skeleton, security, jwt, spring-security, error-handling]
|
|
created: 2026-06-08
|
|
status_label: collecting
|
|
---
|
|
|
|
# interview-prep: jwt-resource-server-fine-grained-error-classification
|
|
|
|
> Layer: `raw/interviews/` — 면접 질문 원본 수집. 다듬은 답변은 `/interviewize` 후 `wiki/interview/`.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-security-operational-baseline]] — JWT Resource Server 인증/인가 실패의 fine-grained 운영 분류 구현.
|
|
|
|
## 질문 / Question
|
|
|
|
- 질문 원문: JWT 인증 실패를 401 하나로 뭉개지 않고, 운영자가 missing/expired/signature/issuer/audience/unknown-kid 를 구분할 수 있게 어떻게 구현했나요? 클라이언트에는 무엇을 노출했나요?
|
|
- 출처: 예상 질문 (실제 면접 아님).
|
|
|
|
## 질문 의도 추론 / Why this question
|
|
|
|
- 핵심 평가 대상:
|
|
- Spring Security resource server 의 **실패 처리 위치** 이해 — bearer 토큰 검증 실패는 `BearerTokenAuthenticationFilter`/`ExceptionTranslationFilter` 가 `AuthenticationEntryPoint` 로 보내며 `@RestControllerAdvice` 에 **도달하지 않는다**. 그래서 fine-grained 분류는 EntryPoint/AccessDeniedHandler 에 있어야 한다.
|
|
- 보안 응답의 **과노출 방지** — 클라이언트엔 generic message(`Authentication failed`)만, 내부엔 분류 code. issuer/audience/token 값을 응답·로그에 흘리지 않기.
|
|
- 표준 정합 — 401 은 `WWW-Authenticate` MUST(RFC 9110 §15.5.2), transient(kid/jwks)엔 `Retry-After`.
|
|
- 함정:
|
|
- "@RestControllerAdvice 에서 `AuthenticationException` 잡으면 된다" — filter-layer 실패는 거기 안 온다.
|
|
- exception → code 매핑을 message 문자열 heuristic 에 의존하는 것의 fragility 를 인정 안 함.
|
|
- clock skew 를 default 에 맡기고 "Spring 이 알아서" — 버전 업 시 silent drift.
|
|
- 후속 질문:
|
|
- `JwtValidationException` 과 `BadJwtException` 의 차이, 각각 어떤 실패인가?
|
|
- unknown kid 를 왜 retryable=true + Retry-After 로 두나? (rotation 중 JWKS refresh 로 해소)
|
|
- clock skew 60s 를 명시 설정한 이유? (default 의존 시 drift)
|
|
- 다중 audience/validator 동시 실패 시 어떤 code 를 우선하나?
|
|
|
|
## 답변 재료 / Raw answer material
|
|
|
|
- 구현: `SecurityErrorClassifier`(exception graph + validator/Nimbus message heuristic, 우선순위 expired>issuer>audience), `EnvelopeAuthenticationEntryPoint`/`EnvelopeAccessDeniedHandler`(공통 `AuthErrorResponseWriter` → Envelope JSON), `JwtDecoderConfig`(`SupplierJwtDecoder` 로 lazy 60s clock skew + issuer + audience validator).
|
|
- 12 code 는 `docs/registries/error-codes.yaml` SSOT 와 `OperationalError` enum 일치(status/category/retryable).
|
|
- redaction: 응답 body 는 generic message, 로그는 code/category/method/path 만 — token(`eyJ...`) 미노출. contract test 로 강제.
|
|
- 한계(솔직): message 문자열 heuristic 은 Spring/Nimbus 버전 메시지 변경에 취약 → unmapped 는 generic 401 fallback(절대 500 아님). 실 IdP 통합 테스트는 미수행(`prod-verified` 아님).
|