๐ŸŒฑ Infra/KeyCloak

[keycloak ๋ง›๋ณด๊ธฐ #4] Keycloak์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ

mini_world 2024. 12. 9. 00:26
๋ชฉ์ฐจ ์ ‘๊ธฐ

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณดํ˜ธ 

 

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๏ธโƒฃ ๊ณตํ†ต์ ์œผ๋กœ ์ ์šฉํ•ด์•ผํ•˜๋Š” ๋ณด์•ˆ ์„ค์ •

  1. ์ธ๊ฐ€์ฝ”๋“œํ๋ฆ„(Authorization Code Flow) ์‚ฌ์šฉ (= Standard Flow)
    • ์ธ๊ฐ€์ฝ”๋“œํ๋ฆ„(Authorization Code Flow)์€ OAuth 2.0์˜ ๊ถŒ์žฅ๋˜๋Š” ํ๋ฆ„ํžˆ๋‹ค.
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ํ† ํฐ์„ ์–ป๋Š” ๋ฐฉ์‹์ด๋‹ค.
    • ์•ก์„ธ์Šค ํ† ํฐ์ด ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•˜์ง€ ์•Š๊ณ  ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์—์„œ ์ง์ ‘ ๊ตํ™˜๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณด์•ˆ์„ฑ์ด ๊ฐ€์žฅ ๋†’์€ ์ธ์ฆ๋ฐฉ์‹์ด๋‹ค.
    • ์ฃผ์˜์‚ฌํ•ญ: ํด๋ผ์ด์–ธํŠธ ์‹œํฌ๋ฆฟ์ด ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ ํ•ด์•ผํ•œ๋‹ค.
  2. PKCE(Proof key for Code Exchange) ์‚ฌ์šฉ
    • ์ธ๊ฐ€์ฝ”๋“œ(Authorization Code)๋ฅผ ์ธํ„ฐ์…‰ํŠธ ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ์ˆ 
    • Keycloak์—์„œ๋Š” Client > Advanced ํƒญ > Advanced settings > Proof Key for Code Exchange Code Challenge Method ์—์„œ ํ™œ์„ฑํ™” ํ•œ๋‹ค. (S256 ๊ถŒ์žฅ) 
  3. Keycloak ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ™œ์šฉ
    • keycloak์—์„œ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ์ด์šฉํ•˜๋Š”๊ฒƒ์ด ์ข‹๋‹ค. 
    • ๊ธฐ์กด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์œ ์ง€ (Resource Owner Password Credential) โŒ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๊ฒƒ์ด ์ข‹๋‹ค.โŒ
      ๋‹จ์ผ ์•ฑ ๋ณด์•ˆ ์นจํ•ด๋กœ ์ „์ฒด ์‹œ์Šคํ…œ์ด ์œ„ํ—˜ํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค.
    • iframe ๋‚ด Keycloak ๋กœ๊ทธ์ธ โŒ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๊ฒƒ์ด ์ข‹๋‹ค.โŒ
      ํด๋ฆญ์žฌํ‚น, ํ”ผ์‹ฑ๊ณต๊ฒฉ(์ถœ์ฒ˜ํ™•์ธ์–ด๋ ค์›€)์— ์ทจ์•ฝํ•˜๋ฉฐ, 
      ๋ธŒ๋ผ์šฐ์ €์˜ ์„œ๋“œํŒŒํ‹ฐ ์ฟ ํ‚ค ์ฐจ๋‹จ์œผ๋กœ์ธํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  4. Valid Redirect URIs ์ œํ•œ
    • ์ธ์ฆ ํ›„ Redirect ๊ฐ€๋Šฅํ•œ URI๋ชฉ๋ก์„ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์ด๋ฉฐ, ์ •ํ™•ํ•œ URI๋กœ Redirect๋˜๋„๋ก ์„ค์ •ํ•ด์•ผํ•œ๋‹ค. 
    • ์˜ˆ) https://your-app.com/callback/*
    • ์ ์ ˆํ•˜์ง€ ์•Š์€ ์„ค์ •์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ OpenRedirect ์ทจ์•ฝ์ ์ด ์ƒ๊ธด๋‹ค.
  5. Web Origins ์„ค์ •
    • CORS ์š”์ฒญ์„ ํ—ˆ์šฉํ•  ์˜ค๋ฆฌ์ง„์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด๋ฉฐ, ํด๋ผ์ด์–ธํŠธ์˜ ๋„๋ฉ”์ธ์„ ์ œํ•œํ•œ๋‹ค.
    • ํ—ˆ๊ฐ€๋˜์ง€ ์•Š์€ ๋„๋ฉ”์ธ์—์„œ์˜ ์š”์ฒญ์„ ์ฐจ๋‹จํ•œ๋‹ค.
  6. ํ† ํฐ ๋ผ์ดํ”„ํƒ€์ž„ ์„ค์ •
    • ์•ก์„ธ์Šค ํ† ํฐ๊ณผ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•˜์—ฌ ์„ธ์…˜ ์ง€์† ์‹œ๊ฐ„์„ ์ œ์–ดํ•œ๋‹ค.
    • Refresh Token์€ ์žฌ์‚ฌ์šฉ ํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.
    • ํ† ํฐ ํƒˆ์ทจ ์‹œ ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • Access Token: 5-15๋ถ„
      • Refresh Token: 8-24์‹œ๊ฐ„
      • SSO Session: 8-10์‹œ๊ฐ„
  7. 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๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์ถœ๋œ ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ์•…์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • 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 ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • 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๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์ถœ๋œ ์ธ๊ฐ€์ฝ”๋“œ๋ฅผ ์•…์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • 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 ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • 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)

  1. Claimed HTTPS scheme:
    "https" ์Šคํ‚ด URI ๋ฆฌ๋””๋ ‰์…˜์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ๋ฆฌ๋””๋ ‰์…˜ ๋ฐฉ์‹์ด๋ฉฐ, ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ฑ์ด ํŠน์ • ๋„๋ฉ”์ธ์˜ "https" URI๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด๋‹น URI๋ฅผ ๋งŒ๋‚˜๋ฉด ์•ฑ์ด ์‹คํ–‰๋˜๋ฉฐ, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹Œ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์—์„œ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋œ๋‹ค.
    (์˜ˆ, https://app.example.com/oauth2redirect/example-provider)
  2. Custom URI scheme(Private-Use URI Scheme):
    ์‚ฌ์šฉ์ž ์ง€์ • URI ๊ตฌ์กฐ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋“ฑ๋กํ•˜๊ณ , Keycloak์ด ์‚ฌ์šฉ์ž ์ง€์ • URI๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์š”์ฒญ์ด ์ „์†ก๋˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
    ์‚ฌ์šฉ์ž ์ง€์ • URI ๊ตฌ์กฐ๋Š”, ์†Œ์œ ํ•œ ๋„๋ฉ”์ธ์˜ ์—ญ์ˆœ๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค. (์˜ˆ, "com.example.app://oauth2/providername"
  3. Loopback interface:
    ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์ด ์šด์˜๋˜๋Š” ์ปดํ“จํ„ฐ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฃจํ”„๋ฐฑ(lookback) ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค์— ํฌํŠธ๋ฅผ ์—ด๊ณ ,
    ์ด ํฌํŠธ๋ฅผ ํ†ตํ•ด ์ธ์ฆ ์‘๋‹ต์„ ๋ฐ›๋Š”๋‹ค.
    (IPv4 ์˜ˆ, http://127.0.0.1:{port}/{path})
  4. 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 '.'

 

 

 

728x90