Files
project-auth-server/docs/topics/03-keycloak/05-adr-keycloak-user-auto-registration.md

3.6 KiB

ADR-005: Keycloak 인증 사용자 내부 자동 등록

Status

Accepted

Date

2026-05-12

Context

Keycloak 이 로그인과 계정 lifecycle 을 소유하면 auth-server 는 검증된 access token 으로 내부 사용자 레코드를 식별해야 합니다. 기존 결정은 내부 사용자가 없으면 AUTH-004 404 를 반환하는 방식이었지만, 이 방식은 Keycloak self-service registration 과 auth-server 의 내부 사용자 테이블을 별도 운영 절차로 동기화해야 했습니다.

자동 등록을 도입하더라도 email 을 계정 연결 키로 쓰면 안 됩니다. email 은 변경되거나 재사용될 수 있으므로 내부 권한 식별자는 계속 Keycloak sub(provider=KEYCLOAK, provider_subject=sub) 조합이어야 합니다.

Decision Drivers

  • Keycloak self-service registration 이후 첫 API 호출에서 내부 사용자 레코드를 준비한다.
  • 인증 주체와 token 발급 책임은 계속 Keycloak 에 둔다.
  • email 은 충돌 검사에만 사용하고 내부 계정 연결 키로 사용하지 않는다.
  • 내부 사용자 생성은 application use case 의 트랜잭션 경계 안에서 수행한다.
  • Resource Server 부팅은 Keycloak discovery endpoint 가 떠 있어야만 성공하는 구조를 피한다.

Considered Options

Option 1: 내부 사용자가 없으면 계속 404 반환

  • 장점: GET /api/v1/auth/me 가 순수 조회로 남고 쓰기 부작용이 없다.
  • 단점: Keycloak 사용자와 내부 사용자 테이블을 별도 배치나 운영 절차로 맞춰야 한다.
  • 트레이드오프: HTTP 의미는 단순하지만 운영 동기화 비용이 생긴다.

Option 2: 첫 인증 요청에서 내부 사용자 자동 등록

  • 장점: Keycloak 에서 계정을 만든 사용자가 첫 API 호출 시 바로 내부 사용자 레코드를 얻는다.
  • 단점: GET /api/v1/auth/me 가 missing user 경로에서 쓰기를 수행한다.
  • 트레이드오프: 사용자 온보딩은 단순해지지만 transaction, 충돌 처리, 감사 이벤트가 필요하다.

Option 3: Keycloak Admin/Event API 로 사전 동기화

  • 장점: API 요청 경로의 쓰기 부작용을 줄일 수 있다.
  • 단점: 외부 API 호출, retry, idempotency, 실패 보상 흐름이 필요하다.
  • 트레이드오프: 운영 복잡도가 커지고 Keycloak 이벤트 전달 신뢰성에 의존한다.

Decision

Option 2 를 채택합니다. GET /api/v1/auth/me 는 검증된 Keycloak claim 의 sub 로 내부 사용자를 조회하고, 없으면 email 충돌을 먼저 검사한 뒤 내부 사용자를 자동 등록합니다. 동일 email 이 이미 다른 subject 에 연결되어 있으면 자동 연결하지 않고 AUTH-005 409 를 반환합니다.

Consequences

긍정적 결과

  • Keycloak self-service registration 과 내부 사용자 생성이 첫 API 호출에서 자연스럽게 이어진다.
  • 내부 사용자 식별 기준은 여전히 immutable sub 이며, email 기반 자동 연결은 금지된다.
  • 자동 등록 성공은 KEYCLOAK_USER_AUTO_REGISTERED 감사 이벤트로 남는다.

부정적 결과

  • GET /api/v1/auth/me 는 missing user 경로에서 DB write 를 수행한다.
  • 동시 첫 요청에서는 DB unique 제약과 충돌 처리 정책을 함께 고려해야 한다.
  • 운영 환경은 APP_SECURITY_KEYCLOAK_ISSUER_URI 를 제공해야 한다.

위험 완화

  • application use case 에 transaction boundary 를 둔다.
  • 같은 email 이 이미 존재하면 AUTH-005 409 로 중단하고 새 subject 에 자동 연결하지 않는다.
  • (provider, provider_subject) unique 제약으로 subject 중복 생성을 방지한다.