diff --git a/docs/consent_scope_selection_flow.md b/docs/consent_scope_selection_flow.md new file mode 100644 index 00000000..f9703d56 --- /dev/null +++ b/docs/consent_scope_selection_flow.md @@ -0,0 +1,110 @@ +# Baron SSO 권한 선택 및 동적 스코프(Scope) 처리 흐름 + +이 문서는 사용자가 Consent(권한 동의) 화면에서 특정 권한(Scope)을 선택하고, 그 선택된 권한들이 어떻게 백엔드와 Ory Hydra로 전달되어 처리되는지에 대한 구현 상세를 설명합니다. 또한, 개발자 포털(`devfront`)에서 설정한 권한별 설명이 어떻게 화면에 동적으로 표시되는지 설명합니다. + +## 1. 개요 + +사용자 중심의 개인정보 제어를 위해 다음과 같은 기능이 구현되었습니다. + +1. **권한 선택**: 사용자는 필수(Mandatory)가 아닌 권한을 직접 선택하거나 해제할 수 있습니다. +2. **동적 설명**: 개발자 포털에서 설정한 각 권한에 대한 사용자 친화적인 설명(한글 등)이 Consent 화면에 표시됩니다. +3. **필수 권한 보장**: `openid`와 같이 서비스 동작에 필수적인 권한은 선택 해제가 불가능합니다. + +## 2. 데이터 흐름 (Data Flow) + +### Step 1: Consent 정보 조회 (`GET /consent`) + +사용자가 `/consent` 페이지에 진입하면, 프론트엔드는 백엔드에 상세 정보를 요청합니다. + +1. **Frontend (`userfront`)**: `AuthProxyService.getConsentInfo(challenge)` 호출. +2. **Backend (`backend`)**: `AuthHandler.GetConsentRequest` 핸들러 실행. + * Hydra Admin API를 호출하여 Consent Request 정보(요청된 스코프, 클라이언트 정보 등)를 가져옵니다. + * **핵심 로직**: Hydra Client의 `Metadata` 필드 내 `structured_scopes`를 파싱합니다. + * 파싱된 정보를 바탕으로 `scope_details` 객체(설명, 필수 여부 포함)를 생성하여 응답에 추가합니다. + +```json +// 백엔드 응답 예시 +{ + "challenge": "...", + "requested_scope": ["openid", "profile", "email"], + "scope_details": { + "openid": { "description": "인증 필수 정보", "mandatory": true }, + "profile": { "description": "프로필 정보", "mandatory": false } + }, + ... +} +``` + +### Step 2: UI 렌더링 및 사용자 선택 + +1. **Frontend (`userfront`)**: `ConsentScreen` 위젯 렌더링. + * 백엔드에서 받은 `requested_scope` 목록을 순회하며 체크박스를 생성합니다. + * `scope_details`에 있는 설명을 우선적으로 표시합니다. + * `mandatory: true`인 스코프(예: `openid`)는 체크박스를 비활성화(선택 고정)합니다. + * 사용자는 나머지 선택 가능한 스코프를 체크하거나 해제합니다. + +### Step 3: 동의 처리 (`POST /consent/accept`) + +사용자가 "동의하고 계속하기" 버튼을 클릭하면, **선택된 스코프 목록만** 백엔드로 전송됩니다. + +1. **Frontend (`userfront`)**: `AuthProxyService.acceptConsent(challenge, grantScope: [...])` 호출. + * `grant_scope` 파라미터에 사용자가 최종적으로 선택한 스코프 배열을 담아 보냅니다. + +2. **Backend (`backend`)**: `AuthHandler.AcceptConsentRequest` 핸들러 실행. + * 요청 바디에서 `grant_scope`를 추출합니다. + * 보안을 위해, 사용자가 보낸 `grant_scope`가 원래 요청된(requested) 스코프에 포함되는지 검증합니다. + * 검증된 스코프 목록을 Hydra의 `AcceptConsentRequest` 페이로드(`grant_scope`)에 담아 호출합니다. + +3. **Hydra**: + * 전달받은 `grant_scope`만을 포함한 Access Token 및 ID Token을 생성할 준비를 하고, 최종 리다이렉트 URL을 반환합니다. + +## 3. 파일별 구현 상세 + +### 1. Backend (`backend/internal/handler/auth_handler.go`) + +* **`GetConsentRequest`**: + * Hydra Client의 Metadata(`structured_scopes`)를 읽어 `scope_details`를 응답에 주입하는 로직이 추가되었습니다. +* **`AcceptConsentRequest`**: + * 요청 바디 구조체에 `GrantScope []string` 필드를 추가했습니다. + * Hydra API 호출 시 `RequestedScope` 필드를 사용자가 선택한 스코프로 덮어씌워 전달합니다. + +### 2. Frontend (`userfront/lib/features/auth/presentation/consent_screen.dart`) + +* **`_fetchConsentInfo`**: + * API 응답의 `scope_details`를 파싱하여 `_scopeDescriptions`와 `_mandatoryScopes` 상태를 동적으로 업데이트합니다. +* **`_acceptConsent`**: + * 사용자가 체크한 `_selectedScopes`를 리스트로 변환하여 API 호출 시 전달합니다. +* **UI**: + * `CheckboxListTile`을 사용하여 각 권한별 선택 UI를 구성했습니다. + +### 3. Frontend Service (`userfront/lib/core/services/auth_proxy_service.dart`) + +* **`acceptConsent`**: + * `List? grantScope` 파라미터를 추가하고, API 요청 바디에 포함시키는 기능을 구현했습니다. + +## 4. 메타데이터 구조 (참고) + +개발자 포털(`devfront`)에서 설정된 스코프 메타데이터는 Hydra Client의 `metadata` 필드에 다음과 같이 저장됩니다. + +```json +{ + "metadata": { + "structured_scopes": [ + { + "id": "1", + "name": "openid", + "description": "서비스 이용을 위한 필수 인증 정보입니다.", + "mandatory": true + }, + { + "id": "2", + "name": "email", + "description": "이메일 알림을 받기 위해 필요합니다.", + "mandatory": false + } + ] + } +} +``` + +백엔드는 이 정보를 읽어서 Consent 화면에 필요한 정보로 가공하여 전달합니다.