From 8ce1410728b478c09b15581ad0dd366473a3c8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=ED=98=95=EC=84=9D?= Date: Tue, 23 Jun 2026 15:38:42 +0900 Subject: [PATCH] =?UTF-8?q?Add=20=ED=9C=B4=EB=8C=80=ED=8F=B0=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=8B=A8=EB=8F=85=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EC=88=A0=20=EA=B2=80=ED=86=A0=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EA=B2=80=ED=86=A0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๊ฒ€ํ† .md | 477 ++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๊ฒ€ํ† .md diff --git a/ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๊ฒ€ํ† .md b/ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๊ฒ€ํ† .md new file mode 100644 index 0000000..662311b --- /dev/null +++ b/ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๊ฒ€ํ† .md @@ -0,0 +1,477 @@ +# ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ ๊ธฐ์ˆ  ๊ฒ€ํ†  ๋ฐ ๊ตฌํ˜„ ๋Œ€์•ˆ + +์ž‘์„ฑ์ผ: 2026-06-23 + +## 1. ๋ชฉ์  + +๋ณธ ๋ฌธ์„œ๋Š” Baron SSO์—์„œ ๋…ผ์˜๋œ "ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋งŒ ์ž…๋ ฅํ•˜๋ฉด ์ฆ‰์‹œ ๋กœ๊ทธ์ธ๋˜๋Š” ๋ฐฉ์‹"์˜ ๊ธฐ์ˆ  ๊ตฌํ˜„์•ˆ์„ ๊ฒ€ํ† ํ•˜๊ณ , ๊ฐ ํ”„๋กœ์„ธ์Šค์—์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜๋Š”์ง€, ํ•ด๋‹น ๋‹จ๊ณ„๊ฐ€ ํ‘œ์ค€ ๊ธฐ์ˆ ์— ํ•ด๋‹นํ•˜๋Š”์ง€, ๋น„ํ‘œ์ค€ ๋˜๋Š” ์šฐํšŒ ๊ตฌํ˜„์ด๋ผ๋ฉด ์–ด๋–ค ์œ„ํ—˜์ด ์žˆ๋Š”์ง€ ์ •๋ฆฌํ•œ๋‹ค. + +๊ฒ€ํ†  ๋ฒ”์œ„๋Š” ๋‹ค์Œ ์ด์Šˆ์™€ ํ˜„์žฌ ์ฝ”๋“œ ๊ธฐ์ค€ ๊ตฌํ˜„ ํ”์ ์„ ํฌํ•จํ•œ๋‹ค. + +- Gitea Issue #1241: `[Feature/API] Headless ํฐ๋ฒˆํ˜ธ ์ „์šฉ ๋กœ๊ทธ์ธ API ๊ตฌํ˜„` +- Gitea Issue #1245: `[Research/Design] ๊ธฐ์กด QR์ฝ”๋“œ/๋งํฌ ๋กœ๊ทธ์ธ ์•„ํ‚คํ…์ฒ˜ ๋ถ„์„ ๋ฐ Headless ํฐ๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ ์ ์šฉ ์„ค๊ณ„์•ˆ` +- Gitea Issue #1247: `[Architecture/Design] Baron Backend โ†” Ory Kratos ์ธํ„ฐ๋ž™์…˜ ๋ถ„์„ ๋ฐ ํฐ๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ ์„ธ์…˜ ๋ฐœ๊ธ‰ ์•„ํ‚คํ…์ฒ˜` +- Gitea Issue #1248: `[Feature] ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ์ž…๋ ฅ๋งŒ์œผ๋กœ ์ฆ‰์‹œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (์ธ์ฆ ์ฝ”๋“œ ์—†๋Š” Passwordless)` +- Gitea Issue #1252: `[Feature] userfront ๋‚ด ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ์ž…๋ ฅ ์ „์šฉ ์ดˆ๊ฐ„๊ฒฐ ๋กœ๊ทธ์ธ ํ™”๋ฉด/๋ชจ๋“œ ๊ตฌํ˜„` +- Gitea Issue #1258: `[Feature] userfront ๋‚ด ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ์ „์šฉ ๋…๋ฆฝ ๋ผ์šฐํŠธ(connect) ์‹ ์„ค ๋ฐ ๋น„์ธ๊ฐ€/๋กœ๊ทธ์•„์›ƒ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์ „๋ฉด ์ผ๊ด„ ์ „ํ™˜` + +## 2. ๊ฒฐ๋ก  ์š”์•ฝ + +ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋งŒ์œผ๋กœ ๋กœ๊ทธ์ธ์‹œํ‚ค๋Š” ๋ฐฉ์‹์€ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ด€์ ์—์„œ ํ‘œ์ค€ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์œผ๋กœ ๋ณด๊ธฐ ์–ด๋ ต๋‹ค. ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋Š” ์‚ฌ์šฉ์ž ์‹๋ณ„์ž(identifier)์ผ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๊ทธ ์ž์ฒด๋กœ ์‚ฌ์šฉ์ž ๋ณธ์ธ์„ฑ ๋˜๋Š” ๋ฒˆํ˜ธ ์†Œ์œ ๋ฅผ ์ฆ๋ช…ํ•˜๋Š” ์ธ์ฆ ์ˆ˜๋‹จ(authenticator)์ด ์•„๋‹ˆ๋‹ค. + +ํ˜„์žฌ ๋…ผ์˜๋œ ๊ตฌํ˜„์•ˆ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋กœ ๋‚˜๋‰œ๋‹ค. + +| ๋ฐฉ์‹ | ์š”์•ฝ | ํ‘œ์ค€์„ฑ ํŒ๋‹จ | ๊ถŒ์žฅ ์—ฌ๋ถ€ | +| --- | --- | --- | --- | +| Kratos Admin ์„ธ์…˜ ์ง์ ‘ ๋ฐœ๊ธ‰ | ๋ฒˆํ˜ธ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์€ ๋’ค ๊ด€๋ฆฌ์ž API๋กœ ์„ธ์…˜ ์ƒ์„ฑ | ์ œํ’ˆ ๋‚ด๋ถ€ Admin API ์‚ฌ์šฉ์ผ ์ˆ˜ ์žˆ์œผ๋‚˜ ์‚ฌ์šฉ์ž ์ธ์ฆ ์šฐํšŒ | ๋น„๊ถŒ์žฅ | +| Courier ์ธํ„ฐ์…‰ํŠธ/์ฝ”๋“œ ์ž๋™ ์ œ์ถœ | Kratos code login์„ ์‹œ์ž‘ํ•˜๊ณ  ๋ฐœ์†ก ์ฝ”๋“œ๋ฅผ ๋ฐฑ์—”๋“œ๊ฐ€ ๊ฐ€๋กœ์ฑ„ ์ œ์ถœ | Kratos code login ์ž์ฒด๋Š” ํ‘œ์ค€ ํ”Œ๋กœ์šฐ์ด๋‚˜ ์ฝ”๋“œ ์†Œ์œ ์ž ํ™•์ธ์„ ์ œ๊ฑฐํ•œ ์šฐํšŒ | ๋น„๊ถŒ์žฅ | +| Headless client assertion + ํฐ๋ฒˆํ˜ธ | RP ํด๋ผ์ด์–ธํŠธ JWT๋Š” ๊ฒ€์ฆํ•˜๊ณ  ์‚ฌ์šฉ์ž๋Š” ๋ฒˆํ˜ธ๋งŒ ์ž…๋ ฅ | ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์€ ํ‘œ์ค€ JWT ๊ธฐ๋ฐ˜์ผ ์ˆ˜ ์žˆ์œผ๋‚˜ ์‚ฌ์šฉ์ž ์ธ์ฆ์€ ๋ถ€์žฌ | ์ œํ•œ์ /๋ณด์™„ ํ•„์ˆ˜ | + +๋ณด์™„ ์—†์ด ์šด์˜ํ•˜๋ฉด ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ์•„๋Š” ์‚ฌ๋žŒ ๋˜๋Š” ๋‚ด๋ถ€ ์‹œ์Šคํ…œ ์ ‘๊ทผ์ž๊ฐ€ ํƒ€์ธ์˜ ์„ธ์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๊ฐ€ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ตœ์†Œํ•œ SMS OTP, FIDO2/WebAuthn, ๊ธฐ๊ธฐ ๋ฐ”์ธ๋”ฉ, ์‚ฌ์ „ ๋“ฑ๋ก ๋‹จ๋ง ์ธ์ฆ, ๊ด€๋ฆฌ์ž ์Šน์ธ, ๋„คํŠธ์›Œํฌ ์ œํ•œ ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์˜ ์‹ค์งˆ์ ์ธ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋˜๋Š” ํ™˜๊ฒฝ ์ธ์ฆ์ด ํ•„์š”ํ•˜๋‹ค. + +## 3. ํ˜„์žฌ ์ฝ”๋“œ ๊ธฐ์ค€ ์ƒํƒœ + +ํ˜„์žฌ ์ฝ”๋“œ์—์„œ ํ™•์ธ๋˜๋Š” ๊ด€๋ จ ์œ„์น˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. + +| ์˜์—ญ | ํŒŒ์ผ | ํ™•์ธ ๋‚ด์šฉ | +| --- | --- | --- | +| Backend route | `backend/cmd/server/main.go` | `/api/v1/auth/headless/link/init`, `/api/v1/auth/headless/link/poll` ๋“ฑ๋ก | +| Headless link init/poll | `backend/internal/handler/auth_handler.go` | `HeadlessLinkInit`, `HeadlessLinkPoll`, `startHeadlessPhoneLink` ๊ตฌํ˜„ | +| Kratos code login ์‹œ์ž‘ | `backend/internal/service/ory_service.go` | `InitiateLinkLogin`์—์„œ Kratos code login flow ์‹œ์ž‘ | +| Kratos code ์ œ์ถœ | `backend/internal/service/ory_service.go` | `VerifyLoginCode`์—์„œ `self-service/login?flow=...`์— code ์ œ์ถœ | +| Courier relay | `backend/internal/handler/auth_handler.go` | `HandleKratosCourierRelay`์—์„œ login code๋ฅผ Redis์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ QR ํ๋ฆ„์„ ์ž๋™ ๊ฒ€์ฆ | +| Admin ์„ธ์…˜ ์ง์ ‘ ๋ฐœ๊ธ‰ ํ•จ์ˆ˜ | `backend/internal/handler/auth_handler.go` | `issueKratosSession` ํ•จ์ˆ˜๋Š” ์žˆ์œผ๋‚˜ ํ˜„์žฌ ๊ฒ€์ƒ‰ ๊ธฐ์ค€ ํ˜ธ์ถœ๋ถ€๋Š” ์—†์Œ | +| OryProvider IssueSession | `backend/internal/service/ory_service.go` | `IssueSession`์€ `domain.ErrNotSupported` ๋ฐ˜ํ™˜ | +| Userfront phone-only route | `userfront/lib/main.dart`, `userfront/lib/features/auth/presentation/login_screen.dart` | ํ˜„์žฌ ๊ฒ€์ƒ‰ ๊ธฐ์ค€ `connect`, `phone_only`, `phone-login` ์ง์ ‘ ๊ตฌํ˜„์€ ํ™•์ธ๋˜์ง€ ์•Š์Œ | + +๋”ฐ๋ผ์„œ ์ด์Šˆ #1248์—์„œ ์ œ์•ˆ๋œ `POST /api/v1/auth/phone-login` ์ง์ ‘ API๋Š” ํ˜„์žฌ ์ฝ”๋“œ์ƒ ํ™œ์„ฑ ๋ผ์šฐํŠธ๋กœ ํ™•์ธ๋˜์ง€ ์•Š๋Š”๋‹ค. ์‹ค์ œ๋กœ ๊ฐ€๊นŒ์šด ๊ตฌํ˜„์€ `/api/v1/auth/headless/link/init` ๋ฐ `/poll` ๊ธฐ๋ฐ˜์˜ headless link ํ๋ฆ„์ด๋‹ค. + +## 4. ํ”„๋กœ์„ธ์Šค๋ณ„ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐ ํ‘œ์ค€์„ฑ ๊ฒ€ํ†  + +### 4.1 ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ์ž…๋ ฅ ๋ฐ ์ •๊ทœํ™” + +ํ”„๋กœ์„ธ์Šค: + +1. ์‚ฌ์šฉ์ž๊ฐ€ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. +2. Userfront ๋˜๋Š” Backend๊ฐ€ ํ•˜์ดํ”ˆ, ๊ณต๋ฐฑ ๋“ฑ์„ ์ œ๊ฑฐํ•œ๋‹ค. +3. Backend๊ฐ€ `normalizePhoneForLoginID` ๊ณ„์—ด ๋กœ์ง์œผ๋กœ E.164์— ๊ฐ€๊นŒ์šด ๋กœ๊ทธ์ธ ์‹๋ณ„์ž๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. +4. ๋ณ€ํ™˜๋œ ๊ฐ’์œผ๋กœ Kratos identity ๋˜๋Š” ๋กœ์ปฌ ์›์žฅ์„ ์กฐํšŒํ•œ๋‹ค. + +์ƒ์„ฑ ๋˜๋Š” ๋ณ€ํ™˜๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์˜ˆ์‹œ | ์ƒ์„ฑ ์ฃผ์ฒด | ์šฉ๋„ | +| --- | --- | --- | --- | +| Raw phone number | `010-1234-5678` | ์‚ฌ์šฉ์ž/Userfront | ์ž…๋ ฅ๊ฐ’ | +| Sanitized phone | `01012345678` | Backend | ์ •๊ทœํ™” ์ „์ฒ˜๋ฆฌ | +| Login ID | `+821012345678` | Backend | Kratos credentials identifier ์กฐํšŒ | +| Identity ID | UUID | Kratos/Admin ์กฐํšŒ | ์ตœ์ข… subject ํ›„๋ณด | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- ์ „ํ™”๋ฒˆํ˜ธ E.164 ์ •๊ทœํ™”๋Š” ํ‘œ์ค€์ ์ธ ์‹๋ณ„์ž ์ •๊ทœํ™”์— ํ•ด๋‹นํ•œ๋‹ค. +- ๊ทธ๋Ÿฌ๋‚˜ ์ „ํ™”๋ฒˆํ˜ธ ์ž…๋ ฅ๋งŒ์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๋Š” ๊ฒƒ์€ ํ‘œ์ค€ ์ธ์ฆ ๊ธฐ์ˆ ์ด ์•„๋‹ˆ๋‹ค. +- ์ด ๋‹จ๊ณ„๋Š” "์‹๋ณ„" ๋‹จ๊ณ„์ด์ง€ "์ธ์ฆ" ๋‹จ๊ณ„๊ฐ€ ์•„๋‹ˆ๋‹ค. + +์œ„ํ—˜: + +- ์ „ํ™”๋ฒˆํ˜ธ๋Š” ๊ณต์œ ๋˜๊ฑฐ๋‚˜ ์œ ์ถœ๋˜๊ธฐ ์‰ฝ๋‹ค. +- ๋ฒˆํ˜ธ ์ž…๋ ฅ ์„ฑ๊ณต/์‹คํŒจ ์‘๋‹ต์ด ๋‹ค๋ฅด๋ฉด ์‚ฌ์šฉ์ž ์กด์žฌ ์—ฌ๋ถ€ ์—ด๋žŒ(user enumeration)์ด ๊ฐ€๋Šฅํ•˜๋‹ค. +- ๊ฐ€์ž…์ž ๋ฒˆํ˜ธ ์žฌํ• ๋‹น, ํ‡ด์‚ฌ์ž ๋ฒˆํ˜ธ ํšŒ์ˆ˜, ๊ฐ€์กฑ/๊ณต์šฉ ๋‹จ๋ง ์‚ฌ์šฉ ๊ฐ™์€ ์‹ค์ œ ์šด์˜ ๋ฌธ์ œ๊ฐ€ ์ธ์ฆ ์‹คํŒจ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- ์กด์žฌ ์—ฌ๋ถ€ ์‘๋‹ต์„ ์ผ๋ฐ˜ํ™”ํ•œ๋‹ค. +- ๋ฒˆํ˜ธ๋Š” ๋ฐ˜๋“œ์‹œ ์†Œ์œ  ์ฆ๋ช… ๋‹จ๊ณ„๋กœ ์ด์–ด์ง€๊ฒŒ ํ•œ๋‹ค. +- ์‚ฌ์šฉ์ž ์›์žฅ์—๋Š” E.164, ๊ตญ๊ฐ€ ์ฝ”๋“œ, ์›๋ณธ ํ‘œ์‹œ๊ฐ’์„ ๋ถ„๋ฆฌ ์ €์žฅํ•œ๋‹ค. + +### 4.2 ์‚ฌ์šฉ์ž ์กด์žฌ ์—ฌ๋ถ€ ์กฐํšŒ + +ํ”„๋กœ์„ธ์Šค: + +1. Backend๊ฐ€ ์ •๊ทœํ™”๋œ ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•ด `UserExists` ๋˜๋Š” Kratos Admin identity ์กฐํšŒ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. +2. ์‚ฌ์šฉ์ž๊ฐ€ ์—†์œผ๋ฉด ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. +3. ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ์œผ๋ฉด ๋‹ค์Œ ์„ธ์…˜ ์ƒ์„ฑ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ํ•œ๋‹ค. + +์ƒ์„ฑ ๋˜๋Š” ์กฐํšŒ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ/์กฐํšŒ ์ฃผ์ฒด | ์šฉ๋„ | +| --- | --- | --- | +| credentials identifier | Backend | Kratos identity ๊ฒ€์ƒ‰ ์กฐ๊ฑด | +| identity id | Kratos | OIDC subject ๋˜๋Š” ์„ธ์…˜ ๋Œ€์ƒ | +| user exists boolean | Backend | ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์—ฌ๋ถ€ | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- IdP ์›์žฅ์—์„œ identifier๋กœ identity๋ฅผ ์กฐํšŒํ•˜๋Š” ํ–‰์œ„ ์ž์ฒด๋Š” ์ผ๋ฐ˜์ ์ด๋‹ค. +- ๋‹ค๋งŒ ์ด ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋กœ๊ทธ์ธ ์„ฑ๊ณต์„ ํ—ˆ์šฉํ•˜๋ฉด ์ธ์ฆ ์š”์†Œ๊ฐ€ ์—†๋‹ค. + +์œ„ํ—˜: + +- ๊ณต๊ฒฉ์ž๊ฐ€ ์ „ํ™”๋ฒˆํ˜ธ ๋ชฉ๋ก์œผ๋กœ ๋“ฑ๋ก ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. +- ๋‚ด๋ถ€ API๊ฐ€ ์—ด๋ ค ์žˆ์œผ๋ฉด ๋Œ€๋Ÿ‰ ์Šค์บ๋‹์ด ๊ฐ€๋Šฅํ•˜๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- rate limit, IP ์ œํ•œ, WAF ๋ฃฐ, device attestation ๋“ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค. +- `404 User not registered` ๋Œ€์‹  ์ผ๋ฐ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. +- ๊ฐ์‚ฌ ๋กœ๊ทธ์—๋Š” ์ž…๋ ฅ ์›๋ฌธ์ด ์•„๋‹Œ ๋งˆ์Šคํ‚น๋œ ๋ฒˆํ˜ธ์™€ ํ•ด์‹œ๋ฅผ ๋‚จ๊ธด๋‹ค. + +### 4.3 Kratos Admin ์„ธ์…˜ ์ง์ ‘ ๋ฐœ๊ธ‰ ๋ฐฉ์‹ + +์ด์Šˆ #1248 ์ดˆ๋ฐ˜ ์„ค๊ณ„์—์„œ๋Š” ๋‹ค์Œ ํ๋ฆ„์ด ์ œ์•ˆ๋˜์—ˆ๋‹ค. + +1. `POST /api/v1/auth/phone-login` ์š”์ฒญ์„ ๋ฐ›๋Š”๋‹ค. +2. Backend๊ฐ€ ์ „ํ™”๋ฒˆํ˜ธ๋กœ Kratos identity ID๋ฅผ ์ฐพ๋Š”๋‹ค. +3. Backend๊ฐ€ Kratos Admin API๋กœ ํ•ด๋‹น identity์— ๋Œ€ํ•œ ์„ธ์…˜์„ ์ง์ ‘ ์ƒ์„ฑํ•œ๋‹ค. +4. ์„ธ์…˜ ํ† ํฐ์„ Userfront์— ๋ฐ˜ํ™˜ํ•œ๋‹ค. + +์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ ์ฃผ์ฒด | ์ €์žฅ/์ „๋‹ฌ ์œ„์น˜ | +| --- | --- | --- | +| Kratos session id | Kratos | Kratos DB, Backend ์‘๋‹ต ์ฒ˜๋ฆฌ | +| Kratos session token | Kratos | Backend ์‘๋‹ต, Userfront token store | +| authenticated_at | Kratos | ์„ธ์…˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ | +| AAL | Backend ์š”์ฒญ/Kratos | ์„ธ์…˜ ๋ณด์ฆ ์ˆ˜์ค€ | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- ๊ด€๋ฆฌ์ž API๋ฅผ ํ†ตํ•œ ์„ธ์…˜ ์ƒ์„ฑ ๊ธฐ๋Šฅ ์ž์ฒด๊ฐ€ ์ œํ’ˆ ๊ธฐ๋Šฅ์ผ ์ˆ˜๋Š” ์žˆ๋‹ค. +- ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋ฌด ์ธ์ฆ ์ˆ˜๋‹จ๋„ ์ œ์‹œํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์œผ๋กœ ์„ธ์…˜์„ ๋งŒ๋“ค์–ด ์ฃผ๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž ์ธ์ฆ ํ‘œ์ค€ ํ๋ฆ„์ด ์•„๋‹ˆ๋‹ค. +- OAuth2/OIDC ๊ด€์ ์—์„œ๋„ ์ธ์ฆ ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋ณธ์ธ์„ฑ์„ ํ™•์ธํ•˜์ง€ ์•Š์€ ์ฑ„ login challenge๋ฅผ acceptํ•˜๋ฉด ์ธ์ฆ ์˜๋ฏธ๊ฐ€ ํ›ผ์†๋œ๋‹ค. + +ํ˜„์žฌ ์ฝ”๋“œ ์ƒํƒœ: + +- `issueKratosSession(ctx, identityID)` ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•œ๋‹ค. +- ํ˜„์žฌ ๊ฒ€์ƒ‰ ๊ธฐ์ค€ ์ด ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ๋ถ€๋Š” ํ™•์ธ๋˜์ง€ ์•Š๋Š”๋‹ค. +- `OryProvider.IssueSession`์€ `domain.ErrNotSupported`๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + +์œ„ํ—˜: + +- Backend ๊ถŒํ•œ ํƒˆ์ทจ ์‹œ ์ž„์˜ ์‚ฌ์šฉ์ž ์„ธ์…˜ ๋ฐœ๊ธ‰์ด ๊ฐ€๋Šฅํ•˜๋‹ค. +- ๊ฐ์‚ฌ ๋กœ๊ทธ์—๋Š” "์ •์ƒ ๋กœ๊ทธ์ธ"์ฒ˜๋Ÿผ ๋‚จ์„ ์ˆ˜ ์žˆ์œผ๋‚˜ ์‹ค์ œ ์‚ฌ์šฉ์ž ํ–‰์œ„๊ฐ€ ์•„๋‹ˆ๋‹ค. +- AAL1๋กœ ํ‘œ์‹œ๋˜๋”๋ผ๋„ ์‹ค์งˆ์  authenticator๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ณด์ฆ ์ˆ˜์ค€ ํ•ด์„์ด ์™œ๊ณก๋œ๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- ์šด์˜ ๋กœ๊ทธ์ธ ๊ฒฝ๋กœ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ํ•„์š”ํ•œ ๊ฒฝ์šฐ break-glass ๊ด€๋ฆฌ์ž ์ง€์› ๊ธฐ๋Šฅ์œผ๋กœ๋งŒ ์ œํ•œํ•˜๊ณ , ๋ณ„๋„ ์Šน์ธ/ํ‹ฐ์ผ“/๊ฐ์‚ฌ/๋งŒ๋ฃŒ ์ •์ฑ…์„ ๋‘”๋‹ค. +- ๊ด€๋ฆฌ์ž ์„ธ์…˜ ๋ฐœ๊ธ‰ ๊ธฐ๋Šฅ์€ feature flag๋กœ ๊ธฐ๋ณธ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค. + +### 4.4 Courier ์ธํ„ฐ์…‰ํŠธ ๋ฐ Code Claim ๋ฐฉ์‹ + +์ด์Šˆ #1248 ํ›„๋ฐ˜ ์ฝ”๋ฉ˜ํŠธ ๋ฐ #1245, #1247์—์„œ๋Š” ์‹ค์ œ ๊ตฌํ˜„์— ๊ฐ€๊นŒ์šด ๋ฐฉ์‹์œผ๋กœ Courier ์ธํ„ฐ์…‰ํŠธ๊ฐ€ ์„ค๋ช…๋˜์–ด ์žˆ๋‹ค. + +ํ”„๋กœ์„ธ์Šค: + +1. Backend๊ฐ€ ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Kratos code login flow๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค. +2. Kratos๊ฐ€ SMS ๋˜๋Š” email courier ๋ฐœ์†ก์„ ์‹œ๋„ํ•œ๋‹ค. +3. Kratos Courier Webhook์ด Backend์˜ relay endpoint๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. +4. Backend๊ฐ€ `template_data.login_code`๋ฅผ ์ถ”์ถœํ•œ๋‹ค. +5. Backend๊ฐ€ ์ถ”์ถœํ•œ code๋ฅผ ์ฆ‰์‹œ Kratos `self-service/login?flow=...`์— ์ œ์ถœํ•œ๋‹ค. +6. Kratos๊ฐ€ session token์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค. +7. Backend๊ฐ€ Redis pending session์„ success๋กœ ๊ฐฑ์‹ ํ•œ๋‹ค. +8. Poll ๋˜๋Š” ํ›„์† ์ฒ˜๋ฆฌ์—์„œ Userfront/RP๊ฐ€ ๋กœ๊ทธ์ธ ์™„๋ฃŒ๋ฅผ ํ™•์ธํ•œ๋‹ค. + +์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋‹จ๊ณ„ | ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ ์ฃผ์ฒด | ์ €์žฅ ์œ„์น˜ | +| --- | --- | --- | --- | +| flow init | flow id | Kratos | Redis `login_code_flow:{loginID}` | +| pending ์ƒ์„ฑ | pendingRef | Backend | Redis `enchanted_session:{pendingRef}` | +| pending ๋งคํ•‘ | loginID -> pendingRef | Backend | Redis `login_code_pending:{loginID}` | +| courier | login_code | Kratos | Webhook payload | +| code ์ €์žฅ | normalized login code | Backend | Redis `login_code_value:{pendingRef}` | +| code verify | session token, session id | Kratos | Redis session payload ๋˜๋Š” ์‘๋‹ต | +| audit | login event | Backend | Audit DB | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- Kratos code login flow ์ž์ฒด๋Š” IdP์˜ ์ •์ƒ self-service login flow์ด๋‹ค. +- Courier Webhook์„ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœ์†ก ๋Œ€ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ๋„ ์ œํ’ˆ ํ†ตํ•ฉ ๋ฐฉ์‹์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. +- ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜์‹ ํ•œ OTP/SMS๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜์ง€ ์•Š๊ณ , ์„œ๋ฒ„๊ฐ€ ์ฝ”๋“œ๋ฅผ ๊ฐ€๋กœ์ฑ„ ์ž๋™ ์ œ์ถœํ•˜๋Š” ๊ฒƒ์€ OTP์˜ ๋ณธ๋ž˜ ๋ณด์•ˆ ์†์„ฑ์ธ "์‚ฌ์šฉ์ž ์†Œ์œ  ์ฑ„๋„ ํ™•์ธ"์„ ์ œ๊ฑฐํ•œ๋‹ค. +- ๋”ฐ๋ผ์„œ ์ „์ฒด ๋กœ๊ทธ์ธ ๋ฐฉ์‹์€ ํ‘œ์ค€ passwordless/SMS OTP ์ธ์ฆ์œผ๋กœ ๋ณด๊ธฐ ์–ด๋ ต๋‹ค. ํ‘œ์ค€ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•ด ๋น„ํ‘œ์ค€ ์ธ์ฆ ์šฐํšŒ๋ฅผ ๊ตฌ์„ฑํ•œ ํ˜•ํƒœ์— ๊ฐ€๊น๋‹ค. + +์œ„ํ—˜: + +- ์ „ํ™”๋ฒˆํ˜ธ๋งŒ ์•Œ๋ฉด ์„ธ์…˜ ๋ฐœ๊ธ‰๊นŒ์ง€ ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๋‹ค. +- SMS ๋น„์šฉ์„ ์ค„์ด๋Š” ๋Œ€์‹  ์†Œ์œ  ์ฆ๋ช…์ด ์‚ฌ๋ผ์ง„๋‹ค. +- Courier relay๊ฐ€ ๋‚ด๋ถ€ ์ธ์ฆ ์—†์ด ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•˜๋ฉด ์ฝ”๋“œ ์ฃผ์ž… ๋˜๋Š” ํ๋ฆ„ ๊ต๋ž€์ด ๊ฐ€๋Šฅํ•˜๋‹ค. +- Redis pending key ํƒˆ์ทจ ์‹œ ์Šน์ธ ํ๋ฆ„์ด ์˜ค์—ผ๋  ์ˆ˜ ์žˆ๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- Courier relay endpoint๋Š” ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ ๋˜๋Š” mTLS, shared secret, HMAC signature๋กœ ๋ณดํ˜ธํ•œ๋‹ค. +- code๋Š” ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•˜๋˜, ์ €์žฅ์ด ํ•„์š”ํ•˜๋ฉด TTL์„ ๋งค์šฐ ์งง๊ฒŒ ์œ ์ง€ํ•œ๋‹ค. +- ์ž๋™ ์ œ์ถœ์€ ์šด์˜ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ํ…Œ์ŠคํŠธ/๊ฐœ๋ฐœ dry-run์— ํ•œ์ •ํ•œ๋‹ค. +- ์‹ค์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ SMS OTP๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋„๋ก ํ•œ๋‹ค. + +### 4.5 Headless client assertion + ํฐ๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ + +์ด์Šˆ #1241์€ RP๊ฐ€ `client_assertion`์„ ์ œ์ถœํ•˜๊ณ , ์‚ฌ์šฉ์ž๋Š” ์ „ํ™”๋ฒˆํ˜ธ๋งŒ ์ „๋‹ฌํ•˜๋Š” headless API๋ฅผ ์ œ์•ˆํ•œ๋‹ค. + +ํ”„๋กœ์„ธ์Šค: + +1. RP๊ฐ€ `client_id`, `client_assertion`, `phoneNumber`, `login_challenge`๋ฅผ Backend์— ์ œ์ถœํ•œ๋‹ค. +2. Backend๊ฐ€ Hydra login request๋ฅผ ์กฐํšŒํ•œ๋‹ค. +3. Backend๊ฐ€ RP์˜ JWT client assertion์„ JWKS๋กœ ๊ฒ€์ฆํ•œ๋‹ค. +4. Backend๊ฐ€ ์ „ํ™”๋ฒˆํ˜ธ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์กฐํšŒํ•œ๋‹ค. +5. Backend๊ฐ€ ์„ธ์…˜ ๋ฐœ๊ธ‰ ๋˜๋Š” code login flow๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค. +6. Backend๊ฐ€ Hydra `AcceptLoginRequest`๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. +7. RP๋Š” `redirectTo` ๋˜๋Š” OIDC authorization code ํ๋ฆ„์„ ์ด์–ด๊ฐ„๋‹ค. + +์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ ์ฃผ์ฒด | ํ‘œ์ค€ ๊ธฐ์ˆ  ์—ฌ๋ถ€ | +| --- | --- | --- | +| client assertion JWT | RP | OAuth2 JWT client authentication ๊ณ„์—ด | +| JWKS key | RP | JOSE/JWK ํ‘œ์ค€ | +| login challenge | Hydra | Ory Hydra/OIDC ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ | +| Kratos subject | Kratos | IdP subject | +| redirectTo | Hydra | OAuth2/OIDC authorization redirect | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- `client_assertion`๊ณผ JWKS ๊ฒ€์ฆ์€ OAuth2/OIDC์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ‘œ์ค€์ ์ธ ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ ๋ฐฉ์‹์— ๊ฐ€๊น๋‹ค. +- Hydra login challenge, authorization code, PKCE๋Š” ํ‘œ์ค€ OIDC/OAuth2 ๊ธฐ์ˆ ์ด๋‹ค. +- ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ฒ€์ฆ์€ RP ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‹ ๋ขฐ ๊ฐ€๋Šฅํ•œ์ง€๋ฅผ ํ™•์ธํ•  ๋ฟ, ์ตœ์ข… ์‚ฌ์šฉ์ž๊ฐ€ ์ „ํ™”๋ฒˆํ˜ธ ์†Œ์œ ์ž์ž„์„ ์ฆ๋ช…ํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋‹จ๊ณ„๋Š” ์—ฌ์ „ํžˆ ๋น„ํ‘œ์ค€ ๋˜๋Š” ๋ถˆ์ถฉ๋ถ„ํ•œ ์ƒํƒœ๋‹ค. + +์œ„ํ—˜: + +- ์‹ ๋ขฐ๋œ RP๊ฐ€ ์ž˜๋ชป ๊ตฌํ˜„๋˜๊ฑฐ๋‚˜ ์นจํ•ด๋˜๋ฉด ์ž„์˜ ์ „ํ™”๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ ์‹œ๋„๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. +- `client_assertion`์„ ์‚ฌ์šฉ์ž ์ธ์ฆ์œผ๋กœ ์˜คํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค. +- RP๋ณ„ ์ •์ฑ… ํŽธ์ฐจ๊ฐ€ ์ปค์ง€๋ฉด SSO์˜ ์ธ์ฆ ๋ณด์ฆ ์ˆ˜์ค€์ด ๋ถˆ๋ช…ํ™•ํ•ด์ง„๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- headless API๋Š” confidential client๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. +- RP๋ณ„๋กœ `headless_phone_login_enabled` ๊ฐ™์€ ๋ช…์‹œ์  allowlist๋ฅผ ๋‘”๋‹ค. +- ์‚ฌ์šฉ์ž ์ธ์ฆ์€ ๋ณ„๋„๋กœ SMS OTP, WebAuthn, ์•ฑ ํ‘ธ์‹œ ์Šน์ธ, ์‚ฌ์ „ ๋“ฑ๋ก ๋‹จ๋ง ์ฆ๋ช… ์ค‘ ํ•˜๋‚˜๋ฅผ ์š”๊ตฌํ•œ๋‹ค. +- `acr` ๋˜๋Š” `amr` claim์— ์‹ค์ œ ์ธ์ฆ ๋ฐฉ์‹์„ ๋ช…ํ™•ํžˆ ๊ธฐ๋กํ•œ๋‹ค. + +### 4.6 Hydra AcceptLoginRequest ๋ฐ OIDC ์—ฐ๋™ + +ํ”„๋กœ์„ธ์Šค: + +1. Backend๊ฐ€ Hydra login challenge๋ฅผ ์กฐํšŒํ•œ๋‹ค. +2. ์ธ์ฆ ์™„๋ฃŒ๋กœ ํŒ๋‹จํ•œ subject๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. +3. Backend๊ฐ€ `AcceptLoginRequest(login_challenge, subject)`๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. +4. Hydra๊ฐ€ RP๋กœ ๋Œ์•„๊ฐˆ `redirectTo`๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. +5. RP๋Š” authorization code ๋˜๋Š” token ๊ตํ™˜์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. + +์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ ์ฃผ์ฒด | ์šฉ๋„ | +| --- | --- | --- | +| subject | Backend/Kratos | OIDC user identifier | +| redirectTo | Hydra | RP redirect | +| authorization code | Hydra | token endpoint ๊ตํ™˜ | +| id/access/refresh token | Hydra | RP ์„ธ์…˜ ์ˆ˜๋ฆฝ | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- Hydra์˜ OAuth2/OIDC ์ฒ˜๋ฆฌ ์ž์ฒด๋Š” ํ‘œ์ค€ ํ๋ฆ„์ด๋‹ค. +- PKCE๊ฐ€ ์ ์šฉ๋œ authorization code flow๋Š” ํ‘œ์ค€ ๋ณด์•ˆ ๊ถŒ๊ณ ์— ๋ถ€ํ•ฉํ•œ๋‹ค. +- ๋‹จ, Hydra๊ฐ€ acceptํ•˜๋Š” ์ „์ œ์ธ "์‚ฌ์šฉ์ž ์ธ์ฆ ์™„๋ฃŒ"๊ฐ€ ๋น„ํ‘œ์ค€ ๋ฐฉ์‹์ด๋ฉด ์ „์ฒด ๋กœ๊ทธ์ธ ๋ณด์ฆ ์ˆ˜์ค€์€ ๋‚ฎ์•„์ง„๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- ๋น„ํ‘œ์ค€ ์ธ์ฆ์œผ๋กœ acceptํ•œ ๊ฒฝ์šฐ `amr=["phone_identifier_only"]`์ฒ˜๋Ÿผ ๋ณ„๋„ ํ‘œ์‹œ๋ฅผ ํ•œ๋‹ค. +- ๋ฏผ๊ฐ RP์—๋Š” ํ•ด๋‹น `amr`์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. +- RP๋ณ„ required ACR ์ •์ฑ…์„ ๋‘”๋‹ค. + +### 4.7 ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฐ ๋กœ๊ทธ์•„์›ƒ ๋ฐ”์ธ๋”ฉ + +ํ”„๋กœ์„ธ์Šค: + +1. ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ Backend๊ฐ€ audit log๋ฅผ ๊ธฐ๋กํ•œ๋‹ค. +2. OIDC accept ๋˜๋Š” consent granted ์ด๋ฒคํŠธ์— `session_id`, `client_id`, `user_id`๋ฅผ ๋‚จ๊ธด๋‹ค. +3. ๋กœ๊ทธ์•„์›ƒ ์‹œ audit log์—์„œ session-client binding์„ ๋ณต์›ํ•œ๋‹ค. +4. Hydra refresh revoke ๋ฐ backchannel logout์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. + +์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ: + +| ๋ฐ์ดํ„ฐ | ์ƒ์„ฑ ์ฃผ์ฒด | ์šฉ๋„ | +| --- | --- | --- | +| audit event | Backend | ๋กœ๊ทธ์ธ/์Šน์ธ ์ด๋ ฅ | +| session_id | Kratos/Backend | ์„ธ์…˜ ์‹๋ณ„ | +| client_id | Hydra/RP | RP ์‹๋ณ„ | +| consent event | Backend | RP ๋™์˜/์—ฐ๋™ ์ถ”์  | + +ํ‘œ์ค€์„ฑ ํŒ๋‹จ: + +- Backchannel Logout, refresh token revoke๋Š” OIDC/OAuth2 ์ƒํƒœ๊ณ„์˜ ํ‘œ์ค€์  ์„ธ์…˜ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์ด๋‹ค. +- ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ฐ˜ ๋ฐ”์ธ๋”ฉ ๋ณต์›์€ ๋‚ด๋ถ€ ๊ตฌํ˜„ ์ „๋žต์ด๋ฉฐ ํ‘œ์ค€ ์ž์ฒด๋Š” ์•„๋‹ˆ๋‹ค. + +์œ„ํ—˜: + +- ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ˆ„๋ฝ ๋˜๋Š” ๋น„๋™๊ธฐ ์ง€์—ฐ ์‹œ ๋กœ๊ทธ์•„์›ƒ ์ „ํŒŒ ๋ˆ„๋ฝ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค. +- ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ์œผ๋กœ ์ƒ์„ฑ๋œ ์„ธ์…˜๊ณผ ํ‘œ์ค€ ๋กœ๊ทธ์ธ ์„ธ์…˜์ด ๊ฐ™์€ ์ˆ˜์ค€์œผ๋กœ ์ทจ๊ธ‰๋  ์ˆ˜ ์žˆ๋‹ค. + +๊ถŒ์žฅ ๋ณด์™„: + +- ์„ธ์…˜-ํด๋ผ์ด์–ธํŠธ ๋ฐ”์ธ๋”ฉ์€ ๊ฐ์‚ฌ ๋กœ๊ทธ ์™ธ์—๋„ ๋ช…์‹œ์  ์ €์žฅ์†Œ๋ฅผ ๋‘˜์ง€ ๊ฒ€ํ† ํ•œ๋‹ค. +- ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ ์„ธ์…˜์—๋Š” ๋ณ„๋„ `login_method`, `amr`, `risk_level`์„ ๋‚จ๊ธด๋‹ค. + +## 5. ํ‘œ์ค€ ๊ธฐ์ˆ ๊ณผ ๋น„ํ‘œ์ค€ ์š”์†Œ ๋งคํ•‘ + +| ๊ตฌ์„ฑ ์š”์†Œ | ์ ์šฉ ๊ธฐ์ˆ  | ํ‘œ์ค€/๋น„ํ‘œ์ค€ ํŒ๋‹จ | ๋น„๊ณ  | +| --- | --- | --- | --- | +| ์ „ํ™”๋ฒˆํ˜ธ E.164 ์ •๊ทœํ™” | E.164 ํ˜•์‹ | ํ‘œ์ค€ | ์‹๋ณ„์ž ์ •๊ทœํ™” | +| Kratos self-service code login | Ory Kratos code login | ์ œํ’ˆ ํ‘œ์ค€ ๊ธฐ๋Šฅ | ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด์•ผ ์ธ์ฆ ์˜๋ฏธ๊ฐ€ ์žˆ์Œ | +| SMS OTP | One-time code over SMS | ์ผ๋ฐ˜์  MFA/passwordless ๊ตฌํ˜„ | SIM swap, SMS ํƒˆ์ทจ ์œ„ํ—˜์€ ๋ณ„๋„ ์กด์žฌ | +| Courier Webhook | Ory Courier ํ†ตํ•ฉ | ์ œํ’ˆ ํ†ตํ•ฉ ๊ธฐ๋Šฅ | ๋ฐœ์†ก ๋Œ€ํ–‰์€ ๊ฐ€๋Šฅ | +| Courier code ์ž๋™ ์ถ”์ถœ/์ œ์ถœ | ๋‚ด๋ถ€ ์šฐํšŒ | ๋น„ํ‘œ์ค€ | ์‚ฌ์šฉ์ž ์†Œ์œ  ์ฑ„๋„ ํ™•์ธ ์ œ๊ฑฐ | +| Kratos Admin ์„ธ์…˜ ์ง์ ‘ ์ƒ์„ฑ | Admin API | ์šด์˜ ์ธ์ฆ์—๋Š” ๋น„ํ‘œ์ค€/๊ณ ์œ„ํ—˜ | break-glass ์™ธ ๋น„๊ถŒ์žฅ | +| OAuth2 client assertion JWT | JWT client authentication | ํ‘œ์ค€ ๊ณ„์—ด | ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์ด์ง€ ์‚ฌ์šฉ์ž ์ธ์ฆ์ด ์•„๋‹˜ | +| JWKS | JOSE/JWK | ํ‘œ์ค€ | ํ‚ค ๋ฐฐํฌ/๊ฒ€์ฆ | +| Hydra login challenge accept | OIDC/OAuth2 | ํ‘œ์ค€ | ์ธ์ฆ ์ „์ œ๊ฐ€ ์ค‘์š” | +| Authorization Code + PKCE | OAuth2/OIDC | ํ‘œ์ค€ | public client ๊ถŒ์žฅ ํ๋ฆ„ | +| Backchannel Logout | OIDC Back-Channel Logout | ํ‘œ์ค€ | RP ์ง€์› ํ•„์š” | +| Refresh token revoke | OAuth2 Token Revocation | ํ‘œ์ค€ | RP/AS ๊ตฌํ˜„ ์ •์ฑ… ํ•„์š” | + +## 6. ๋Œ€์ฒด ๊ธฐ์ˆ  ๋ฐ ๋ณด์™„ ์ œ์•ˆ + +### 6.1 ๊ถŒ์žฅ์•ˆ A: SMS OTP ๊ธฐ๋ฐ˜ ํฐ ๋กœ๊ทธ์ธ + +์‚ฌ์šฉ์ž UX: + +1. ์ „ํ™”๋ฒˆํ˜ธ ์ž…๋ ฅ +2. SMS OTP ์ˆ˜์‹  +3. OTP ์ž…๋ ฅ +4. Kratos code login verify +5. Hydra accept + +์žฅ์ : + +- ์ „ํ™”๋ฒˆํ˜ธ ์†Œ์œ  ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. +- ๊ธฐ์กด Kratos code login ๊ตฌ์กฐ๋ฅผ ๊ฐ€์žฅ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค. +- ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐฉ์‹์ด ๋ช…ํ™•ํ•˜๋‹ค. + +๋ณด์™„: + +- OTP TTL 3~5๋ถ„ +- ์žฌ์ „์†ก ์ œํ•œ +- ๋ฒˆํ˜ธ๋ณ„/IP๋ณ„ rate limit +- ์‹คํŒจ ํšŸ์ˆ˜ ์ œํ•œ +- SIM swap ์œ„ํ—˜ ์•ˆ๋‚ด ๋ฐ ๊ณ ์œ„ํ—˜ RP ์ถ”๊ฐ€ ์ธ์ฆ + +### 6.2 ๊ถŒ์žฅ์•ˆ B: WebAuthn/FIDO2 ํŒจ์Šคํ‚ค + +์‚ฌ์šฉ์ž UX: + +1. ์ „ํ™”๋ฒˆํ˜ธ ๋˜๋Š” ๊ณ„์ • ์‹๋ณ„์ž ์ž…๋ ฅ +2. ๋ธŒ๋ผ์šฐ์ €/OS ํŒจ์Šคํ‚ค ์ธ์ฆ +3. Kratos ์„ธ์…˜ ๋ฐœ๊ธ‰ +4. Hydra accept + +์žฅ์ : + +- ํ”ผ์‹ฑ ์ €ํ•ญ์„ฑ์ด ๋†’๋‹ค. +- SMS๋ณด๋‹ค ๊ฐ•ํ•œ ์‚ฌ์šฉ์ž ์ธ์ฆ์ด๋‹ค. +- ๋ฐ˜๋ณต ๋กœ๊ทธ์ธ UX๊ฐ€ ๋น ๋ฅด๋‹ค. + +๋ณด์™„: + +- ์ดˆ๊ธฐ ๋“ฑ๋ก ์ ˆ์ฐจ ํ•„์š” +- ๋ถ„์‹ค/๊ธฐ๊ธฐ ๊ต์ฒด ๋ณต๊ตฌ ์ •์ฑ… ํ•„์š” + +### 6.3 ๊ถŒ์žฅ์•ˆ C: ์‚ฌ๋‚ด ์ „์šฉ ๋‹จ๋ง/ํ‚ค์˜ค์Šคํฌ ์ œํ•œ ๋ชจ๋“œ + +ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… UX๋ฅผ ๋ฐ˜๋“œ์‹œ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ์œผ๋กœ ๋ณด์ง€ ๋ง๊ณ  "ํ†ต์ œ๋œ ํ™˜๊ฒฝ์˜ ๋‹จ๋ง ๋กœ๊ทธ์ธ"์œผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค. + +ํ•„์ˆ˜ ์กฐ๊ฑด: + +- ์ „์šฉ ๋‹จ๋ง ์ธ์ฆ์„œ ๋˜๋Š” mTLS +- ๊ณ ์ • ๋„คํŠธ์›Œํฌ/IP allowlist +- ๋‹จ๋ง๋ณ„ client credential +- ๋‹จ๋ง ๋“ฑ๋ก/ํ๊ธฐ ๊ด€๋ฆฌ +- ์‚ฌ์šฉ์ž๋ณ„ ํ—ˆ์šฉ RP ์ œํ•œ +- ์งง์€ ์„ธ์…˜ TTL +- ๋ฏผ๊ฐ ๊ธฐ๋Šฅ ์žฌ์ธ์ฆ + +ํŒ๋‹จ: + +- ์‚ฌ์šฉ์ž๋Š” ์ „ํ™”๋ฒˆํ˜ธ๋กœ ์‹๋ณ„ํ•˜๊ณ , ์‹ค์ œ ์ธ์ฆ์€ ๋‹จ๋ง/๋„คํŠธ์›Œํฌ/๊ด€๋ฆฌ ์ •์ฑ…์ด ๋Œ€์‹  ์ˆ˜ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ๋‹ค. +- ์ด ๊ฒฝ์šฐ์—๋„ `amr`์—๋Š” `trusted_device_phone_identifier`์ฒ˜๋Ÿผ ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ๊ณผ ๋‹ค๋ฅธ ๋ฐฉ์‹์„ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค. + +### 6.4 ๊ถŒ์žฅ์•ˆ D: ๋ชจ๋ฐ”์ผ ์•ฑ ํ‘ธ์‹œ ์Šน์ธ + +์‚ฌ์šฉ์ž UX: + +1. PC/ํ‚ค์˜ค์Šคํฌ์—์„œ ์ „ํ™”๋ฒˆํ˜ธ ์ž…๋ ฅ +2. ๋“ฑ๋ก๋œ ๋ชจ๋ฐ”์ผ ์•ฑ์œผ๋กœ ํ‘ธ์‹œ ์Šน์ธ +3. ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์—์„œ ์Šน์ธ +4. Backend๊ฐ€ Kratos/Hydra ํ๋ฆ„ ์™„๋ฃŒ + +์žฅ์ : + +- ๋น ๋ฅธ UX๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ์‚ฌ์šฉ์ž ์†Œ์œ  ๊ธฐ๊ธฐ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. +- QR ๋กœ๊ทธ์ธ๊ณผ ์œ ์‚ฌํ•œ ์Šน์ธ ๋ชจ๋ธ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. + +๋ณด์™„: + +- ์•ฑ ๋“ฑ๋ก/๊ธฐ๊ธฐ ๋ฐ”์ธ๋”ฉ ํ•„์š” +- ํ‘ธ์‹œ ํ”ผ๋กœ ๊ณต๊ฒฉ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ rate limit์™€ number matching ํ•„์š” + +### 6.5 ๊ถŒ์žฅ์•ˆ E: ๊ณตํ†ต ์Šน์ธ ํ™”๋ฉด ์ ์šฉ + +ํ˜„์žฌ ์ž‘์—… ํŠธ๋ฆฌ์— ์žˆ๋Š” `approval/info`, `approval/reject`, `LoginApprovalScreen` ๊ณ„์—ด ๋ณ€๊ฒฝ์€ QR/๋งํฌ ๊ฐ™์€ ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ์— ์Šน์ธ ์ „ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ณด์™„์ฑ…์ด๋‹ค. + +๊ถŒ์žฅ ์ ์šฉ: + +- QR, ๋งํฌ, headless link ๋ชจ๋‘ ๊ณตํ†ต ์Šน์ธ ํ™”๋ฉด ์‚ฌ์šฉ +- ์Šน์ธ ์ „์— ์š”์ฒญ ์„œ๋น„์Šค, ๊ธฐ๊ธฐ, IP, ์‹œ๊ฐ„ ํ‘œ์‹œ +- `๋‚ด ์š”์ฒญ์ด ์•„๋‹™๋‹ˆ๋‹ค` ์„ ํƒ ์‹œ pending ์ƒํƒœ๋ฅผ `rejected` ๋˜๋Š” `blocked`๋กœ ๋ณ€๊ฒฝ +- ์Šน์ธ ๊ฑฐ์ ˆ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์‚ฌ ๋กœ๊ทธ๋กœ ๋‚จ๊น€ + +์ฃผ์˜: + +- ์Šน์ธ ํ™”๋ฉด์€ "๋ณด์™„์ฑ…"์ด์ง€ ์ „ํ™”๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ์˜ ์ธ์ฆ ๋ถ€์žฌ๋ฅผ ์™„์ „ํžˆ ํ•ด๊ฒฐํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์Šน์ธํ•˜๋Š” ๋ณ„๋„ ์†Œ์œ  ๊ธฐ๊ธฐ๋‚˜ ์„ธ์…˜์ด ์žˆ์„ ๋•Œ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค. + +## 7. ์šด์˜ ์ •์ฑ… ์ œ์•ˆ + +### 7.1 ๊ธฐ๋ณธ ์ •์ฑ… + +- ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋งŒ์œผ๋กœ ์„ธ์…˜์„ ๋ฐœ๊ธ‰ํ•˜๋Š” API๋Š” ์šด์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋กœ ๋…ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. +- `POST /api/v1/auth/phone-login` ํ˜•ํƒœ์˜ ๋‹จ์ผ ์š”์ฒญ ์ฆ‰์‹œ ๋กœ๊ทธ์ธ์€ ๊ธˆ์ง€ํ•œ๋‹ค. +- Courier code ์ž๋™ ์ œ์ถœ์€ ๊ฐœ๋ฐœ/ํ…Œ์ŠคํŠธ dry-run ๋˜๋Š” ์ œํ•œ๋œ ๋‚ด๋ถ€ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. +- ๋ชจ๋“  ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ์—๋Š” `login_method`, `amr`, `risk_level`, `client_id`, `device_id`๋ฅผ ๋‚จ๊ธด๋‹ค. + +### 7.2 RP๋ณ„ ์ •์ฑ… + +- RP๋ณ„๋กœ ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ์ธ์ฆ ๊ฐ•๋„๋ฅผ ์„ค์ •ํ•œ๋‹ค. +- ๋ฏผ๊ฐ RP๋Š” SMS OTP ์ด์ƒ ๋˜๋Š” WebAuthn์„ ์š”๊ตฌํ•œ๋‹ค. +- headless ๋กœ๊ทธ์ธ ํ—ˆ์šฉ RP๋Š” ๋ณ„๋„ allowlist์™€ ๋ณด์•ˆ ์‹ฌ์‚ฌ๋ฅผ ๊ฑฐ์นœ๋‹ค. + +### 7.3 ๊ฐ์‚ฌ ๋ฐ ํƒ์ง€ + +- ์ „ํ™”๋ฒˆํ˜ธ ๊ธฐ๋ฐ˜ ๋กœ๊ทธ์ธ ์‹œ๋„๋Š” ์„ฑ๊ณต/์‹คํŒจ ๋ชจ๋‘ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค. +- ๋™์ผ IP์˜ ๋‹ค์ˆ˜ ๋ฒˆํ˜ธ ์‹œ๋„, ๋™์ผ ๋ฒˆํ˜ธ์˜ ๋ฐ˜๋ณต ์‹คํŒจ, ์งง์€ ์‹œ๊ฐ„ ๋‚ด ๋‹ค์ˆ˜ RP ๋กœ๊ทธ์ธ์€ ํƒ์ง€ ๋Œ€์ƒ์œผ๋กœ ๋‘”๋‹ค. +- ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ์œผ๋กœ ๋ฐœ๊ธ‰๋œ ์„ธ์…˜์€ ๋Œ€์‹œ๋ณด๋“œ์™€ ๊ด€๋ฆฌ์ž ํ™”๋ฉด์—์„œ ๊ตฌ๋ถ„ ํ‘œ์‹œํ•œ๋‹ค. + +## 8. ๊ถŒ์žฅ ๊ตฌํ˜„ ๋ฐฉํ–ฅ + +1. ํฐ๋ฒˆํ˜ธ ๋‹จ๋… ์ฆ‰์‹œ ๋กœ๊ทธ์ธ์€ ๊ตฌํ˜„ํ•˜๋”๋ผ๋„ ๊ธฐ๋ณธ ๋น„ํ™œ์„ฑ feature flag๋กœ ๋‘”๋‹ค. +2. ์šด์˜ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ์—๋Š” SMS OTP ๋˜๋Š” WebAuthn์„ ๋ถ™์ธ๋‹ค. +3. ํ‚ค์˜ค์Šคํฌ/์ „์šฉ ๋‹จ๋ง ์š”๊ตฌ์‚ฌํ•ญ์ด๋ผ๋ฉด ๋ณ„๋„ ๋ผ์šฐํŠธ์™€ ๋ณ„๋„ ๋ณด์ฆ ์ˆ˜์ค€์œผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค. +4. Hydra accept ์‹œ `amr`/`acr`๋ฅผ ๋ช…ํ™•ํžˆ ๊ธฐ๋กํ•˜๊ณ  RP ์ •์ฑ…๊ณผ ์—ฐ๊ฒฐํ•œ๋‹ค. +5. Courier relay๋Š” ๋‚ด๋ถ€๋ง ๋˜๋Š” ์„œ๋ช… ๊ฒ€์ฆ์œผ๋กœ ๋ณดํ˜ธํ•œ๋‹ค. +6. ๊ณตํ†ต ์Šน์ธ ํ™”๋ฉด์„ QR/๋งํฌ/headless link์— ์ ์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญ ์ •๋ณด๋ฅผ ํ™•์ธํ•˜๊ฒŒ ํ•œ๋‹ค. +7. ํ‘œ์ค€ ๋กœ๊ทธ์ธ๊ณผ ๋น„ํ‘œ์ค€ ๋กœ๊ทธ์ธ์˜ ๊ฐ์‚ฌ ๋กœ๊ทธ, ์„ธ์…˜ TTL, RP ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ„๋ฆฌํ•œ๋‹ค. + +## 9. ์ตœ์ข… ํŒ๋‹จ + +ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๋‹จ๋… ๋กœ๊ทธ์ธ์€ "๊ฐ„ํŽธํ•œ ์‹๋ณ„ UX"๋กœ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ ์ž์ฒด๋ฅผ ์‚ฌ์šฉ์ž ์ธ์ฆ์œผ๋กœ ๊ฐ„์ฃผํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. + +Baron SSO์˜ ํ‘œ์ค€ ์ธ์ฆ ๊ฒฝ๋กœ๋กœ ์ฑ„ํƒํ•˜๋ ค๋ฉด ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•œ๋‹ค. + +- SMS OTP ๋˜๋Š” ์Œ์„ฑ OTP +- WebAuthn/FIDO2 ํŒจ์Šคํ‚ค +- ๋“ฑ๋ก๋œ ๋ชจ๋ฐ”์ผ ์•ฑ ํ‘ธ์‹œ ์Šน์ธ +- ์‚ฌ๋‚ด ์ „์šฉ ๋‹จ๋ง ์ธ์ฆ ๋ฐ ๋„คํŠธ์›Œํฌ ์ œํ•œ +- ๊ด€๋ฆฌ์ž ์Šน์ธ ๋˜๋Š” ์—…๋ฌด ์‹œ์Šคํ…œ ๋‚ด 2์ฐจ ์Šน์ธ + +์ด ์ค‘ ์•„๋ฌด ๊ฒƒ๋„ ์—†๋Š” `phoneNumber -> session` ๊ตฌ์กฐ๋Š” ํ‘œ์ค€ ์ธ์ฆ์ด ์•„๋‹ˆ๋ผ ์ธ์ฆ ์šฐํšŒ์— ๊ฐ€๊น๋‹ค. ๋”ฐ๋ผ์„œ ์šด์˜ ์ ์šฉ ์‹œ์—๋Š” "๋น„ํ‘œ์ค€ ์ €๋ณด์ฆ ๋กœ๊ทธ์ธ"์œผ๋กœ ๋ช…์‹œํ•˜๊ณ , ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ RP์™€ ๊ธฐ๋Šฅ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•ด์•ผ ํ•œ๋‹ค.