Add Baron SSO 로그인 화면 3가지 방식 절차 및 흐름.md

This commit is contained in:
2026-06-22 14:15:24 +09:00
parent 9e89754c4b
commit 927881f5ef

View File

@@ -0,0 +1,428 @@
# Baron SSO 로그인 화면 3가지 방식 절차 및 흐름 정리
작성일: 2026-06-22
## 1. 목적
Baron SSO 로그인 페이지에는 사용자가 선택할 수 있는 로그인 탭이 3개 있다.
- 비밀번호
- 로그인 링크
- QR 코드
본 문서는 각 탭이 어떤 상황에서 쓰이고, 사용자가 어떤 절차를 거치며, 내부 시스템에서는 어떤 데이터와 인증 상태가 이동하는지 설명한다.
## 2. 로그인 화면 기준 요약
| 탭 | 사용자 입력/행동 | 대표 Backend API | 인증 성격 | 적합한 상황 |
| --- | --- | --- | --- | --- |
| 비밀번호 | 이메일 또는 휴대폰 번호 + 비밀번호 입력 | `POST /api/v1/auth/password/login` | Kratos password 기반 직접 인증 | 가장 기본적인 로그인, 최초 로그인, 비밀번호를 알고 있는 경우 |
| 로그인 링크 | 이메일 또는 휴대폰 번호 입력 후 수신한 링크/코드 승인 | `POST /api/v1/auth/enchanted-link/init` -> `poll`, `POST /api/v1/auth/magic-link/verify`, `POST /api/v1/auth/login/code/verify` | Baron pendingRef 기반 원격 승인 | 비밀번호 입력을 줄이고 싶거나, 다른 기기/채널로 본인 확인하려는 경우 |
| QR 코드 | 로그인 화면에 QR 표시, 이미 로그인된 기기에서 스캔/승인 | `POST /api/v1/auth/qr/init` -> `poll` -> `approve` | 교차 기기 승인 | PC에서 로그인하려는데 모바일 또는 다른 브라우저에 이미 로그인되어 있는 경우 |
핵심 차이는 다음과 같다.
- 비밀번호: 현재 로그인 화면에서 사용자가 직접 비밀번호를 증명한다.
- 로그인 링크: 현재 로그인 화면은 대기하고, 이메일/SMS/링크를 받은 쪽에서 승인을 수행한다.
- QR 코드: 현재 로그인 화면은 QR을 띄우고, 이미 로그인된 승인 기기가 해당 QR을 승인한다.
## 3. 공통 운영 경로
운영 환경에서는 사용자의 요청이 Backend, Kratos, Hydra로 직접 들어가지 않는다. 일반적으로 앞단의 L7 또는 Traefik, Baron Gateway Nginx, Oathkeeper를 거쳐 내부 서비스로 라우팅된다.
```mermaid
flowchart LR
User[사용자 브라우저]
RP[RP/업무시스템]
L7[Traefik 또는 외부 L7]
GW[Baron Gateway Nginx]
UF[UserFront 로그인 화면]
BE[Baron Backend]
OK[Oathkeeper]
KR[Ory Kratos]
HY[Ory Hydra]
Redis[Redis]
User -->|업무시스템 접속| RP
RP -->|OIDC 인증 요청 /oidc/oauth2/auth| L7
User -->|Baron SSO 화면/API 요청| L7
L7 --> GW
GW -->|/| UF
GW -->|/api/v1/*| BE
GW -->|/auth/*| OK
GW -->|/oidc/*| OK
OK --> KR
OK --> HY
BE --> Redis
BE --> KR
BE --> HY
```
운영 관점에서는 다음 경로를 구분해서 설명하면 이해하기 쉽다.
| 구분 | 경로 | 의미 |
| --- | --- | --- |
| 화면 진입 | `사용자 -> L7 -> Nginx -> UserFront` | 로그인 페이지를 보여주는 경로 |
| Baron API | `UserFront -> L7 -> Nginx -> Backend` | 로그인 버튼 클릭, 링크 발송, QR 생성, 폴링 요청 |
| Ory Public | `브라우저 또는 Nginx -> Oathkeeper -> Kratos/Hydra` | Ory public API 또는 OIDC public endpoint |
| 내부 백채널 | `Backend -> Kratos/Hydra/Redis` | 세션 발급, OIDC accept, 대기 상태 저장 |
## 4. 비밀번호 로그인
### 4.1 사용자가 보는 절차
1. 사용자가 `비밀번호` 탭을 선택한다.
2. `이메일 또는 휴대폰 번호`를 입력한다.
3. `비밀번호`를 입력한다.
4. 로그인 버튼을 누른다.
5. 인증 성공 시 Baron SSO 세션이 만들어지고, OIDC 로그인 중이면 원래 RP 시스템으로 돌아간다.
6. 실패 시 비밀번호 오류, 미등록 사용자, 비활성 사용자 등의 메시지가 표시된다.
### 4.2 주요 상황
| 상황 | 처리 |
| --- | --- |
| 사용자가 이메일 입력 | 입력값을 그대로 loginId로 사용 |
| 사용자가 휴대폰 번호 입력 | `010-1234-5678` 같은 값을 정리하여 `+821012345678` 형태로 정규화 |
| 일반 UserFront 로그인 | `sessionJwt`를 받아 UserFront 세션으로 사용 |
| RP OIDC 로그인 중 | `login_challenge`를 함께 보내고, 성공 후 Hydra `AcceptLoginRequest`를 거쳐 `redirectTo`로 이동 |
### 4.3 데이터 예시
요청 예시:
```json
{
"loginId": "user@example.com",
"password": "********",
"login_challenge": "optional-hydra-login-challenge"
}
```
응답 예시:
```json
{
"status": "ok",
"provider": "kratos",
"sessionJwt": "eyJ...",
"token": "eyJ...",
"redirectTo": "https://sso.example.com/oidc/oauth2/auth?..."
}
```
`redirectTo`는 OIDC 로그인 흐름일 때 중요하다. RP 시스템에서 Baron SSO로 보낸 로그인 요청을 Hydra가 이어받고, Backend가 로그인 성공을 Hydra에 알려주면, Hydra가 최종 이동할 URL을 돌려준다.
### 4.4 흐름도
```mermaid
sequenceDiagram
autonumber
actor User as 사용자
participant RP as RP/업무시스템
participant L7 as L7/Traefik
participant GW as Baron Gateway Nginx
participant UF as UserFront
participant BE as Baron Backend
participant KR as Ory Kratos
participant HY as Ory Hydra
User->>RP: 업무시스템 접속
RP->>L7: /oidc/oauth2/auth?client_id=...
L7->>GW: OIDC 요청 전달
GW->>HY: /oidc/* -> Hydra
HY-->>UF: login_challenge 포함 로그인 화면 이동
User->>UF: 이메일/휴대폰 + 비밀번호 입력
UF->>L7: POST /api/v1/auth/password/login
L7->>GW: API 요청 전달
GW->>BE: /api/v1/auth/password/login
BE->>KR: Kratos password 인증
KR-->>BE: session_token, cookie
alt login_challenge 있음
BE->>HY: AcceptLoginRequest(login_challenge, subject)
HY-->>BE: redirectTo
BE-->>UF: sessionJwt + redirectTo
UF->>RP: OIDC callback 이동
else 일반 로그인
BE-->>UF: sessionJwt
end
```
## 5. 로그인 링크
### 5.1 사용자가 보는 절차
1. 사용자가 `로그인 링크` 탭을 선택한다.
2. 이메일 또는 휴대폰 번호를 입력한다.
3. `로그인 링크 전송`을 누른다.
4. 현재 브라우저는 로그인 대기 상태가 된다.
5. 사용자는 이메일/SMS로 받은 링크를 클릭하거나, 전달받은 코드를 입력한다.
6. 승인 또는 코드 검증이 성공하면 원래 대기 중이던 브라우저가 로그인 완료된다.
### 5.2 주요 상황
| 상황 | 처리 |
| --- | --- |
| 이메일 입력 | 이메일로 로그인 링크 또는 코드 전달 |
| 휴대폰 번호 입력 | SMS로 로그인 링크 또는 코드 전달 |
| 요청 브라우저 | `pendingRef`를 들고 `poll`을 반복 호출 |
| 승인 브라우저/기기 | 링크 클릭 또는 코드 검증으로 `pendingRef` 승인 |
| verifyOnly 승인 | 승인 기기에는 세션을 만들지 않고, 원래 요청 브라우저만 로그인 완료 |
로그인 링크 방식의 핵심은 `pendingRef`이다. `pendingRef`는 "로그인을 기다리는 요청"을 식별하는 임시 키다. 현재 브라우저는 이 키로 계속 상태를 확인하고, 링크를 클릭한 쪽이 해당 키를 승인하면 현재 브라우저가 세션을 받는다.
### 5.3 데이터 예시
초기 요청:
```json
{
"loginId": "user@example.com",
"uri": "https://sso.example.com",
"codeOnly": false
}
```
초기 응답:
```json
{
"linkId": "Sent",
"pendingRef": "AbC123",
"mode": "code",
"provider": "kratos",
"expiresIn": 300,
"interval": 2,
"resendAfter": 30
}
```
Redis 대기 상태 예시:
```json
{
"key": "enchanted_session:AbC123",
"value": {
"status": "pending",
"loginId": "user@example.com"
},
"ttl": "5m"
}
```
폴링 중 대기 응답:
```json
{
"error": "authorization_pending",
"code": "authorization_pending",
"interval": 2
}
```
승인 후 성공 응답:
```json
{
"status": "ok",
"sessionJwt": "eyJ..."
}
```
### 5.4 흐름도
```mermaid
sequenceDiagram
autonumber
actor User as 사용자
participant UF as 요청 브라우저(UserFront)
participant L7 as L7/Traefik
participant GW as Baron Gateway Nginx
participant BE as Baron Backend
participant Redis as Redis
participant KR as Ory Kratos/Courier
participant Channel as Email/SMS
participant Approver as 링크 승인 기기
User->>UF: 로그인 링크 탭에서 이메일/휴대폰 입력
UF->>L7: POST /api/v1/auth/enchanted-link/init
L7->>GW: API 요청 전달
GW->>BE: init 요청
BE->>BE: 사용자 존재 여부 확인, 전화번호 정규화
BE->>KR: 링크/코드 로그인 시작
BE->>Redis: pendingRef 상태 저장(status=pending)
KR-->>Channel: 로그인 링크 또는 코드 발송
BE-->>UF: pendingRef, expiresIn, interval
loop 요청 브라우저 대기
UF->>BE: POST /api/v1/auth/enchanted-link/poll(pendingRef)
BE->>Redis: pendingRef 상태 조회
BE-->>UF: authorization_pending 또는 slow_down
end
User->>Approver: 수신한 링크 클릭 또는 코드 입력
Approver->>BE: POST /api/v1/auth/magic-link/verify 또는 /login/code/verify
BE->>Redis: pendingRef 승인 처리(status=approved/success)
UF->>BE: poll 재시도
BE-->>UF: sessionJwt
```
### 5.5 보안상 설명 포인트
로그인 링크는 비밀번호를 입력하지 않아 편리하지만, 링크를 받은 사람이 곧 승인자가 된다. 따라서 운영 설명 시 다음 통제가 필요하다.
- 링크 만료 시간은 짧게 유지한다.
- 재전송 제한과 폴링 속도 제한을 둔다.
- 승인 화면에는 요청 기기, IP, 브라우저, 요청 시각을 표시한다.
- "내가 요청한 로그인이 아님" 버튼을 제공한다.
- 요청자와 승인자를 감사 로그에 분리 기록한다.
## 6. QR 코드 로그인
### 6.1 사용자가 보는 절차
1. 사용자가 `QR 코드` 탭을 선택한다.
2. 로그인 화면에 QR 코드가 표시된다.
3. 사용자는 이미 로그인되어 있는 기기에서 QR을 스캔한다.
4. 승인 기기에서 로그인 요청 정보를 확인하고 승인한다.
5. 원래 QR을 띄운 브라우저가 로그인 완료된다.
### 6.2 주요 상황
| 상황 | 처리 |
| --- | --- |
| PC에서 새 로그인 필요 | PC 화면에 QR 표시 |
| 사용자가 모바일/다른 브라우저에 이미 로그인되어 있음 | 해당 기기로 QR 스캔 후 승인 |
| 승인 기기에 세션 없음 | 승인 불가, 먼저 승인 기기에서 로그인 필요 |
| QR 만료 | `expired_token` 처리 후 새 QR 발급 필요 |
| 폴링 과다 | `slow_down`으로 폴링 간격 증가 |
QR 방식은 "이미 로그인된 기기"를 신뢰 근거로 쓴다. 즉 QR을 스캔하는 기기가 Baron SSO 세션을 가지고 있어야 하며, 그 세션의 사용자가 QR을 띄운 브라우저에 대한 로그인을 승인하는 구조다.
### 6.3 데이터 예시
QR 초기화 응답:
```json
{
"qrCode": "https://sso.example.com/ql/QR_REF_64_CHARS",
"pendingRef": "PENDING_REF",
"expiresIn": 300,
"interval": 2
}
```
Redis 저장 예시:
```json
[
{
"key": "enchanted_session:PENDING_REF",
"value": {
"status": "pending"
},
"ttl": "5m"
},
{
"key": "qr_ref:QR_REF_64_CHARS",
"value": "PENDING_REF",
"ttl": "5m"
}
]
```
승인 요청 예시:
```json
{
"pendingRef": "PENDING_REF"
}
```
승인 요청은 쿠키 세션 또는 토큰 세션을 통해 승인 사용자를 확인한다. 승인 후에는 QR을 띄운 브라우저 쪽 `pendingRef`가 성공 상태로 바뀐다.
### 6.4 흐름도
```mermaid
sequenceDiagram
autonumber
actor User as 사용자
participant PC as 요청 PC(UserFront)
participant L7 as L7/Traefik
participant GW as Baron Gateway Nginx
participant BE as Baron Backend
participant Redis as Redis
participant Mobile as 이미 로그인된 승인 기기
participant KR as Ory Kratos
User->>PC: QR 코드 탭 선택
PC->>L7: POST /api/v1/auth/qr/init
L7->>GW: API 요청 전달
GW->>BE: QR init
BE->>Redis: pendingRef/status=pending 저장
BE->>Redis: qrRef -> pendingRef 매핑 저장
BE-->>PC: qrCode URL, pendingRef, expiresIn
PC->>PC: QR 코드 표시
loop 요청 PC 대기
PC->>BE: POST /api/v1/auth/qr/poll(pendingRef)
BE->>Redis: pendingRef 상태 조회
BE-->>PC: authorization_pending 또는 slow_down
end
User->>Mobile: QR 스캔
Mobile->>BE: POST /api/v1/auth/qr/approve(pendingRef 또는 qrRef)
BE->>KR: 승인 기기 세션 확인(whoami/session)
BE->>KR: QR 대상 사용자의 코드 로그인/세션 발급 처리
BE->>Redis: pendingRef/status=success, jwt 저장
BE-->>Mobile: QR Login Approved
PC->>BE: poll 재시도
BE-->>PC: sessionJwt
```
### 6.5 QR 방식의 핵심 통제
QR 로그인은 편리하지만, 공격자가 자기 PC에 QR을 띄우고 사용자에게 스캔을 유도할 수 있다. 따라서 승인 화면에는 반드시 다음 정보가 필요하다.
| 승인 화면 표시 정보 | 이유 |
| --- | --- |
| 요청 기기 종류 | 내 PC인지 확인 |
| 요청 브라우저 | 평소 사용하는 브라우저인지 확인 |
| 요청 IP/대략 위치 | 낯선 위치 요청인지 확인 |
| 요청 시각 | 방금 내가 요청한 것인지 확인 |
| RP 시스템 이름 | 어떤 업무시스템 로그인을 승인하는지 확인 |
또한 승인 버튼과 별도로 `내가 요청한 로그인이 아님` 버튼을 제공해야 한다. 이 버튼은 해당 `pendingRef`를 차단하고, 필요 시 세션/refresh grant revoke로 이어져야 한다.
## 7. 세 방식 비교
| 비교 항목 | 비밀번호 | 로그인 링크 | QR 코드 |
| --- | --- | --- | --- |
| 현재 화면에서 인증 완료 가능 | 가능 | 불가능, 외부 링크/코드 필요 | 불가능, 승인 기기 필요 |
| 비밀번호 필요 | 필요 | 불필요 | 불필요 |
| 기존 로그인 기기 필요 | 불필요 | 필수는 아님 | 사실상 필요 |
| 이메일/SMS 채널 필요 | 불필요 | 필요 | 불필요 |
| 교차 기기 승인 | 아님 | 가능 | 핵심 기능 |
| 주요 임시 키 | 없음 | `pendingRef`, magic token, login code | `pendingRef`, `qrRef` |
| 주요 위험 | 비밀번호 탈취, 무차별 대입 | 링크 탈취, 오발송, 피싱 승인 | QR 피싱, 잘못된 승인 |
| 운영 통제 | 비밀번호 정책, 실패 횟수 제한 | TTL, 재전송 제한, 승인 정보 표시 | TTL, 승인 정보 표시, 기존 세션 검증 |
## 8. 팀 설명용 쉬운 표현
비밀번호 로그인은 "본인이 지금 이 화면에서 비밀번호를 직접 증명하는 방식"이다.
로그인 링크는 "지금 화면은 대기표를 뽑고, 이메일이나 문자로 받은 링크를 눌러 그 대기표를 승인하는 방식"이다.
QR 코드는 "새 PC 화면에 임시 출입증을 띄우고, 이미 로그인된 내 기기로 그 출입증을 스캔해서 승인하는 방식"이다.
세 방식 모두 최종 목적은 같다. Baron Backend가 사용자를 확인하고, Kratos 세션을 얻은 뒤, RP 로그인이면 Hydra OIDC 흐름을 완료하여 사용자를 원래 업무시스템으로 돌려보낸다. 다만 "사용자 본인임을 확인하는 수단"이 각각 다르다.
## 9. 운영/보안 권고
1. 비밀번호 방식은 기본 로그인 수단으로 유지하되, 실패 횟수 제한과 감사 로그를 반드시 유지한다.
2. 로그인 링크 방식은 만료 시간, 재전송 제한, 링크 재사용 방지를 강하게 적용한다.
3. QR 방식은 승인 화면에 요청 정보를 충분히 보여주고, 사용자가 명확히 승인하도록 한다.
4. 링크/QR 방식 모두 `요청자 기기``승인자 기기`를 감사 로그에 분리 저장한다.
5. `내가 요청한 로그인이 아님`을 누르면 해당 pending 요청을 즉시 차단하고, 이미 발급된 세션 또는 refresh grant가 있으면 단계적으로 revoke한다.
6. 내부 사용자는 Naver Works 같은 조직 채널 알림과 연동할 수 있지만, 외부 사용자는 이메일/패스키/TOTP 등 별도 신뢰 채널을 마련해야 한다.