From ee2e186bd6cf9f10474218aa7e1c38ecad9cec84 Mon Sep 17 00:00:00 2001 From: kyy Date: Mon, 13 Apr 2026 18:10:50 +0900 Subject: [PATCH] =?UTF-8?q?scope=20=EC=A0=95=EC=B1=85=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/headless-consent-and-scope.md | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/headless-consent-and-scope.md diff --git a/docs/headless-consent-and-scope.md b/docs/headless-consent-and-scope.md new file mode 100644 index 0000000..cefc1d7 --- /dev/null +++ b/docs/headless-consent-and-scope.md @@ -0,0 +1,97 @@ +# Headless 스코프(Scope) 및 자동 승인(Auto-Consent) 가이드 + +이 문서는 Headless 로그인 환경에서 애플리케이션이 사용자 정보를 가져오기 위해 사용하는 **스코프(Scope)**의 개념과, IdP의 화면 없이 권한을 획득하는 **자동 승인(Auto-Consent)** 로직을 설명합니다. + +--- + +## 1. 요구 스코프 (Requested Scopes) + +데모 앱은 OIDC 표준에 따라 다음 스코프를 IdP(Baron SSO)에 요청합니다. + +| 스코프 | 필수 여부 | 역할 및 획득 데이터 | +| :-------- | :-------: | :------------------------------------------------------------------ | +| `openid` | **필수** | 해당 요청이 OIDC 인증임을 명시. `id_token` 발급을 위해 반드시 필요. | +| `profile` | 권장 | 사용자의 기본 프로필 정보(이름, 사번, 부서명 등) 접근 권한. | +| `email` | 권장 | 사용자의 이메일 주소 정보 접근 권한. | + +데모 앱은 최초 인증 요청(`GET /oauth2/auth`) 시 `scope=openid profile` 형식으로 권한을 요구합니다. + +--- + +## 2. 자동 승인(Auto-Consent) 흐름도 + +일반적인 OIDC 환경에서는 사용자에게 '정보 제공 동의' 화면이 노출되지만, Headless 환경에서는 **RP 서버가 이 과정을 백그라운드에서 자동으로 수행**합니다. + +```mermaid +sequenceDiagram + autonumber + participant Browser as 사용자 브라우저 + participant RP as 데모 앱 서버 (RP) + participant SSO as Baron SSO (IdP) + + RP->>SSO: 1. 본인 인증 요청 (전화번호) + SSO-->>RP: 2. 인증 성공 및 리다이렉트 (redirectTo: /consent...) + + Note over RP: [자동 승인 로직 시작] + RP->>RP: 3. 리다이렉트 경로 중 '/consent' 감지 + RP->>SSO: 4. 동의 상세 정보 요청 (GET /api/v1/auth/consent) + SSO-->>RP: 5. 요청된 스코프 정보 반환 (openid, profile 등) + + RP->>SSO: 6. 동의 승인 API 호출 (POST /api/v1/auth/consent/accept)
[grant_scope 포함] + SSO-->>RP: 7. 최종 리다이렉트 주소 반환 (code=...) + + Note over RP: [표준 OIDC 흐름 복귀] + RP->>SSO: 8. Token Exchange (Code -> id_token) + SSO-->>RP: 9. 사용자 정보가 담긴 id_token 발급 + RP-->>Browser: 10. 세션 생성 및 로그인 완료 안내 +``` +--- + +## 3. 핵심 구현 코드 (server.js) + +데모 앱의 `server.js` 내 `resolveRedirects` 함수는 SSO 서버로부터 오는 리다이렉트 주소를 추적하다가, 동의 화면(`consent_challenge`)이 나타나면 이를 가로채서 처리합니다. + +```javascript +if (currentUrl.includes("/consent")) { + console.log( + " [자동 승인] 사용자의 정보 제공 동의 화면이 감지되어 시스템이 자동으로 승인 중입니다.", + ); + + // 1. URL에서 consent_challenge 추출 + const consentUrl = new URL(currentUrl); + const consentChallenge = consentUrl.searchParams.get("consent_challenge"); + + // 2. SSO 서버에 현재 요청된 스코프가 무엇인지 확인 + const detailsUrl = new URL("/api/v1/auth/consent", currentUrl); + detailsUrl.searchParams.set("consent_challenge", consentChallenge); + const detailsRes = await fetch(detailsUrl.toString(), { + headers: { Cookie: nextCookies }, + }); + const consentInfo = await detailsRes.json(); + + // 3. 확인된 스코프(openid, profile 등)를 모두 허용(accept)한다고 서버에 전송 + const acceptUrl = new URL("/api/v1/auth/consent/accept", currentUrl); + const acceptRes = await fetch(acceptUrl.toString(), { + method: "POST", + headers: { "Content-Type": "application/json", Cookie: nextCookies }, + body: JSON.stringify({ + consent_challenge: consentChallenge, + grant_scope: consentInfo.requested_scope || ["openid", "profile"], + grant_access_token_audience: + consentInfo.requested_access_token_audience || [], + }), + }); + + const acceptPayload = await acceptRes.json(); + // 4. 승인 후 받은 최종 목적지(code 포함)로 이동 계속 + return resolveRedirects(acceptPayload.redirectTo, nextCookies, depth + 1); +} +``` + +--- + +## 4. 특징 및 장점 + +- **심리스한 UX**: 사용자는 "로그인 중..."이라는 메시지만 보게 되며, 복잡한 권한 동의 절차를 신경 쓸 필요가 없습니다. +- **강력한 보안**: 자동 승인 과정은 서버 대 서버(Back-channel) 통신으로 이루어지며, 브라우저에는 동의 관련 데이터가 전혀 노출되지 않습니다. +- **유연한 확장**: 나중에 새로운 사용자 정보(`email` 등)가 필요해지더라도, 앱의 스코프 설정만 변경하면 자동으로 승인 로직에 반영됩니다.