์น ์ ํ๋ฆฌ์ผ์ด์ ๋ณดํธ
1๏ธโฃ ๊ฐ์
์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ๊ฐ์ฝ๋ํ๋ฆ(Authorization Code Flow)๋ฅผ ์ฌ์ฉํด์ผํ๋ฉฐ,
PKCE(Proof key for Code Exchange)๋ฅผ ํจ๊ป ์ฌ์ฉํ๋๊ฒ์ด ์ข๋ค.
์ธ๊ฐ์ฝ๋ ํ๋ฆ(Authorization Code Flow)๋ ์๋์ ๊ฐ์ ์ธ์ฆ ํ๋ฆ์ ๊ฐ์ง๋ค.
# [์ด๊ธฐ ๋ก๊ทธ์ธ ํ๋ฆ]
Browser App Server Keycloak
| | |
|----(1) ์ ๊ทผ ์๋------------>| |
| | |
|<---(2) ๋ก๊ทธ์ธ ํ์ด์ง ๋ฆฌ๋ค์ด๋ ํธ--| |
| | |
|----(3) ๋ก๊ทธ์ธ ์์ฒญ-------------------------------------->|
| | |
|<---(4) ์ธ๊ฐ ์ฝ๋ ์ ๋ฌ------------------------------------|
| | |
|----(5) ์ธ๊ฐ ์ฝ๋ ์ ๋ฌ-------->| |
| |----(6) ํ ํฐ ๊ตํ---------->|
| |<---(7) ํ ํฐ ์๋ต-----------|
| | |
| |---(8) ํ ํฐ ์ ์ฅ |
|<--(9) ์ธ์
์ฟ ํค ๋ฐ๊ธ----------| |
| | |
# [์ดํ API ์์ฒญ ํ๋ฆ]
Browser App Server Resource Server
| | |
|----(1) API ์์ฒญ------------>| |
| (์ธ์
์ฟ ํค ํฌํจ) | |
| |----(2) API ์์ฒญ---------->|
| | (์ ์ฅ๋ ํ ํฐ ์ฌ์ฉ) |
| |<---(3) API ์๋ต-----------|
|<---(4) ๊ฒฐ๊ณผ ์ ๋ฌ-------------| |
๋ํ ๊ณ ๋ คํด์ผํ ๋ถ๋ถ์ ์ด๋ค ์ํคํ
์ณ์ ์น ์ดํ๋ฆฌ์ผ์ด์
์ ์ด์ํ๋์ง์ ๋ฐ๋ผ ๊ณ ๋ คํด์ผํ ๋ณด์์ต์
์ด ๋ฌ๋ผ์ง๋ค.
ํนํ ๋ณด์์์ค์ ์ฐจ์ด, ํ ํฐ ๊ด๋ฆฌ๋ฐฉ์์ ์ฐจ์ด๊ฐ ์ํคํ
์ณ๋ง๋ค ์์ดํ๋ฏ๋ก, ๊ฐ๊ฐ์ ์ฌ์ฉ์ฌ๋ก์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ์ํ ๋ณด์์ค์ ์ ํ๋๊ฒ์ด ์ค์ํ๋ค.
์น ์ดํ๋ฆฌ์ผ์ด์ ์ํคํ ์ณ ๊ตฌ๋ถ | ๊ฐ๋จ ์ค๋ช |
Server Side | • Access Type: confidential • Client Authentication: ํ์ (Client Secret ์ฌ์ฉ) • Standard Flow: ํ์ฑํ • PKCE: ๊ถ์ฅ • Token Storage: ์๋ฒ ์ธก ์์ ํ ์ ์ฅ์ ํน์ง: ๊ฐ์ฅ ์์ ํ ๊ตฌ์ฑ, ํ ํฐ์ ์๋ฒ์์ ์์ ํ ์ ์ด |
SPA with dedicated Rest API | • Access Type: public • Client Authentication: ๋ถ๊ฐ๋ฅ • Standard Flow: ํ์ฑํ • PKCE: ํ์ • Token Storage: ๋ธ๋ผ์ฐ์ (๋ฉ๋ชจ๋ฆฌ ๊ถ์ฅ) ์ถ๊ฐ ๊ณ ๋ ค์ฌํญ: • BFF(Backend For Frontend) ํจํด ๊ณ ๋ ค • ํ ํฐ ๊ฐฑ์ ๋ก์ง ํ๋ก ํธ์๋ ๊ตฌํ |
SPA with intermediary API | • Access Type: public (ํ๋ก ํธ์๋) • Client Authentication: confidential (์ค๊ฐ ์๋ฒ) • Standard Flow: ํ์ฑํ • PKCE: ํ์ • Token Storage: - ํ๋ก ํธ์๋: ๋ฉ๋ชจ๋ฆฌ - ์ค๊ฐ ์๋ฒ: ์์ ํ ์ ์ฅ์ ์ถ๊ฐ ๊ณ ๋ ค์ฌํญ: • ์ค๊ฐ ์๋ฒ์์ ํ ํฐ ๊ด๋ฆฌ • ํ๋ก ํธ์๋๋ ์ธ์ ์ฟ ํค๋ง ์ฌ์ฉ |
SPA with external API | • Access Type: public • Client Authentication: ๋ถ๊ฐ๋ฅ • Standard Flow: ํ์ฑํ • PKCE: ํ์ • Token Storage: ๋ธ๋ผ์ฐ์ (๋ฉ๋ชจ๋ฆฌ ๊ถ์ฅ) ์ถ๊ฐ ๊ณ ๋ ค์ฌํญ: • CORS ์ค์ ํ์ • ์ธ๋ถ API์ ์ธ์ฆ ์๊ตฌ์ฌํญ ์ค์ • ํ ํฐ ๊ฐฑ์ ์ ๋ต ์๋ฆฝ |
2๏ธโฃ ๊ณตํต์ ์ผ๋ก ์ ์ฉํด์ผํ๋ ๋ณด์ ์ค์
- ์ธ๊ฐ์ฝ๋ํ๋ฆ(Authorization Code Flow) ์ฌ์ฉ (= Standard Flow)
- ์ธ๊ฐ์ฝ๋ํ๋ฆ(Authorization Code Flow)์ OAuth 2.0์ ๊ถ์ฅ๋๋ ํ๋ฆํ๋ค.
- ํด๋ผ์ด์ธํธ๊ฐ ์ธ๊ฐ์ฝ๋๋ฅผ ํตํด ํ ํฐ์ ์ป๋ ๋ฐฉ์์ด๋ค.
- ์ก์ธ์ค ํ ํฐ์ด ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํ์ง ์๊ณ ๋ฐฑ์๋ ์๋ฒ์์ ์ง์ ๊ตํ๋๋ ๋ฐฉ์์ผ๋ก ๋ณด์์ฑ์ด ๊ฐ์ฅ ๋์ ์ธ์ฆ๋ฐฉ์์ด๋ค.
- ์ฃผ์์ฌํญ: ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ์ด ๋ ธ์ถ๋์ง ์๋๋ก ์ฃผ์ ํด์ผํ๋ค.
- PKCE(Proof key for Code Exchange) ์ฌ์ฉ
- ์ธ๊ฐ์ฝ๋(Authorization Code)๋ฅผ ์ธํฐ์ ํธ ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ๋ฐฉ์งํ๊ธฐ ์ํ ๊ธฐ์
- Keycloak์์๋ Client > Advanced ํญ > Advanced settings > Proof Key for Code Exchange Code Challenge Method ์์ ํ์ฑํ ํ๋ค. (S256 ๊ถ์ฅ)
- Keycloak ๋ก๊ทธ์ธ ํ์ด์ง ํ์ฉ
- keycloak์์ ์ ๊ณตํ๋ ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ์ด์ฉํ๋๊ฒ์ด ์ข๋ค.
- ๊ธฐ์กด ๋ก๊ทธ์ธ ํ์ด์ง ์ ์ง (Resource Owner Password Credential) โ์ฌ์ฉํ์ง ์๋๊ฒ์ด ์ข๋ค.โ
๋จ์ผ ์ฑ ๋ณด์ ์นจํด๋ก ์ ์ฒด ์์คํ ์ด ์ํํด์ง ์ ์๋ค. - iframe ๋ด Keycloak ๋ก๊ทธ์ธ โ์ฌ์ฉํ์ง ์๋๊ฒ์ด ์ข๋ค.โ
ํด๋ฆญ์ฌํน, ํผ์ฑ๊ณต๊ฒฉ(์ถ์ฒํ์ธ์ด๋ ค์)์ ์ทจ์ฝํ๋ฉฐ,
๋ธ๋ผ์ฐ์ ์ ์๋ํํฐ ์ฟ ํค ์ฐจ๋จ์ผ๋ก์ธํ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๋ค.
- Valid Redirect URIs ์ ํ
- ์ธ์ฆ ํ Redirect ๊ฐ๋ฅํ URI๋ชฉ๋ก์ ์ ์ํ๋ ๋ถ๋ถ์ด๋ฉฐ, ์ ํํ URI๋ก Redirect๋๋๋ก ์ค์ ํด์ผํ๋ค.
- ์) https://your-app.com/callback/*
- ์ ์ ํ์ง ์์ ์ค์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ OpenRedirect ์ทจ์ฝ์ ์ด ์๊ธด๋ค.
- Web Origins ์ค์
- CORS ์์ฒญ์ ํ์ฉํ ์ค๋ฆฌ์ง์ ์ค์ ํ๋ ๋ถ๋ถ์ด๋ฉฐ, ํด๋ผ์ด์ธํธ์ ๋๋ฉ์ธ์ ์ ํํ๋ค.
- ํ๊ฐ๋์ง ์์ ๋๋ฉ์ธ์์์ ์์ฒญ์ ์ฐจ๋จํ๋ค.
- ํ ํฐ ๋ผ์ดํํ์ ์ค์
- ์ก์ธ์ค ํ ํฐ๊ณผ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ ์ค์ ํ์ฌ ์ธ์ ์ง์ ์๊ฐ์ ์ ์ดํ๋ค.
- Refresh Token์ ์ฌ์ฌ์ฉ ํ์ง ์๋๋ก ์ค์ ํด์ผํ๋ค.
- ํ ํฐ ํ์ทจ ์ ํผํด๋ฅผ ์ต์ํ ํ ์ ์๋ค.
- Access Token: 5-15๋ถ
- Refresh Token: 8-24์๊ฐ
- SSO Session: 8-10์๊ฐ
- Implicit Flow ๋นํ์ฑํ
- ํ ํฐ์ URL ํ๋ผ๋ฉํฐ๋ก ์ ๋ฌํ๋ ๋ฐฉ์์ด๋ฉฐ, ๋ณด์ ์ด์๋ก ์ฌ์ฉํ์ง ์๋๊ฒ์ด ์ข๋ค.
3๏ธโฃ Server Side ๋ณด์์ค์
์๋ฒ ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฐ์ฅ ์ ํต์ ์ธ ๋ฐฉ์์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก,
์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ Keycloak ์ฌ์ด์์๋ง Token์ด ๊ตํ๋๋ค.
์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
๋ด์ Token์ด ์ ์ฅ๋๋ฉฐ, ๋ธ๋ผ์ฐ์ ๋ Session Cookie๋ฅผ ๊ฐ์ง๊ณ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ค.
# ์ํคํ
์ณ
[๋ธ๋ผ์ฐ์ ]
โ (์ธ์
์ฟ ํค)
[์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
] ←→ [Keycloak]
โ
[๋ฆฌ์์ค/DB]
# ์ธ์ฆ ํ๋ก์ฐ
1. ๋ธ๋ผ์ฐ์ → ์๋ฒ: ๋ก๊ทธ์ธ ์์ฒญ
2. ์๋ฒ → Keycloak: Authorization Code ์์ฒญ
3. ์ฌ์ฉ์ → Keycloak: ๋ก๊ทธ์ธ
4. Keycloak → ์๋ฒ: ์ธ์ฆ ์ฝ๋
5. ์๋ฒ → Keycloak: ํ ํฐ ๊ตํ (client secret ์ฌ์ฉ)
6. ์๋ฒ: ํ ํฐ ์์ ํ๊ฒ ์ ์ฅ
7. ์๋ฒ → ๋ธ๋ผ์ฐ์ : ์ธ์
์ฟ ํค ๋ฐ๊ธ
8. ์ดํ ์์ฒญ: ๋ธ๋ผ์ฐ์ → ์๋ฒ๋ก ์ธ์
์ฟ ํค๋ฅผ ํฌํจํ์ฌ ์์ฒญ
9. ์๋ฒ: ์ธ์
์ฟ ํค ๊ฒ์ฆ ํ ํ์์ ํ ํฐ์ผ๋ก ๋ฆฌ์์ค ์ ๊ทผ
๋ค๋ฅธ SPA์ํคํ ์ณ์ ๋น๊ตํ๋ค๋ฉด ์๋์ ๊ฐ์ ํน์ง์ ๊ฐ์ง๋ค.
- ํ ํฐ์ด ํด๋ผ์ด์ธํธ์ ๋ ธ์ถ๋์ง ์์
- XSS ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ์๋์ ์ผ๋ก ์์
- ์ธ์ ๊ด๋ฆฌ๊ฐ ์๋ฒ์์ ์ค์ํ๋จ
์๋ฒ ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณดํธํ๋ ๊ฒฝ์ฐ์๋ ์๋์ ๊ฐ์ ์ค์ ์ ํ๋ค.
- Public Client๊ฐ ์๋ Confidential Client๋ก ์ค์ ํ๋ค.
- ์๋ฒ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ Client Secret์ ์๋ฒ์์๋ง ์์ ํ๊ฒ ๋ณด๊ดํ๊ณ ์ฌ์ฉํ๋ค.
(์๋ฒ์์ ์์ ํ๊ธฐ ๋ณด๊ด๋๊ธฐ๋๋ฌธ) - Confidential Client๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ถ๋ ์ธ๊ฐ์ฝ๋๋ฅผ ์ ์ฉํ ์ ์๋ค.
- ์๋ฒ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ Client Secret์ ์๋ฒ์์๋ง ์์ ํ๊ฒ ๋ณด๊ดํ๊ณ ์ฌ์ฉํ๋ค.
- PKCE (Proof Key for Code Exchange) ํ์ฑํ๋ฅผ ๊ถ์ฅํ๋ค.
- ์ธ์ฆ ์ฝ๋ ์ธํฐ์ ํธ ๊ณต๊ฒฉ ๋ฐฉ์งํ ์ ์๋ค.
- Authorization Code Flow์ ํจ๊ป ์ฌ์ฉํ๋๊ฒ์ ๊ถ์ฅํ๋ค.
4๏ธโฃ SPA with dedicated Rest API ๋ณด์์ค์
์ด ์ํคํ
์ณ๋ ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๋ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์์ Keycloak๊ณผ ๋ฐฑ์๋ API๋ฅผ ์ง์ ํธ์ถํ๋ค.
SPA์์๋ Keycloak๊ณผ ์ง์ ํต์ ํ๊ธฐ๋๋ฌธ์, ํ ํฐ์ด ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์ ๋
ธ์ถ๋๋ฏ๋ก ํ ํฐ ์ ์ฅ๊ณผ ๊ด๋ฆฌ์ ํน๋ณํ ์ฃผ์๊ฐ ํ์ํ๋ค.
# ์ํคํ
์ณ
[๋ธ๋ผ์ฐ์ ]
|
|โโ SPA (ํ๋ก ํธ์๋)
| โ (๋ก์ปฌ ์ ์ฅ์/๋ฉ๋ชจ๋ฆฌ)
| [Keycloak]
| ↓ (์ก์ธ์ค ํ ํฐ)
| ↓
↓ ↓
[๋ฐฑ์๋ API] ←→ [๋ฆฌ์์ค/DB]
# ์ธ์ฆ ํ๋ก์ฐ
1. SPA → Keycloak: Authorization Code ์์ฒญ (PKCE ์ฌ์ฉ)
2. ์ฌ์ฉ์ → Keycloak: ๋ก๊ทธ์ธ
3. Keycloak → SPA: ์ธ์ฆ ์ฝ๋
4. SPA → Keycloak: ํ ํฐ ๊ตํ (PKCE verifier ์ฌ์ฉ)
5. SPA: ํ ํฐ์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ
6. API ํธ์ถ ์ Bearer ํ ํฐ ์ฌ์ฉ
ํ ํฐ์ ๋ธ๋ผ์ฐ์ ์์ ์ง์ ๊ด๋ฆฌํ๊ฒ ๋๋ฉฐ, ์ด ์ํคํ ์ณ์์๋ ์๋์ ๊ฐ์ ๋ณด์ ์ค์ ์ด ํ์ํ๋ค.
- Access Type: public์ ์ฌ์ฉํ๋ค.
- Confidential Client๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Client Secret์ด ํ์ํ๋ฐ, SPA์ ํฌํจํ๋ฉด ๋๊ตฌ๋ ๋ณผ์์๊ฒ ๋๋ค.
๋ค์ ์ ๋ฆฌํ์๋ฉด, SPA๋ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ JavaScript ์ฝ๋์ด๋ฏ๋ก, ์์ค ์ฝ๋๊ฐ ๋ชจ๋ ํด๋ผ์ด์ธํธ์ ๋ ธ์ถ๋๊ธฐ๋๋ฌธ์
Client Secret ์ฌ์ฉํ์ง ์๋๋ค.
- Confidential Client๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Client Secret์ด ํ์ํ๋ฐ, SPA์ ํฌํจํ๋ฉด ๋๊ตฌ๋ ๋ณผ์์๊ฒ ๋๋ค.
- PKCE (Proof Key for Code Exchange)๋ฅผ ํ์ฑํํ๋ค.
- ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ PKCE๋ก ๋ฐ๋์ ๋ณด์์ ๊ฐํํด์ผํ๋ค.
- ํ ํฐ ์ ์ฅ ๋ฐ ๊ด๋ฆฌ์ ๋ ์ ๊ฒฝ์จ์ผํ๋ค.
- ๋ธ๋ผ์ฐ์ ํญ ์ข ๋ฃ์ ์๋์ผ๋ก ์ญ์ ๋ ์ ์๋๋ก ๋ฉ๋ชจ๋ฆฌ ์ ์ฅ ๊ถ์ฅ (localStorage/sessionStorage ์ง์)
- ์๋ ๊ฐฑ์ ์ ์ํ Refresh Token ๊ด๋ฆฌ ์ ๋ต ํ์ (RefreshToken ์ฌ์ฌ์ฉ ๊ธ์ง)
- ํ ํฐ ๋ง๋ฃ ์๊ฐ ์ ์ ํ ์ค์
5๏ธโฃ SPA with intermediary API ๋ณด์์ค์
์ด ์ํคํ
์ณ๋ SPA์ ๋์ผํ ๋๋ฉ์ธ์์ ์ค๊ฐ(intermediary)์๋ฒ๋ฅผ ๊ฐ์ง๋ค.
BFF (Backend For Frontend) ํจํด์ด๋ผ๊ณ ๋ ํ๋๋ฐ, API Gateway์ ์ ์ฌํ์ง๋ง ์๋์ ๊ฐ์ ํน์ง์ด ์๋ค.
- ํ๋ก์์ญํ : ์ธ์ฆ/์ธ๊ฐ ์ฒ๋ฆฌ, ํ ํฐ๊ด๋ฆฌ, API์์ฒญ ์ค๊ณ
- ๋ณด์๊ฐํ: ๋ฏผ๊ฐํ ์ ๋ณด(ํ ํฐ ๋ฑ) ์๋ฒ ์ธก ๊ด๋ฆฌ, ๋์ผํ ๋๋ฉ์ธ์ผ๋ก CORS ์ด์ ํด๊ฒฐ, ์ธ์ ๊ธฐ๋ฐ ์ธ์ฆ ๊ฐ๋ฅ
- ์ต์ ํ: ํ๋ก ํธ์๋์ ๋ํ ์ต์ ํ๋ API์ ๊ณต, ๋ฐ์ดํฐ ์ง๊ณ ๋ฐ ๋ณํ, ๋คํธ์ํฌ ์์ฒญ ์ต์ํ
- ์ธ์ ๊ด๋ฆฌ: ์๋ฒ ์ธก์์ ์ค์ํ๋ ์ธ์ ๊ด๋ฆฌ ๊ฐ๋ฅ
- ํ ํฐ ๊ฐฑ์ : Refresh Token์ ์๋ฒ์์ ์์ ํ๊ฒ ๊ด๋ฆฌํ๊ณ ์๋ ๊ฐฑ์
ํ๋ก ํธ์๋๋ ์ค์ง BFF์๋ฒ์ ๋ง ํต์ ํ๋ฉฐ, BFF์์ ํ ํฐ์ ๊ด๋ฆฌํ๊ณ ๋ฐฑ์๋ API ํธ์ถ์ ์ฌ์ฉํ๋ค.
์ฆ, ์ด ์ํคํ
์ณ๋ ํ๋ก ํธ์๋/ ๋ฐฑ์๋/ Keycloak ์ฌ์ด์ ์ค๊ฐ์๋ฅผ ๋ ์ํคํ
์ณ๋ผ๊ณ ํ ์ ์๋ค.
# ์ํคํ
์ณ
[๋ธ๋ผ์ฐ์ ]
|โ SPA (ํ๋ก ํธ์๋)
| โ (httpOnly ์ธ์
์ฟ ํค)
| ↓
[BFF ์๋ฒ] ←→ [๋ฐฑ์๋ API] ←→ [๋ฆฌ์์ค/DB]
โ
[Keycloak]
# ์ธ์ฆ ํ๋ก์ฐ
1. SPA → BFF: ๋ก๊ทธ์ธ ์์ฒญ
2. BFF → Keycloak: Authorization Code ์์ฒญ
3. Keycloak → BFF: ํ ํฐ ๋ฐ๊ธ
4. BFF: ํ ํฐ ์๋ฒ์ ์์ ํ๊ฒ ์ ์ฅ
5. BFF → SPA: httpOnly ์ธ์
์ฟ ํค ๋ฐ๊ธ
6. ์ดํ ๋ชจ๋ API ์์ฒญ์ ์ธ์
์ฟ ํค๋ก ์ฒ๋ฆฌ
7. ํ ํฐ ๋ง๋ฃ์ BFF๊ฐ ์๋์ผ๋ก ๊ฐฑ์
8. ์ธ์
๋ง๋ฃ ์ ์ฑ
์ ์ฉ
Keycloak๊ณผ ๋ฐฑ์๋API ํธ์ถ์ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์์ ํ๋๊ฒ์ด ์๋๋ผ ์ค๊ฐ ์๋ฒ๊ฐ ํ๋ค.
๋ฐ๋ผ์, Keycloak์ Client Authentication: confidential๋ฅผ ์ฌ์ฉํ ์ ์์ด ํ ํฐ์ด ์ ์ถ๋ ์ํ์ด ์ค์ด๋ ๋ค.
์๋์ ๊ฐ์ ๋ณด์ ์ค์ ์ ํ๋ค.
- Public Client๊ฐ ์๋ Confidential Client๋ก ์ค์ ํ๋ค.
- ์๋ฒ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ Client Secret์ ์๋ฒ์์๋ง ์์ ํ๊ฒ ๋ณด๊ดํ๊ณ ์ฌ์ฉํ๋ค.
(์๋ฒ์์ ์์ ํ๊ธฐ ๋ณด๊ด๋๊ธฐ๋๋ฌธ) - Confidential Client๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ถ๋ ์ธ๊ฐ์ฝ๋๋ฅผ ์ ์ฉํ ์ ์๋ค.
- ์๋ฒ์ฌ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ Client Secret์ ์๋ฒ์์๋ง ์์ ํ๊ฒ ๋ณด๊ดํ๊ณ ์ฌ์ฉํ๋ค.
- PKCE (Proof Key for Code Exchange) ํ์ฑํ๋ฅผ ๊ถ์ฅํ๋ค.
- ์ธ์ฆ ์ฝ๋ ์ธํฐ์ ํธ ๊ณต๊ฒฉ ๋ฐฉ์งํ ์ ์๋ค.
- Authorization Code Flow์ ํจ๊ป ์ฌ์ฉํ๋๊ฒ์ ๊ถ์ฅํ๋ค.
- ์ธ์
์ฟ ํค ์ค์
- httpOnly: true (ํ์)
- secure: true (HTTPS ํ์)
- sameSite: strict
์ฌ๊ธฐ์ Session Cookie์ httpOnly ํ๋๊ทธ๊ฐ ํ์์ธ ์ด์ ๋ ๋ณด์, ํนํ XSS(Cross-Site Scripting) ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ๋ณดํธํ๊ธฐ ์ํด์๋ผ๊ณ ํ๋๋ฐ, ์ด๋ถ๋ถ์ ์ดํดํ์ง ๋ชปํ๋ค..
6๏ธโฃ SPA with external API ๋ณด์์ค์
์ด ์ํคํ
์ฒ๋ ๋ธ๋ผ์ฐ์ ์์ ๋ ๋๋ง ๋ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์์ ์ธ๋ถ ๋๋ฉ์ธ์ API๋ฅผ ์ง์ ํธ์ถํ๋ค.
API Gateway๋ ๋ค๋ฅธ ๋๋ฉ์ธ์ ์๋น์ค๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ์ ํด๋นํ๋ค.
# ์ํคํ
์ณ
[๋ธ๋ผ์ฐ์ ]
|
|โโ SPA (ํ๋ก ํธ์๋)
| โ (๋ก์ปฌ ์ ์ฅ์/๋ฉ๋ชจ๋ฆฌ)
| [Keycloak]
| ↓ (์ก์ธ์ค ํ ํฐ)
| ↓
↓ ↓
[์ธ๋ถ API] (๋ค๋ฅธ ๋๋ฉ์ธ)
↓
[๋ฆฌ์์ค]
# ์ธ์ฆ ํ๋ก์ฐ
1. SPA → Keycloak: Authorization Code ์์ฒญ (PKCE ์ฌ์ฉ)
2. ์ฌ์ฉ์ → Keycloak: ๋ก๊ทธ์ธ
3. Keycloak → SPA: ์ธ์ฆ ์ฝ๋
4. SPA → Keycloak: ํ ํฐ ๊ตํ (PKCE verifier ์ฌ์ฉ)
5. SPA: ํ ํฐ์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ
6. CORS๋ฅผ ํตํ ์ธ๋ถ API ํธ์ถ (Bearer ํ ํฐ)
์ด ์ํคํ ์ณ๋ Keycloak์ ์ง์ ํธ์ถํ๋ฉฐ ์๋์ ๊ฐ์ ํน์ง์ ๊ฐ์ง๋ค.
- CORS ์ค์ ํ์
- ํ ํฐ ๋ ธ์ถ ์ํ ์์
- ๋ค์ค ๋๋ฉ์ธ ์์ฒญ ์ฒ๋ฆฌ
์๋์ ๊ฐ์ ๋ณด์์ค์ ์ ํด์ผํ๋ค.
- Access Type: public์ ์ฌ์ฉํ๋ค.
- Confidential Client๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Client Secret์ด ํ์ํ๋ฐ, SPA์ ํฌํจํ๋ฉด ๋๊ตฌ๋ ๋ณผ์์๊ฒ ๋๋ค.
๋ค์ ์ ๋ฆฌํ์๋ฉด, SPA๋ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ JavaScript ์ฝ๋์ด๋ฏ๋ก, ์์ค ์ฝ๋๊ฐ ๋ชจ๋ ํด๋ผ์ด์ธํธ์ ๋ ธ์ถ๋๊ธฐ๋๋ฌธ์
Client Secret ์ฌ์ฉํ์ง ์๋๋ค.
- Confidential Client๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Client Secret์ด ํ์ํ๋ฐ, SPA์ ํฌํจํ๋ฉด ๋๊ตฌ๋ ๋ณผ์์๊ฒ ๋๋ค.
- PKCE (Proof Key for Code Exchange)๋ฅผ ํ์ฑํํ๋ค.
- ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ PKCE๋ก ๋ฐ๋์ ๋ณด์์ ๊ฐํํด์ผํ๋ค.
- ํ ํฐ ์ ์ฅ ๋ฐ ๊ด๋ฆฌ์ ๋ ์ ๊ฒฝ์จ์ผํ๋ค.
- ๋ธ๋ผ์ฐ์ ํญ ์ข ๋ฃ์ ์๋์ผ๋ก ์ญ์ ๋ ์ ์๋๋ก ๋ฉ๋ชจ๋ฆฌ ์ ์ฅ ๊ถ์ฅ (localStorage/sessionStorage ์ง์)
- ์๋ ๊ฐฑ์ ์ ์ํ Refresh Token ๊ด๋ฆฌ ์ ๋ต ํ์ (RefreshToken ์ฌ์ฌ์ฉ ๊ธ์ง)
- ํ ํฐ ๋ง๋ฃ ์๊ฐ ์ ์ ํ ์ค์
- ์ถ๊ฐ๋ก ๊ณ ๋ คํด์ผํ ๋ถ๋ถ์ ์๋์ ๊ฐ๋ค.
- CSP(Content Security Policy) ์ค์
- Rate Limiting ๋๋น
- ์๋ฌ ์ฒ๋ฆฌ ์ ๋ฏผ๊ฐ ์ ๋ณด ๋ ธ์ถ ์ฃผ์
Native App (Desktop,Mobild App) ๋ณดํธ
Desktop(CLI)/๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ๋ณดํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก๋ ์น ์ดํ๋ฆฌ์ผ์ด์ ํ๊ณ ๋น์ทํ๋ค.
์ธ๊ฐ์ฝ๋ํ๋ฆ(Authorization Code Flow)๋ฅผ ์ฌ์ฉํด์ผํ๋ฉฐ,
PKCE(Proof key for Code Exchange)๋ฅผ ํจ๊ป ์ฌ์ฉํ๋๊ฒ์ด ์ข๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก Keycloak ๋ก๊ทธ์ธ ํ์ด์ง๋ก Redirectํ์ฌ ์ฌ์ฉํ๋๊ฒ์ด ์ข๊ณ , ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๋ณ๋๋ก ๊ตฌ์ถํด์ ์ฌ์ฉํ์ง ์๋๊ฒ์ด ์ข๋ค.
์ธ์ฆ์ ์ํด ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด์ด ์ฌ์ฉ์์๊ฒ ์ธ์ฆ์ ์์ฒญํ๊ณ , ์ธ์ฆ ์ฝ๋๋ ํ ํฐ์ ๋ฐ์์ค๋ ๋ฐฉ์์ด ์์ ํ๋ฉฐ,
OAuth 2.0์์๋ ์ด๋ฅผ "Device Flow" ๋๋ "Authorization Code Flow with PKCE"๋ก ๊ตฌํํ ์ ์๋ค.
1๏ธโฃ ์ธ ๊ฐ์ง ์ธ์ฆ ์ ํ
๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ธ์ฆ์ ์ฒ๋ฆฌํ ๋, ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ ๋ฐ๋ผ ์๋ ์ธ๊ฐ์ง ์ ํ์ ์ฌ์ฉํ ์ ์๋ค.
์๋ฒ ๋๋ ์น ๋ทฐ (์ฌ์ฉX) | ์ฌ์ฉ์์ ๊ธฐ๋ณธ ๋ธ๋ผ์ฐ์ (์ธ๋ถ ์ฌ์ฉ์ ์์ด์ ํธ) |
์๋๋ก์ด๋ ๋ฐ iOS์ ์ธ์ฑ ๋ธ๋ผ์ฐ์ ํญ ์ฌ์ฉ | |
์ค๋ช | ์ ํ๋ฆฌ์ผ์ด์
๋ด์์ ์น ์ฝํ
์ธ ๋ฅผ ํ์ํ๊ธฐ ์ํด ์๋ฒ ๋๋ ์น ๋ทฐ๋ฅผ ์ฌ์ฉํ๋ค. ์ฌ์ฉ์๋ ์ฑ์ ๋ ๋์ง ์๊ณ ์ธ์ฆ ๊ณผ์ ์ ์๋ฃํ ์ ์๋ค. |
์ฌ์ฉ์์ ๊ธฐ๋ณธ ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด์ด ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค. ์ฌ์ฉ์๋ ๋ธ๋ผ์ฐ์ ์์ ์ธ์ฆ์ ์๋ฃํ ํ ์ฑ์ผ๋ก ๋์์จ๋ค. |
์๋๋ก์ด๋์ Chrome Custom Tabs๋ iOS์ Safari View Controller๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ ๋ด์์ ๋ธ๋ผ์ฐ์ ํญ์ ์ด์ด ์ธ์ฆ์ ์งํํ๋ค. |
์ฅ์ | ์ฌ์ฉ์ ๊ฒฝํ์ด ๋งค๋๋ฝ๊ณ , ์ฑ ๋ด์์ ๋ชจ๋ ์์ ์ด ์ด๋ฃจ์ด์ง๋ค. | ๋ธ๋ผ์ฐ์ ๋ ์ฑ๊ณผ ๋ถ๋ฆฌ๋์ด ์์ด ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ์ฑ์ ๋ ธ์ถ๋์ง ์๋๋ค. (๋ณด์๊ฐํ) | ์ฌ์ฉ์๋ ์ฑ์ ๋ ๋์ง ์์ง๋ง, ๋ธ๋ผ์ฐ์ ์ ๋ณด์ ์ด์ ์ ํ์ฉํ ์ ์์ด, ๋ณด์๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ท ํ์ ์ ๊ณตํ๋ค. |
๋จ์ | ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ์ฑ์ ๋ ธ์ถ๋ ์ ์์ด OAuth 2.0์์๋ ๊ถ์ฅ๋์ง ์๋๋ค. (๋ณด์ ๋ฌธ์ ) | ์ฌ์ฉ์๊ฐ ์ฑ์ ๋ ๋ ๋ธ๋ผ์ฐ์ ๋ก ์ด๋ํด์ผ ํ๊ธฐ ๋๋ฌธ์, ์ฌ์ฉ์ ๊ฒฝํ์ด ๋ค์ ๋๊ธธ ์ ์๋ค. | ํ๋ซํผ์ ๋ฐ๋ผ ๊ตฌํ์ด ๋ค๋ฅผ ์ ์๊ณ , ๋ชจ๋ ํ๋ซํผ์์ ์ง์๋์ง ์์ ์ ์๋ค. |
2๏ธโฃ ์ธ์ฆํ๋ฆ & ๋ฆฌ๋๋ ํธ URI ์ต์
Native App (Desktop,Mobild App)์ ์๋์ ๊ฐ์ ์ธ์ฆ ํ๋ฆ์ ๊ฐ์ง๋ค.
์ ํ๋ฆฌ์ผ์ด์
๋ธ๋ผ์ฐ์ Keycloak
| | |
|----(1) ๋ก๊ทธ์ธ -------------->|------------------------->|
| | |
| |<---(2) ์ฌ์ฉ์ ์ธ์ฆ----------|
| | |
|<---(3) ์ธ๊ฐ ์ฝ๋ ์ ๋ฌ---------|--------------------------|
| | |
|----(4) ํ ํฐ ํ๋--------------------------------------->|
# 1. ๋ก๊ทธ์ธ ์์ฒญ: ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด Keycloak์ ๋ก๊ทธ์ธ ์์ฒญ์ ๋ณด๋
# 2. ์ฌ์ฉ์ ์ธ์ฆ: ์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ์์ ์ธ์ฆ์ ์๋ฃํจ
# 3. ์ธ๊ฐ ์ฝ๋ ์ ๋ฌ: ๋ธ๋ผ์ฐ์ ๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ผ๋ก ์ธ๊ฐ ์ฝ๋๋ฅผ ์ ๋ฌํจ
# 4. ํ ํฐ ํ๋: ์ ํ๋ฆฌ์ผ์ด์
์ด ์ธ๊ฐ ์ฝ๋๋ฅผ ํ ํฐ์ผ๋ก ๊ตํํจ
์ด๋, ๋ค์ดํฐ๋ธ ์ฑ์ด ๋ธ๋ผ์ฐ์ ๋ก๋ถํฐ ์ธ์ฆ ์๋ต์ ๋ฐ์ ๋(3๋ฒ๋จ๊ณ) ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฆฌ๋๋ ํธ URI ์ต์
์ด ์กด์ฌํ๋ค.
(์ฐธ๊ณ : OAuth 2.0 for Native Apps)
- Claimed HTTPS scheme:
"https" ์คํด URI ๋ฆฌ๋๋ ์ ์ ์ผ๋ฐ์ ์ผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ ๋ฆฌ๋๋ ์ ๋ฐฉ์์ด๋ฉฐ, ๋ค์ดํฐ๋ธ ์ฑ์์๋ ์ฌ์ฉํ ์ ์๋ค. ์ฑ์ด ํน์ ๋๋ฉ์ธ์ "https" URI๋ฅผ ์ฒ๋ฆฌํ๋๋ก ํ์ฉํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ํด๋น URI๋ฅผ ๋ง๋๋ฉด ์ฑ์ด ์คํ๋๋ฉฐ, ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ ๋ค์ดํฐ๋ธ ์ฑ์์ ์ฝ๋ฐฑ์ด ์คํ๋๋ค.
(์, https://app.example.com/oauth2redirect/example-provider) - Custom URI scheme(Private-Use URI Scheme):
์ฌ์ฉ์ ์ง์ URI ๊ตฌ์กฐ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ๊ณ , Keycloak์ด ์ฌ์ฉ์ ์ง์ URI๋ก ๋ฆฌ๋ค์ด๋ ํธ ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฒญ์ด ์ ์ก๋๋ ๋ฐฉ์์ด๋ค.
์ฌ์ฉ์ ์ง์ URI ๊ตฌ์กฐ๋, ์์ ํ ๋๋ฉ์ธ์ ์ญ์๊ณผ ์ผ์นํด์ผ ํ๋ค. (์, "com.example.app://oauth2/providername" - Loopback interface:
๋ค์ดํฐ๋ธ ์ฑ์ด ์ด์๋๋ ์ปดํจํฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฃจํ๋ฐฑ(lookback) ๋คํธ์ํฌ ์ธํฐํ์ด์ค์ ํฌํธ๋ฅผ ์ด๊ณ ,
์ด ํฌํธ๋ฅผ ํตํด ์ธ์ฆ ์๋ต์ ๋ฐ๋๋ค.
(IPv4 ์, http://127.0.0.1:{port}/{path}) - A special redirect URI ( = Out of Band (OOB) ๋ฐฉ์/์ฐธ๊ณ ๋งํฌ):
์ด๊ฑด ์ง์ ๋์ง ์์ ๋ฐฉ๋ฒ์ ํตํด ๋ค์ด์ค๋ OAuth ์๋ต์ ์ง์ํ๊ธฐ ์ํ ๋ฐฉ์์ด๋ค.
๋ฆฌ๋๋ ์ ํ์ฌ ์๋์ผ๋ก ์ธ๊ฐ ์ฝ๋๋ฅผ ์ ๋ฌ ํ๋๊ฒ์ด ์๋๋ผ, ๋คํฐ์ด๋ธ์ฑ์ ์๋์ผ๋ก ์ ๋ ฅํด์ผํ๋ ์ฝ๋๊ฐ ์ฌ์ฉ์์๊ฒ ํ์๋๋ค.
์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ์์ ์ธ์ฆ์ ์๋ฃํ ํ, ์ธ์ฆ ์ฝ๋๋ฅผ ์ง์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ ฅํ๋ ๋ฐฉ์์ด๋ฉฐ, ์ฃผ๋ก ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ ์ ์๋ ํ๊ฒฝ์ด๋, ๋ฆฌ๋๋ ํธ URI๋ฅผ ๋ฑ๋กํ ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค.
(์, "์ธ์ฆ ์ฝ๋: 123456")
// ์์
// 1. HTTPS ์คํด์ ์ฌ์ฉํ ๋ฆฌ๋๋ ํธ URI
const redirectUri = 'https://app.example.com/oauth2redirect';
open(`https://auth.example.com/authorize?client_id=your_client_id&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`);
// 2. ์ฌ์ฉ์ ์ง์ URI ์คํด์ ์ฌ์ฉํ ๋ฆฌ๋๋ ํธ URI
const redirectUri = 'com.example.app:/oauth2redirect';
open(`https://auth.example.com/authorize?client_id=your_client_id&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`);
// 3. ๋ฃจํ๋ฐฑ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ ๋ฆฌ๋๋ ํธ URI
const port = 8000; // ๋๋ ์์์ ํฌํธ ์คํ
const redirectUri = `http://127.0.0.1:${port}/oauth2redirect`;
const express = require('express');
const app = express();
app.get('/oauth2redirect', (req, res) => {
const code = req.query.code;
console.log('Authorization Code:', code);
res.send('Authorization complete. You can close this window.');
});
app.listen(port, () => {
console.log(`Listening on port ${port}`);
open(`https://auth.example.com/authorize?client_id=your_client_id&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`);
});
// 4. ํน๋ณ ๋ฆฌ๋๋ ํธ(OOB) ๋ฐฉ์
const redirectUri = 'urn:ietf:wg:oauth:2.0:oob';
open(`https://auth.example.com/authorize?client_id=your_client_id&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`);
console.log('Please enter the authorization code:'); // ์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ์์ ์ธ์ฆ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์
๋ ฅ
process.stdin.on('data', (data) => {
const code = data.toString().trim();
console.log('Authorization Code:', code); // ์ฌ๊ธฐ์ ํ ํฐ ์์ฒญ ์งํ
});
3๏ธโฃ CLI๋ก ์ธ์ฆ ๋ฐ๊ธฐ ํ ์คํธ
* ํ ์คํธ ์์ค์ฝ๋: https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition
์ค์ต์ ์ํด ์ ํ ์คํธ ์ฝ๋๋ฅผ ๋ค์ด๋ฐ๊ณ ์๋์ ๊ฐ์ด ์์๋๋ก ์ค๋นํ๋ค.
# Keycloak ์ค๋น
docker run -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak \
start-dev
# Backend ์ดํ๋ฆฌ์ผ์ด์
์ค๋น
git clone https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition.git
cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch6
npm install
์ด์ Keycloak Client๋ฅผ ์์ฑ ํ๊ณ , ๋ก๊ทธ์ธํ ์ ์๋ ์ฌ์ฉ์(user)๋ฅผ ์ค๋นํ๋ค.
- client id: cli
- access type: public
- vaild redirect URLs: http://localhost/callback/
์ด์ ์ฝ๋๋ฅผ ์คํํด๋ณด์.
node app.js
์ฑ์ ์คํํ๋ฉด ๋ธ๋ผ์ฐ์ ์์ Keyclock ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ๋ฆฌ๋๋ ์ ๋๋ฉฐ, ์ฌ์ฉ์๋ก ์ธ์ฆ ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์๋ค.
RestAPI ๋ฐ Service ๋ณดํธ
1๏ธโฃ ๊ฐ์
์ ํ๋ฆฌ์ผ์ด์ ์ด Keycloak์ผ๋ก ๋ณดํธ๋๋ RestAPI๋ฅผ ํธ์ถํ๋ ค๋ ๊ฒฝ์ฐ, ๋จผ์ Keycloak์์ ์ ๊ทผ ํ ํฐ์ ์ป์ ๋ค์ RestAPI๋ก ๋ณด๋ด๋ ์์ฒญ์ ์ ๊ทผ ํ ํฐ์ ์๋์ ๊ฐ์ ํ์์ผ๋ก ํฌํจํด์ผ ํ๋ค.
fetch('https://api.example.com/data', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIs...'
}
});
์ ๊ทผ ํ ํฐ์ ์ป๋ ๋ฐฉ์์ ์ด ์ ํฌ์คํ
์ฃผ์ ๊ถํ ๋ถ์ฌ ์ ํ์์ ๋ค๋ฃจ์๋ค.
์น์ธ์ฝ๋ ์ ํ (Authorization Code Grant)์ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋๋ ์
๋์ด ์ฌ์ฉ์์ ID/PW๋ฅผ ์ด์ฉํ์ฌ ์ ๊ทผํ ํฐ์ ์ป๋๋ค.
์ด ๊ฒฝ์ฐ, API ์ฌ์ด์ ํต์ (์,MSA๊ตฌ์กฐ์์)์ธ ๊ฒฝ์ฐ์๋ ์ ๊ทผ ํ ํฐ์ ์ป๊ธฐ ์ด๋ ต๋ค.
์ด๋ ์ฌ์ฉํ ์ ์๋ ๊ถํ๋ถ์ฌ ์ ํ์ด ์ฑ ์ธ์ฆ ์ ํ(Client Credentials Grant) ์ด๋ค. (์ฐธ๊ณ ์๋ฃ: Client Credentials Grant)
+-------+ +------+
| API 1 |---[Client Credentials Grant]--->| API2 |
| | +------+
| | +------+
| |---[Client Credentials Grant]--->| API3 |
+-------+ +------+
์ด ๋ถ๋ถ๋ ์ด ์ ํฌ์คํ AccessToken ๊ฒ์ฆํ๊ธฐ์์ ์ ๊น ๋ค๋ฃจ์๋๋ฐ ์ฌ๊ธฐ์์ ๋ค์ํ๋ฒ ์ค๋ช ํ๋ค.
2๏ธโฃ ์ฑ ์ธ์ฆ ์ ํ(Client Credentials Grant) ํ ์คํธ
๋จผ์ Client๊ฐ ํ์๋ก Confidential๋ก ์ค์ ๋์ด์์ด์ผ ํ๋ค.
- client id: client-credentials
- access type: Confidential
- Service accounts roles: ON
Client (client-credentials) Settingํญ์์ Client authentication์ On์ผ๋ก ๋ณ๊ฒฝํ๋ค.
Service accounts roles๋ Enable ํ๋ค.
Saveํ๋ฉด Credentials ํญ์ด ์๊ธด๋ค.
Credentials ํญ์์ Client Secret ๊ฐ์ ๋ณต์ฌํ๋ค.
Access Token์ curl๋ก ๋ฐ์์ค์!
์๋ IrefKp5o3IJxKCBQXCsRYmLyWups8uVA ๋ถ๋ถ์ ์์์ ๋ณต์ฌํ Client Secret๊ฐ์ผ๋ก ๋ฐ๊ฟ์ผํ๋ค.
# Client Secret๊ฐ ๋ณ์๋ก ์ ์ฅ
export KEYCLOAK_CLIENT_SECRET=IrefKp5o3IJxKCBQXCsRYmLyWups8uVA # ์์์ ๋ณต์ฌํ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ
# Access Token ์์ฒญ
export KEYCLOAK_ACCESS_TOKEN=$(curl -X POST \
'http://localhost:8080/realms/myrealm/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "client_id=client-credentials" \
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
-d 'grant_type=client_credentials' | jq -r '.access_token')
์๋ํฌ์ธํธ๋ก ์์ฒญ์ ๋ณด๋ด๋ณด์.
# Base64 ์ธ์ฝ๋ฉ๋ Client Secret ์ ์ฅ
export KEYCLOAK_CLIENT_SECRET_ENCODING=$(echo -n "client-credentials:${KEYCLOAK_CLIENT_SECRET}" | base64)
# ๊ฐ ๋ชจ๋ ๋ค์ด์๋์ง ์ ๊ฒ
echo "Client Secret: $KEYCLOAK_CLIENT_SECRET"
echo "Access Token: $KEYCLOAK_ACCESS_TOKEN"
echo "Encoded Client Secret: $KEYCLOAK_CLIENT_SECRET_ENCODING"
# Access Token ๊ฒ์ฆ
curl -X POST \
'http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "Authorization: Basic $KEYCLOAK_CLIENT_SECRET_ENCODING" \
-d "token=$KEYCLOAK_ACCESS_TOKEN" | jq '.'