Spring Security 7에서 OIDC UserInfo 엔드포인트 이해: ID 토큰과 사용자 정보 조회

OIDC UserInfo 엔드포인트의 역할과 구현 메커니즘

1. UserInfo 엔드포인트의 필요성

OAuth2 인증 흐름에서 클라이언트는 두 가지 토큰을 수신합니다:
토큰 종류용도주요 특징
Access Token리소스 서버 접근용사용자 식별자만 포함
ID Token신원 확인용JWT 형식의 사용자 정보 포함
OIDC 표준에 정의된 /userinfo 엔드포인트는 다음과 같은 핵심 역할을 수행합니다:
  • 실시간 사용자 정보 제공
  • 클라이언트의 스코프 기반 데이터 필터링
  • 단기 유효 ID 토큰의 보완

2. ID 토큰과 UserInfo 엔드포인트 비교

┌──────────────────────┬────────────────────────┐
│     ID Token 특성    │   UserInfo 엔드포인트 특성   │
├──────────────────────┼────────────────────────┤
│ 단회성, 짧은 유효기간  │ 실시간 정보 제공 가능       │
│ 정적 클레임 정보      │ 동적 사용자 상태 반영       │
│ 민감 정보 포함 가능성  │ 스코프 기반 필터링 지원     │
└──────────────────────┴────────────────────────┘

3. Spring Security 구현 분석

3.1 엔드포인트 처리 흐름

  1. Bearer 토큰 포함한 GET /userinfo 요청 수신
  2. OidcUserInfoEndpointFilter의 토큰 검증
  3. Redis를 통한 인증 정보 검색:
    • 액세스 토큰 → 인증 ID 매핑 조회
    • 인증 ID → OAuth2Authorization 객체 복원
  4. ID 토큰 클레임 추출 및 스코프 기반 필터링
  5. JSON 응답 생성 및 반환

3.2 주요 코드 구조


// 커스텀 토큰 생성기 구현
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> customJwtTokenCustomizer() {
    return context -> {
        if ("id_token".equals(context.getTokenType().getValue())) {
            Authentication auth = context.getPrincipal();
            if (auth.getPrincipal() instanceof CustomUser user) {
                Map<String, Object> claims = context.getClaims().build();
                
                // 필수 클레임 설정
                claims.put("sub", user.getId());
                claims.put("name", user.getDisplayName());
                
                // 스코프 기반 선택적 클레임 추가
                Set<String> scopes = context.getAuthorizedScopes();
                if (scopes.contains("email")) {
                    claims.put("email", user.getEmail());
                    claims.put("email_verified", true);
                }
            }
        }
    };
}

4. 흔한 문제와 해결 방법

4.1 클레임 누락 문제

원인: ID 토큰의 필수 클레임(sub) 삭제

해결: 엑세스 토큰 커스터마이징 시 OIDC 필수 클레임 보존

4.2 사용자 정보 확장

스코프 기반 클레임 동적 추가 예시:


private void addScopedClaims(Map<String, Object> claims, CustomUser user, Set<String> scopes) {
    if (scopes.contains("profile")) {
        claims.put("given_name", user.getFirstName());
        claims.put("family_name", user.getLastName());
    }
    if (scopes.contains("phone")) {
        claims.put("phone_number", user.getMobile());
    }
}

5. 최적의 사용 사례

사용 시나리오권장 방식
모바일 앱ID 토큰만 사용
실시간 데이터 요구 시스템UserInfo 엔드포인트 우선
민감 정보 관리UserInfo 엔드포인트 + 스코프 제어

태그: Spring Security oauth2 oidc UserInfo Endpoint ID Token

10월 4일 17:29에 게시됨