forked from baron/baron-sso
위키 업데이트 및 docs 최신화
This commit is contained in:
117
docs/trouble-shooting/consent_loop_fix_report.md
Normal file
117
docs/trouble-shooting/consent_loop_fix_report.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Consent 반복 노출 문제 해결 보고서
|
||||
|
||||
## 1. 개요
|
||||
Gitea 등 RP(Relying Party) 로그인 시, 사용자가 이미 권한에 동의했음에도 불구하고 재로그인할 때마다 Consent(권한 동의) 화면이 반복적으로 노출되는 문제를 해결했습니다.
|
||||
이 문서는 해당 문제를 해결하기 위해 수정된 파일, 함수, 그리고 변경된 동작 흐름을 기술합니다.
|
||||
|
||||
## 2. 수정된 파일 및 함수
|
||||
|
||||
### A. 백엔드 서비스 계층
|
||||
* **파일**: `backend/internal/service/hydra_admin_service.go`
|
||||
* **함수**: `AcceptConsentRequest`, `AcceptLoginRequest`
|
||||
* **변경 내용**: Hydra에 동의 및 로그인 정보를 저장할 때 유효 기간(`remember_for`)을 연장.
|
||||
|
||||
### B. 백엔드 핸들러 계층
|
||||
* **파일**: `backend/internal/handler/auth_handler.go`
|
||||
* **함수**: `GetConsentRequest`
|
||||
* **변경 내용**: Hydra로부터 받은 동의 요청 정보에 `skip: true` 플래그가 있는 경우, 화면 데이터를 반환하는 대신 **자동 승인 프로세스**를 수행하도록 로직 추가.
|
||||
|
||||
### C. 프론트엔드 (UserFront)
|
||||
* **파일**: `userfront/lib/features/auth/presentation/consent_screen.dart`
|
||||
* **함수**: `_fetchConsentInfo`
|
||||
* **변경 내용**: 백엔드 API 응답에 `redirectTo` 필드가 포함된 경우, UI 렌더링을 건너뛰고 **즉시 해당 URL로 리다이렉트**하도록 수정.
|
||||
|
||||
---
|
||||
|
||||
## 3. 상세 수정 로직 및 동작 흐름
|
||||
|
||||
### 3.1. 동의 기억 기간 연장 (Backend Service)
|
||||
기존에는 동의 정보가 1시간(`3600`) 동안만 유지되어, 1시간 후 재로그인 시 다시 동의 화면이 나타났습니다. 이를 30일(`2592000`)로 늘려 사용자의 편의성을 높였습니다.
|
||||
|
||||
```go
|
||||
// backend/internal/service/hydra_admin_service.go
|
||||
|
||||
func (s *HydraAdminService) AcceptConsentRequest(...) {
|
||||
// ...
|
||||
payload := map[string]interface{}{
|
||||
// ...
|
||||
"remember": true,
|
||||
"remember_for": 2592000, // 수정 전: 3600 (1시간) -> 수정 후: 30일
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2. 자동 승인(Skip) 로직 구현 (Backend Handler)
|
||||
Hydra는 사용자가 이전에 동의한 기록이 유효하다면 `skip: true` 플래그를 보냅니다. 백엔드는 이 신호를 감지하여 사용자 개입 없이 동의 절차를 완료해야 합니다.
|
||||
|
||||
**[수정된 흐름]**
|
||||
1. `GetConsentRequest` 호출 시 Hydra로부터 `consentRequest` 정보를 받아옴.
|
||||
2. `consentRequest.Skip`이 `true`인지 확인.
|
||||
3. **True인 경우 (자동 승인):**
|
||||
* Kratos에서 사용자 신원(`Identity`) 조회.
|
||||
* 사용자 특성(`Traits`)을 기반으로 OIDC 클레임(`sessionClaims`) 생성.
|
||||
* `Hydra.AcceptConsentRequest`를 호출하여 승인 처리.
|
||||
* Hydra가 반환한 리다이렉트 URL(`redirectTo`)을 프론트엔드에 JSON으로 응답.
|
||||
4. **False인 경우 (일반 진행):**
|
||||
* 기존 로직대로 Consent 화면에 필요한 정보(클라이언트 이름, 스코프 목록 등)를 반환.
|
||||
|
||||
```go
|
||||
// backend/internal/handler/auth_handler.go
|
||||
|
||||
func (h *AuthHandler) GetConsentRequest(c *fiber.Ctx) error {
|
||||
// ... Hydra 조회 ...
|
||||
|
||||
// [추가된 로직] Skip 플래그 확인 및 자동 승인
|
||||
if consentRequest.Skip {
|
||||
// 1. 사용자 정보 조회
|
||||
identity, _ := h.KratosAdmin.GetIdentity(...)
|
||||
// 2. 클레임 생성
|
||||
sessionClaims := buildOidcClaimsFromTraits(...)
|
||||
// 3. Hydra 승인 요청
|
||||
acceptResp, _ := h.Hydra.AcceptConsentRequest(..., sessionClaims)
|
||||
|
||||
// 4. 리다이렉트 URL 반환 (화면 생략)
|
||||
return c.JSON(acceptResp)
|
||||
}
|
||||
|
||||
// ... 기존 화면 정보 반환 로직 ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. 즉시 리다이렉트 처리 (Frontend)
|
||||
프론트엔드는 백엔드의 응답을 확인하여, 화면을 그릴지 아니면 바로 다른 페이지로 이동할지 결정합니다.
|
||||
|
||||
**[수정된 흐름]**
|
||||
1. `ConsentScreen` 진입 시 `_fetchConsentInfo` 실행.
|
||||
2. 백엔드 API(`GET /consent`) 호출.
|
||||
3. 응답 데이터(`info`)에 `redirectTo` 필드가 있는지 확인.
|
||||
4. **존재하는 경우:** `webWindow.redirectTo`를 통해 즉시 이동. (UI 렌더링 중단)
|
||||
5. **없는 경우:** 받은 정보를 바탕으로 권한 동의 UI(체크박스, 버튼 등) 렌더링.
|
||||
|
||||
```dart
|
||||
// userfront/lib/features/auth/presentation/consent_screen.dart
|
||||
|
||||
Future<void> _fetchConsentInfo() async {
|
||||
final info = await AuthProxyService.getConsentInfo(...);
|
||||
|
||||
// [추가된 로직] 리다이렉트 URL 존재 시 즉시 이동
|
||||
if (info['redirectTo'] != null) {
|
||||
webWindow.redirectTo(info['redirectTo']);
|
||||
return;
|
||||
}
|
||||
|
||||
// ... UI 렌더링 준비 ...
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 최종 동작 시나리오
|
||||
|
||||
1. **최초 로그인**:
|
||||
* Hydra `skip: false` -> 백엔드가 화면 정보 반환 -> 프론트엔드가 Consent UI 노출 -> 사용자 동의 -> 백엔드가 `remember_for: 30일`로 승인 처리.
|
||||
2. **재로그인 (30일 이내)**:
|
||||
* Hydra `skip: true` 반환.
|
||||
* 백엔드 `GetConsentRequest`가 이를 감지하고 내부적으로 `AcceptConsentRequest` 수행.
|
||||
* 백엔드가 프론트엔드에 `{ "redirectTo": "https://gitea..." }` 응답.
|
||||
* 프론트엔드는 화면을 그리지 않고 즉시 Gitea로 이동.
|
||||
* **결과**: 사용자는 동의 화면을 보지 않고 로그인 완료.
|
||||
51
docs/trouble-shooting/hydra-rp-dummy.md
Normal file
51
docs/trouble-shooting/hydra-rp-dummy.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# hydra-rp-dummy 사용 기록
|
||||
|
||||
## 목적
|
||||
`devfront/hydra-rp-dummy.py`를 이용해 Hydra에 더미 RP consent를 생성하고, UserFront 활동상황 카드에 반영되는지 확인합니다.
|
||||
|
||||
## 사전 조건
|
||||
- Hydra/크라토스 스택이 실행 중이어야 합니다.
|
||||
- `ory_hydra` 컨테이너가 존재해야 합니다.
|
||||
- Docker 이미지 `python:3.12-alpine`가 필요합니다.
|
||||
|
||||
## 입력 값
|
||||
- `CLIENT_ID`: 더미 RP의 client_id
|
||||
- `SUBJECT`: Kratos identity id (예: `22607c1b-bfbf-4a90-9505-36b348472e7a`)
|
||||
- `REDIRECT_URI`: client에 등록된 redirect_uri
|
||||
- `SCOPE`: `openid profile email`
|
||||
- `STATE`, `NONCE`: 충분히 긴 랜덤 값
|
||||
|
||||
## 실행 방법
|
||||
다음 명령으로 `hydra-rp-dummy.py`를 컨테이너에 마운트해 실행합니다.
|
||||
|
||||
```bash
|
||||
docker run --rm --network container:ory_hydra \
|
||||
-v /home/lectom/repos/baron-sso/devfront/hydra-rp-dummy.py:/tmp/hydra-rp-dummy.py:ro \
|
||||
-e CLIENT_ID=52a597f0-5b06-4fcb-b804-93e88a56a75a \
|
||||
-e SUBJECT=22607c1b-bfbf-4a90-9505-36b348472e7a \
|
||||
-e REDIRECT_URI=https://example.com/callback \
|
||||
-e SCOPE='openid profile email' \
|
||||
-e STATE=state-$(date +%s%N) \
|
||||
-e NONCE=nonce-$(date +%s%N) \
|
||||
python:3.12-alpine python /tmp/hydra-rp-dummy.py
|
||||
```
|
||||
|
||||
## 동작 방식 요약
|
||||
- `hydra-rp-dummy.py`가 127.0.0.1:3000에 임시 consent app을 띄웁니다.
|
||||
- `/oauth2/auth` 호출로 login_challenge를 받고 자동 수락합니다.
|
||||
- 이어서 consent_challenge를 받아 자동 수락합니다.
|
||||
- 마지막 redirect 단계에서 CSRF 에러가 발생할 수 있으나, **consent 세션 자체는 생성됩니다.**
|
||||
|
||||
## 생성 확인 방법
|
||||
Hydra Admin API에서 consent 세션을 확인합니다.
|
||||
|
||||
```bash
|
||||
docker run --rm --network container:ory_hydra curlimages/curl:8.11.1 \
|
||||
sh -lc 'curl -s "http://127.0.0.1:4445/oauth2/auth/sessions/consent?subject=22607c1b-bfbf-4a90-9505-36b348472e7a&client=52a597f0-5b06-4fcb-b804-93e88a56a75a"'
|
||||
```
|
||||
|
||||
예시 응답에 `grant_scope`, `handled_at`, `client_id` 등이 포함되면 성공입니다.
|
||||
|
||||
## 참고
|
||||
- `URLS_LOGIN`, `URLS_CONSENT`는 현재 `http://127.0.0.1:3000`으로 설정되어 있습니다.
|
||||
- 따라서 임시 consent app 없이는 consent 생성이 진행되지 않습니다.
|
||||
Reference in New Issue
Block a user