# 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`)으로 리다이렉트 시킵니다.