Files
project-auth-server/docs/topics/03-keycloak/03-claim-role-design.md
T

3.7 KiB

Keycloak Claim / Role 설계

Why

Resource Server는 token을 검증한 뒤 어떤 claim을 내부 principal로 신뢰할지 정해야 합니다. 모든 claim을 그대로 비즈니스 계층에 넘기면 Keycloak 계약 변경이 내부 로직 전체로 번집니다.

What

auth-server가 사용하는 최소 claim은 다음입니다.

Claim 용도 내부 모델
sub Keycloak 사용자 고정 식별자 provider_subject
email 내부 사용자 신규 생성과 충돌 검사 UserEmail
name 내부 사용자 표시 이름 UserName
preferred_username name이 없을 때 fallback AuthenticatedUser.name
scope OAuth2 scope authority SCOPE_*
realm_access.roles Realm role authority ROLE_*

Sample JWT payload

Keycloak realm platform 이 발급하는 access token 의 payload 는 다음 형태입니다 (값은 예시).

{
  "iss": "https://keycloak.dev.example.com/realms/platform",
  "sub": "1f7a3b2e-9c4d-4f81-a0e7-2b8f5c1d6a4b",
  "aud": "auth-server-ingress",
  "exp": 1735689600,
  "iat": 1735686000,
  "azp": "auth-server-ingress",
  "scope": "openid email profile",
  "email": "alice@example.com",
  "email_verified": true,
  "name": "Alice Kim",
  "preferred_username": "alice",
  "realm_access": {
    "roles": ["user"]
  },
  "resource_access": {
    "auth-server-ingress": { "roles": [] }
  }
}

sub 는 Keycloak 이 사용자에게 부여하는 immutable UUID 로, 내부 DB 의 provider_subject 와 1:1 로 묶입니다. realm_access.roles 는 위 표의 ROLE_* 매핑 대상입니다.

How

KeycloakJwtAuthenticationConverter가 JWT claim을 AuthenticatedUser로 변환합니다. controller는 @CurrentUser로 이 principal을 받고, application에는 KeycloakUserClaims(subject, email, name)만 전달합니다. KeycloakUserClaimsValidator는 claim이 비어 있거나 UserEmail/UserName 도메인 규칙에 어긋나면 InvalidKeycloakClaimsException(AUTH-003, HTTP 400)으로 번역해 Keycloak 오염 클레임이 비즈니스 로직까지 흘러들지 않게 차단합니다.

권장 realm role:

Keycloak role Spring authority 용도
user ROLE_user 일반 인증 사용자
admin ROLE_admin 운영/관리 API

역할 이름은 Keycloak realm에서 소유합니다. auth-server는 role 값을 새로 발급하거나 DB 값으로 권한을 덮어쓰지 않습니다. 현재 /api/v1/auth/meGET 요청에 대해 ROLE_user가 필요합니다. 따라서 Keycloak realm의 self-service registration 기본 역할에는 user를 포함해야 하며, 역할이 없는 token은 인증은 성공하더라도 AUTH-002로 거절됩니다.

Result / Trade-offs

  • token 검증 결과는 신뢰하되, 내부 사용자 식별은 (provider=KEYCLOAK, provider_subject=sub) 기준으로만 수행합니다.
  • email은 자동 계정 연결 키로 쓰지 않습니다. 연결된 내부 사용자가 없으면 email 충돌 검사 후 새 내부 사용자를 만들고, 같은 email 이 이미 다른 subject 에 연결되어 있으면 AUTH-005 409를 반환합니다.
  • 실무 권장안은 “IdP subject를 immutable external identity로 삼고, email은 표시/연락/초기 등록 보조값으로만 다루는 것”입니다. email은 변경될 수 있고 재사용될 수 있으므로 권한 있는 내부 계정 연결 키로 쓰면 위험합니다.
  • 표시용 email/name은 token claim에서 왔더라도 DB에 저장된 값이 authoritative입니다. 따라서 /api/v1/auth/me 응답은 DB 값을 노출하고, 권한(authorities)은 token claim에서 그대로 가져옵니다.