# Headless 사번 로그인 로직 이 문서는 사용자가 데모 애플리케이션에서 **사번과 비밀번호**를 이용해 로그인할 때 발생하는 내부 OIDC (OpenID Connect) Headless 인증 흐름을 설명합니다. ## 핵심 개념 - **Headless Login**: 사용자가 IdP(Identity Provider, Baron SSO)의 로그인 화면을 거치지 않고, RP(Relying Party, 데모 앱) 내에서 직접 아이디와 비밀번호를 입력받아 백채널(Back-channel)로 인증을 수행하는 방식입니다. - **Client Assertion**: 보안을 위해 데모 앱은 사번/비밀번호를 전송할 때 자신이 신뢰할 수 있는 앱임을 증명하는 JWT(`client_assertion`)를 생성하여 함께 전송합니다. --- ## 1. 사번 로그인 시퀀스 다이어그램 ```mermaid sequenceDiagram autonumber actor User as 사용자 participant RP as 데모 앱 (RP) participant OIDC as Baron SSO (OIDC/Hydra) participant Auth as Baron SSO (Headless API) User->>RP: 1. 사번 / 비밀번호 입력 후 로그인 요청 note over RP: [1단계: 신호 요청] RP->>OIDC: 2. OIDC Discovery 및 Authorization Request
(GET /oauth2/auth?response_type=code...) OIDC-->>RP: 3. login_challenge 발급 및 302 Redirect note over RP: [2단계: 본인 인증] RP->>RP: 4. Private Key로 client_assertion (JWT) 생성 RP->>Auth: 5. Headless 로그인 API 호출
(POST /api/v1/auth/headless/password/login)
[client_assertion, login_challenge, 사번, 비밀번호 포함] Auth-->>RP: 6. 인증 성공 및 리다이렉트 URL 반환 note over RP: [3단계: 권한 획득] RP->>OIDC: 7. 리다이렉트 URL 추적 (Cookie 유지) OIDC-->>RP: 8. Consent 화면 (필요 시 자동 승인 API 호출) OIDC-->>RP: 9. 최종 Authorization Code 발급 (code=...) note over RP: [4단계: 인증키 발급] RP->>OIDC: 10. Token Endpoint 호출 (Authorization Code Grant)
[client_assertion 포함] OIDC-->>RP: 11. id_token, access_token 발급 note over RP: [5단계: 로그인 완료] RP->>RP: 12. id_token 검증 및 사용자 세션 생성 RP-->>User: 13. 홈 화면 리다이렉트 및 로그인 성공 ``` --- ## 2. 단계별 상세 로직 설명 위 다이어그램의 주요 단계를 데모 애플리케이션(`server.js`의 `runHeadlessSsoLogin` 함수) 로직을 중심으로 설명합니다. ### [1단계] 신호 요청 (Authorization Request) - **목적**: SSO 서버와의 세션을 시작하고, 인증 컨텍스트를 식별할 수 있는 고유값(`login_challenge`)을 획득합니다. - **동작**: 1. OIDC Discovery를 통해 엔드포인트들을 가져옵니다. 2. `response_type=code`, `client_id`, `state`, `nonce` 등을 포함하여 Authorization Endpoint(`/oauth2/auth`)로 `fetch` 요청(리다이렉트 수동 추적 모드)을 보냅니다. 3. 응답 헤더의 `Location`에 포함된 `login_challenge` 값을 추출합니다. ### [2단계] 본인 인증 (Headless Password Login) - **목적**: 사용자가 입력한 자격 증명(사번, 비밀번호)을 SSO 서버에 직접 전달하여 인증을 승인받습니다. - **동작**: 1. RP의 **Private Key(`keys.json`)**를 사용하여 `client_assertion` JWT를 서명합니다. (이때 `aud` 값은 SSO 서버의 Headless API URL이어야 합니다.) 2. `POST /api/v1/auth/headless/password/login` 엔드포인트로 JSON 페이로드를 전송합니다. ```json { "client_id": "...", "login_challenge": "...", "loginId": "사번", "password": "비밀번호", "client_assertion": "..." } ``` 3. 인증이 성공하면 서버는 OIDC 로그인 흐름을 계속할 수 있는 `redirectTo` 주소를 반환합니다. ### [3단계] 권한 획득 (Redirect Resolution & Consent) - **목적**: 인증 성공 후, OIDC 프로토콜에 따라 최종적으로 `Authorization Code`를 획득합니다. - **동작**: 1. 2단계에서 받은 `redirectTo` 주소를 따라가며 쿠키(Cookie)를 계속 유지시킵니다. 2. 만약 경로 중에 정보 제공 동의(`consent`) 화면이 감지되면, 데모 앱이 사용자 대신 백그라운드에서 동의 API(`POST /api/v1/auth/consent/accept`)를 호출하여 자동 승인합니다. 3. 최종적으로 URL에 `code=...`가 포함된 리다이렉트를 찾습니다. ### [4단계] 인증키 발급 (Token Exchange) - **목적**: 획득한 `Authorization Code`를 안전한 토큰들로 교환합니다. - **동작**: 1. Token Endpoint로 코드를 전송합니다. 2. 이때도 보안을 위해 `private_key_jwt` 방식이 사용되므로, 새로 생성한 `client_assertion`을 요청 본문에 포함시킵니다. 3. 검증이 완료되면 `access_token`과 사용자 정보가 담긴 `id_token`을 받게 됩니다. ### [5단계] 로그인 완료 (Session Creation) - **목적**: 받은 토큰을 검증하고, 브라우저가 인식할 수 있는 로컬 세션을 만듭니다. - **동작**: 1. `jose` 라이브러리를 통해 `id_token`의 서명을 검증하고 payload(사번, 이름, 부서 등)를 추출합니다. 2. 추출된 사용자 정보를 `express-session`의 `req.session.user`에 저장합니다. 3. 클라이언트 브라우저에게 세션 쿠키를 발급하며 홈 화면(`/home.html`)으로 리다이렉트 시킵니다.