68 lines
3.6 KiB
Markdown
68 lines
3.6 KiB
Markdown
# 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 중복 생성을 방지한다.
|