Update Baron SSO 로그인 방식 및 비표준 인증 흐름 정리.md
This commit is contained in:
@@ -33,6 +33,50 @@ Baron SSO는 Ory Kratos와 Ory Hydra를 기반으로 한다.
|
||||
|
||||
참고로 `POST /api/v1/auth/sms`, `POST /api/v1/auth/verify-sms`도 존재하지만, 위의 "표준 2개 + 비표준 3개" 분류에서는 별도 보조 또는 내부 토큰 기반 흐름으로 보는 것이 적절하다.
|
||||
|
||||
### 2.1 운영 요청 경로와 Gateway 역할
|
||||
|
||||
운영 경로에서는 사용자의 요청이 곧바로 Backend, Kratos, Hydra로 들어가지 않는다. 앞단에는 Traefik 또는 외부 L7, Baron Gateway Nginx, Oathkeeper가 있다.
|
||||
|
||||
현재 `gateway/nginx.conf` 기준 역할은 다음과 같다.
|
||||
|
||||
| 경로 | Nginx 처리 | 다음 대상 |
|
||||
| --- | --- | --- |
|
||||
| `/api/*` | Backend API proxy | `baron_backend:3000` |
|
||||
| `/auth/*` | Ory Stack proxy | `oathkeeper:4455` -> Kratos public |
|
||||
| `/oidc/*` | Ory Stack proxy | `oathkeeper:4455` -> Hydra public |
|
||||
| `/` | UserFront 정적/앱 proxy | `baron_userfront:5000` |
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
User[사용자 브라우저]
|
||||
RP[RP/업무시스템]
|
||||
L7[Traefik 또는 외부 L7]
|
||||
GW[Nginx Baron Gateway]
|
||||
UF[UserFront]
|
||||
BE[Baron Backend]
|
||||
OK[Oathkeeper]
|
||||
KR[Ory Kratos Public]
|
||||
HY[Ory Hydra Public]
|
||||
|
||||
User --> RP
|
||||
RP -->|OIDC /oidc/oauth2/auth| L7
|
||||
User -->|Baron SSO 접속| L7
|
||||
L7 --> GW
|
||||
GW -->|/| UF
|
||||
GW -->|/api/v1/*| BE
|
||||
GW -->|/auth/*| OK
|
||||
GW -->|/oidc/*| OK
|
||||
OK -->|Kratos public API| KR
|
||||
OK -->|Hydra public API| HY
|
||||
```
|
||||
|
||||
따라서 로그인 흐름을 설명할 때는 다음 두 경로를 구분해야 한다.
|
||||
|
||||
- 브라우저/API 호출 경로: `사용자 또는 RP -> L7 -> Nginx Gateway -> Backend/Oathkeeper`
|
||||
- 내부 백채널 경로: `Backend -> Kratos Admin/Public`, `Backend -> Hydra Admin`, `Kratos Courier -> Backend`
|
||||
|
||||
## 3. 표준 방식 2개
|
||||
|
||||
### 3.1 ID/Password
|
||||
@@ -49,6 +93,31 @@ Kratos 인증이 성공하면 세션 토큰과 Kratos 세션 쿠키가 발급된
|
||||
|
||||
OIDC 로그인 중이면 `login_challenge`도 함께 전달된다. 이 경우 Backend는 Hydra의 login request를 확인한 뒤 `AcceptLoginRequest`를 호출하고, Hydra가 내려준 `redirectTo`를 UserFront에 반환한다. UserFront는 이 URL로 이동하여 RP의 OIDC 흐름을 계속 진행한다.
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant UF as UserFront
|
||||
participant BE as Baron Backend
|
||||
participant KR as Ory Kratos
|
||||
participant HY as Ory Hydra
|
||||
participant RP as RP/업무시스템
|
||||
|
||||
User->>RP: 업무시스템 접속
|
||||
RP->>HY: OIDC 인증 요청
|
||||
HY->>UF: login_challenge와 함께 로그인 화면 이동
|
||||
User->>UF: ID/Password 입력
|
||||
UF->>BE: POST /api/v1/auth/password/login
|
||||
BE->>KR: password login flow 제출
|
||||
KR-->>BE: Kratos session_token, session cookie
|
||||
BE->>HY: AcceptLoginRequest(login_challenge, subject)
|
||||
HY-->>BE: redirectTo 반환
|
||||
BE-->>UF: redirectTo 반환
|
||||
UF->>RP: OIDC callback으로 이동
|
||||
```
|
||||
|
||||
### 3.2 Login Code
|
||||
|
||||
Login Code는 Kratos의 `method=code` 기반 로그인이다. 사용자는 이메일 또는 전화번호 등으로 전달받은 코드를 입력하고, Backend는 Kratos login flow에 해당 코드를 제출한다.
|
||||
@@ -61,6 +130,33 @@ Login Code는 Kratos의 `method=code` 기반 로그인이다. 사용자는 이
|
||||
|
||||
검증 성공 시 Kratos는 세션 토큰을 발급한다. Baron은 이 세션 토큰을 기준으로 UserFront 세션을 구성하거나, OIDC 흐름이 있으면 Hydra login accept로 이어간다.
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant UF as UserFront
|
||||
participant BE as Baron Backend
|
||||
participant KR as Ory Kratos
|
||||
participant HY as Ory Hydra
|
||||
|
||||
User->>UF: 로그인 ID 입력
|
||||
UF->>BE: 코드 로그인 시작 요청
|
||||
BE->>KR: method=code login flow 시작
|
||||
KR-->>User: 이메일/SMS 등으로 인증 코드 전달
|
||||
User->>UF: 인증 코드 입력
|
||||
UF->>BE: POST /api/v1/auth/login/code/verify
|
||||
BE->>KR: VerifyLoginCode(loginId, flowID, code)
|
||||
KR-->>BE: Kratos session_token
|
||||
alt OIDC login_challenge 있음
|
||||
BE->>HY: AcceptLoginRequest(login_challenge, subject)
|
||||
HY-->>BE: redirectTo 반환
|
||||
else 일반 UserFront 로그인
|
||||
BE-->>UF: sessionJwt 반환
|
||||
end
|
||||
```
|
||||
|
||||
## 4. 비표준 방식 3개
|
||||
|
||||
비표준 방식은 Kratos/Hydra의 표준 프로토콜만으로 끝나지 않고, Baron Backend가 `pendingRef`, Redis 상태, 폴링, 교차 기기 승인 같은 추가 상태 머신을 운영한다.
|
||||
@@ -90,6 +186,33 @@ Enchanted Link는 사용자가 로그인 ID를 입력하면 Backend가 로그인
|
||||
|
||||
이 방식의 특징은 인증 요청 기기와 승인 기기가 분리될 수 있다는 점이다. 따라서 "누가 요청했는지"와 "누가 승인했는지"를 반드시 분리해서 기록해야 한다.
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant UF as 요청 브라우저
|
||||
participant BE as Baron Backend
|
||||
participant Redis as Redis
|
||||
participant Approver as 링크/코드 승인 기기
|
||||
|
||||
User->>UF: 로그인 ID 입력
|
||||
UF->>BE: POST /api/v1/auth/enchanted-link/init
|
||||
BE->>Redis: pendingRef/status=pending 저장
|
||||
BE-->>User: 로그인 링크 또는 코드 전달
|
||||
loop 요청 브라우저 폴링
|
||||
UF->>BE: POST /api/v1/auth/enchanted-link/poll
|
||||
BE->>Redis: pendingRef 상태 조회
|
||||
BE-->>UF: authorization_pending
|
||||
end
|
||||
User->>Approver: 링크 열기 또는 코드 검증
|
||||
Approver->>BE: magic-link/code verify
|
||||
BE->>Redis: pendingRef/status=approved, sessionJwt 저장
|
||||
UF->>BE: poll 재시도
|
||||
BE-->>UF: sessionJwt 반환
|
||||
```
|
||||
|
||||
### 4.2 Magic Link Verify
|
||||
|
||||
Magic Link Verify는 전달된 토큰을 검증하여 로그인 또는 승인만 수행하는 흐름이다.
|
||||
@@ -113,6 +236,31 @@ Magic Link Verify는 전달된 토큰을 검증하여 로그인 또는 승인만
|
||||
|
||||
이 방식은 사용자가 링크를 누르는 위치에 따라 동작이 달라질 수 있다. 예를 들어 같은 브라우저에서 바로 로그인할 수도 있고, 다른 기기에서 원래 요청만 승인할 수도 있다.
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant BE as Baron Backend
|
||||
participant Redis as Redis
|
||||
participant UF as UserFront
|
||||
participant Req as 원래 요청 브라우저
|
||||
|
||||
BE->>Redis: token -> loginId/pendingRef 저장
|
||||
BE-->>User: Magic Link 전달
|
||||
User->>UF: Magic Link 클릭
|
||||
UF->>BE: POST /api/v1/auth/magic-link/verify
|
||||
BE->>Redis: token 조회 및 만료 확인
|
||||
alt verifyOnly=true
|
||||
BE->>Redis: 원래 pendingRef 승인 처리
|
||||
Req->>BE: poll
|
||||
BE-->>Req: sessionJwt 또는 승인 결과 반환
|
||||
else 현재 브라우저 로그인
|
||||
BE-->>UF: token/sessionJwt 반환
|
||||
end
|
||||
```
|
||||
|
||||
### 4.3 QR 로그인
|
||||
|
||||
QR 로그인은 요청 기기에서 QR을 표시하고, 이미 로그인된 승인 기기에서 QR을 스캔하여 요청 기기를 로그인시키는 흐름이다.
|
||||
@@ -139,6 +287,32 @@ QR 로그인은 요청 기기에서 QR을 표시하고, 이미 로그인된 승
|
||||
|
||||
QR 로그인은 편의성이 높지만, 타인이 QR을 띄우고 사용자가 실수로 승인하는 상황을 반드시 방어해야 한다.
|
||||
|
||||
그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Web as 요청 기기
|
||||
participant BE as Baron Backend
|
||||
participant Redis as Redis
|
||||
participant Mobile as 승인 기기
|
||||
|
||||
Web->>BE: POST /api/v1/auth/qr/init
|
||||
BE->>Redis: pendingRef/status=pending 저장
|
||||
BE-->>Web: qrCode, pendingRef 반환
|
||||
loop QR 상태 폴링
|
||||
Web->>BE: POST /api/v1/auth/qr/poll
|
||||
BE->>Redis: pendingRef 상태 조회
|
||||
BE-->>Web: authorization_pending
|
||||
end
|
||||
Mobile->>Web: QR 스캔
|
||||
Mobile->>BE: POST /api/v1/auth/qr/approve
|
||||
BE->>BE: 승인 기기의 Kratos 세션 확인
|
||||
BE->>Redis: pendingRef/status=approved, approverSubject 저장
|
||||
Web->>BE: poll 재시도
|
||||
BE-->>Web: sessionJwt 반환
|
||||
```
|
||||
|
||||
## 5. 비표준 방식의 공통 위험 지점
|
||||
|
||||
비표준 방식의 핵심 위험은 "요청 주체"와 "승인 주체"가 다를 수 있다는 데 있다.
|
||||
@@ -390,6 +564,90 @@ Baron SSO의 로그인 방식은 표준 2개와 비표준 3개로 정리할 수
|
||||
|
||||
`#1247`, `#1248`, `#1241`에서 제안된 Headless 폰번호 로그인은 다음 흐름을 가진다.
|
||||
|
||||
전체 흐름을 그림으로 보면 다음과 같다.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant RP as RP/시스템
|
||||
participant GW as Nginx Gateway
|
||||
participant BE as Baron Backend
|
||||
participant Redis as Redis
|
||||
participant OK as Oathkeeper
|
||||
participant KR as Ory Kratos
|
||||
participant HY as Ory Hydra
|
||||
|
||||
User->>RP: 전화번호 입력 후 로그인 요청
|
||||
RP->>GW: POST /api/v1/auth/headless/phone/login
|
||||
Note over RP,GW: body 예시<br/>client_id=headless-phone-client<br/>phoneNumber=010-1234-5678<br/>login_challenge=abc...<br/>client_assertion=private_key_jwt
|
||||
GW->>BE: /api 경로를 Backend로 proxy
|
||||
BE->>BE: RP 서명 검증 및 전화번호 정규화
|
||||
Note over BE: phoneNumber 정규화<br/>010-1234-5678 -> +821012345678
|
||||
BE->>Redis: phone pending/interception flag 저장
|
||||
Note over BE,Redis: key 예시<br/>prefixLoginCodePhonePending:+821012345678<br/>TTL=5m
|
||||
BE->>KR: InitiateLinkLogin(phoneNumber)
|
||||
KR-->>BE: flowID 반환
|
||||
BE->>Redis: phoneNumber -> flowID 저장
|
||||
Note over BE,Redis: key 예시<br/>prefixLoginCode:+821012345678 -> flowID
|
||||
KR->>BE: Courier SMS 발송 웹훅 호출
|
||||
Note over KR,BE: POST /api/v1/auth/webhooks/kratos-courier<br/>recipient=+821012345678<br/>template_data.login_code=123456
|
||||
BE->>Redis: interception flag 확인
|
||||
BE->>BE: Courier payload에서 login_code 추출
|
||||
BE->>KR: VerifyLoginCode(phoneNumber, flowID, login_code)
|
||||
KR-->>BE: Kratos session_token/session_id 발급
|
||||
BE->>HY: AcceptLoginRequest(login_challenge, subject)
|
||||
HY-->>BE: redirectTo 반환
|
||||
BE-->>GW: redirectTo, sessionId, status 반환
|
||||
GW-->>RP: JSON 응답 전달
|
||||
RP->>GW: redirectTo 따라 /oidc/oauth2/auth 계속
|
||||
GW->>OK: /oidc 경로를 Oathkeeper로 proxy
|
||||
OK->>HY: strip /oidc 후 Hydra public으로 전달
|
||||
```
|
||||
|
||||
이때 흐름 속 주요 데이터 예시는 다음과 같다.
|
||||
|
||||
RP가 Backend로 보내는 요청:
|
||||
|
||||
```json
|
||||
{
|
||||
"client_id": "headless-phone-client",
|
||||
"client_assertion": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"phoneNumber": "010-1234-5678",
|
||||
"login_challenge": "b59a857064ef4827940bb..."
|
||||
}
|
||||
```
|
||||
|
||||
Backend가 Redis에 저장하는 임시 키:
|
||||
|
||||
```text
|
||||
prefixLoginCodePhonePending:+821012345678 = interception_flag
|
||||
prefixLoginCode:+821012345678 = <kratos-flow-id>
|
||||
TTL = 5m
|
||||
```
|
||||
|
||||
Kratos Courier가 Backend로 보내는 발송 요청의 핵심 데이터:
|
||||
|
||||
```json
|
||||
{
|
||||
"recipient": "+821012345678",
|
||||
"template_type": "login_code",
|
||||
"template_data": {
|
||||
"login_code": "123456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Backend가 RP로 반환하는 성공 응답:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"redirectTo": "https://sso.example.com/oidc/oauth2/auth?login_verifier=...",
|
||||
"sessionId": "a0f8dc4e-9400-4785-802c-..."
|
||||
}
|
||||
```
|
||||
|
||||
### 10.1 단계별 데이터 이동
|
||||
|
||||
#### Step 1. 인가 및 전화번호 식별
|
||||
|
||||
Reference in New Issue
Block a user