1
0
forked from baron/baron-sso

ory-hosting 기본구동

This commit is contained in:
Lectom C Han
2026-01-27 22:58:49 +09:00
parent 41f0549435
commit c3f7b18afc
31 changed files with 1910 additions and 176 deletions

View File

@@ -0,0 +1,48 @@
+# CompletePasswordReset 리팩터 전략 (IDP 추상화 전환)
2 +
3 +## 현 상황
4 +- `AuthHandler.CompletePasswordReset`가 Descope 전용 흐름을 포함:
5 + - Descope 비밀번호 정책 검사
6 + - Descope Management API `SetActivePassword` 호출
7 + - IDP Provider 추상화는 마지막에만 호출
8 +- 목표: IDP Provider 기반으로 통일하고, Descope 정책 검사는 Descope 선택 시에만 적용.
9 +
10 +## 단계별 패치 전략 (쿼터/앵커 분할)
11 +1) **전역 준비**: 함수 시작부에 `providerName := "unknown"; if h.IdpProvider != nil
{ providerName = h.I
dpProvider.Name() }` 이미 추가됨.
12 +2) **비밀번호 정책 블록 분리**
13 + - 현재 Descope 정책 블록을 그대로 두고, 먼저 작은 단위로 변환:
14 + - 블록 상단 주석을 `// Validate password complexity (Descope only)`로 교체.
15 + - 블록 전체를 `if providerName == "Descope" && h.DescopeClient != nil { ... }`로 감싸기.
16 + - 특수문자 `[\W_]` 등 이스케이프가 많으므로, 치환 시 작은 범위의 문장/줄 단위로 교체.
17 +3) **Descope SetActivePassword 제거 및 공통 UpdateUserPassword 호출**
18 + - `Attempting to update password via Descope Auth API` 주석부터 Descope 전용 호출을 삭제.
19 + - 그 위치에 공통 경로 추가:
20 + ```go
21 + ale.Log(..., slog.String("idp", providerName))
22 + if h.IdpProvider == nil { ... 500 ... }
23 + if err := h.IdpProvider.UpdateUserPassword(...); err != nil { ... 500 ... }
24 + ```
25 +4) **앵커 선택**
26 + - 삭제 앵커: `"Attempting to update password via Descope Auth API"` 줄과 그 아래 Descope 클
라이언트 nil 체
크 ~ SetActivePassword 오류 처리 블록.
27 + - 유지 앵커: 상단의 Redis 토큰 삭제/성공 응답 부분은 그대로.
28 +5) **검증**
29 + - `gofmt -w backend/internal/handler/auth_handler.go`
30 + - `rg "Descope Client is nil" backend/internal/handler/auth_handler.go` → 없어야 함.
31 + - `rg "UpdateUserPassword" backend/internal/handler/auth_handler.go` → 공통 경로만 남는지
확인.
32 +
33 +## 테스트 계획
34 +- 단위 테스트: IDP Provider를 모킹해 `CompletePasswordReset`를 직접 호출하는 테스트 추가 권장
(추후 작업). 현재는 수동 검증 예정.
35 +- 수동 확인: 빌드(go test ./... 불가하면 최소 gofmt 및 `go test ./internal/service -run
TestUpdateUserPassw
ord` 재확인).
36 +
37 +## 적용 순서 (패치 실행용)
38 +1) 정책 블록 if 래핑 + 주석 변경.
39 +2) Descope API 호출 블록 제거 후 공통 IDP 호출 삽입.
40 +3) gofmt 및 로그/앵커 검사.

View File

@@ -0,0 +1,82 @@
# Descope Federated Apps 연동 PoC (Hydra 1st party 보조)
목표: Hydra를 1st party OIDC 엔진으로 유지하면서 Descope Federated Apps를 통해 외부 IDP(SAML/OIDC)를 빠르게 붙여 BYOID/소셜을 지원한다. Kratos/DB가 SoT가 되고, Hydra 토큰의 subject는 Kratos identity.id로 맞춘다.
## 플로우(로그인)
```mermaid
sequenceDiagram
participant RP as RP App
participant HY as Hydra
participant UI as Baron Login UI
participant DS as Descope Federated App
participant IDP as Ext. IDP (SAML/OIDC)
participant KR as Kratos
RP->>HY: /oauth2/auth (login_challenge)
HY->>UI: redirect to login UI
UI-->>UI: 사용자 선택: "Descope Federated App"
UI->>DS: redirect to Descope Federated App (idp=azuread 등)
DS->>IDP: AuthN (SAML/OIDC)
IDP-->>DS: Assertion/Token
DS-->>UI: redirect back with session/authorization data
UI->>KR: upsert identity (traits from DS)
UI->>KR: CreateSession (kratos admin)
UI->>HY: AcceptLogin(subject=kratos identity.id, amr=descope-federated-{idp})
HY-->>RP: ID/Access Token 발급 (subject=kratos identity.id)
```
## 환경 변수/구성 요소
- Hydra: `HYDRA_ADMIN_URL`, `HYDRA_PUBLIC_URL`, 등록된 RP 클라이언트(redirect URI 포함).
- Kratos: `KRATOS_ADMIN_URL`, `KRATOS_PUBLIC_URL`, identity schema에 federated trait 필드 포함.
- Descope: `DESCOPE_PROJECT_ID`, `DESCOPE_MANAGEMENT_KEY`, `DESCOPE_FEDERATED_APP_ID`, (옵션) `DESCOPE_TENANT`, `DESCOPE_BASE_URL`(self-host 시).
- Baron UI/백엔드: Federated 버튼 노출 플래그, callback URL (`/auth/federated/descope/callback` 등), Hydra login_challenge 전달 경로.
## 데이터 매핑/저장
- Kratos identity traits 예: `email`, `email_verified`, `name`, `phone_number`, `department`, `grade`, `federated_identities` 배열.
- federated_identities 제안 스키마
- `provider` (예: `descope-federated-azuread`)
- `idp_sub`
- `idp_email`
- `idp_email_verified`
- `raw_claims` (JSON, 최소 보관)
- `last_login_at`
- 최초 로그인: email match로 기존 identity 찾기 → 없으면 생성 → federated slot 추가.
- 재로그인: provider+sub 매칭 후 Kratos identity.id 사용.
## 구현 단계 (PoC)
1) **Hydra 클라이언트 준비**
- RP별 redirect URI 등록, `skip_consent` 옵션은 false 권장(동의 화면 제공 시).
- `ory-net` 네트워크에서 Hydra Admin 접근 허용.
2) **Descope Federated App 설정**
- Federated App 생성 후 외부 IDP(SAML/OIDC) 연결.
- Callback을 Baron Login UI로 설정(`/auth/federated/descope/callback?login_challenge=...`).
- 전달 클레임: `sub`, `email`, `email_verified`, `name`, `groups`(option), `phone_number`.
3) **Login UI/Backend 연결**
- 옵션 버튼 “Sign in with <IDP> (via Descope)” 추가.
- Start 엔드포인트: Hydra login_challenge를 받고 Descope Federated App redirect URL 생성 후 302.
- Callback 핸들러:
- Descope session/token 검증 (관리 키 또는 JWKS).
- Kratos identity upsert (traits + federated slot).
- Kratos `CreateSession` 호출 → 세션 쿠키 설정.
- Hydra `AcceptLogin` 호출(subject=kratos identity.id, amr=`federated:descope-{idp}`) → Hydra redirect.
4) **로그/감사**
- Hydra login_challenge, provider, sub, email, amr 기록.
- 실패 시 Hydra `RejectLogin`로 일관된 에러 제공.
## 보안/운영 체크
- `email_verified` 필수 검증, 미확인 이메일은 거절 또는 별도 플로우.
- 토큰/세션 검증 시 JWKS 캐시 및 만료 확인.
- Rate limit: Federated callback, Hydra login_challenge 재사용 방지.
- PII 최소 저장: raw_claims는 단기 TTL 또는 축약 저장.
- 장애 시 폴백: `IDP_PROVIDER=ory,descope` 설정으로 Descope 기본 로컬 로그인 경로 유지.
## 테스트 시나리오 (PoC)
- 성공: Federated 버튼 → 외부 IDP 로그인 → Hydra 토큰 발급, subject=kratos identity.id 확인.
- 이메일 검증 실패: email_verified=false인 경우 거절 메시지.
- 재로그인: 기존 federated_identities 매칭 후 동일 subject 유지.
- 오류: 잘못된 login_challenge, 만료된 Descope 토큰, Hydra RejectLogin 동작 확인.
## 후속 구현(코드)
- Ory IDP 어댑터에 Kratos Admin/Hydra Admin 연동 구현 (`InitiatePasswordReset`, `VerifyPasswordResetToken`, `UpdateUserPassword` 포함).
- AuthHandler에서 Descope 종속 로직을 IDP 추상화 기반으로 재구성(비밀번호 재설정/가입/로그인 모두).
- Login UI에 Federated 버튼 및 상태 처리 추가.
- CI에서 ory-stack 기동 + federated mock IDP로 통합 테스트 추가.