๐ŸŒฑ Infra/KeyCloak

[keycloak ๋ง›๋ณด๊ธฐ #3] ์ ‘๊ทผ๊ถŒํ•œ์ธ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ

mini_world 2024. 12. 1. 20:36
๋ชฉ์ฐจ ์ ‘๊ธฐ

 


์ฐธ๊ณ ์ž๋ฃŒ

* ์ฑ…: https://www.yes24.com/Product/Goods/122459785

* ํ…Œ์ŠคํŠธ ์†Œ์Šค์ฝ”๋“œ: https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition

 


์‹ค์Šต ์ค€๋น„) Keycloak & ํ…Œ์ŠคํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ธํŒ…

 

Keycloak ์ค€๋น„

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/ch5/backend
npm install
npm start

# Frontend ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch5/frontend
npm install
npm start

# ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ ‘์†
http://localhost:8000

 

Keycloak Client ์ค€๋น„

  • client id: oauth-playground
  • access type: public
  • vaild redirect URLs: http://localhost:8000/
  • web origin: http://localhost:8000

์ด๋•Œ Keyclock client์—์„œ access type์„ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ฐพ์„ ์ˆ˜ ์—†์„ํ…๋ฐ, 
์•„๋ž˜ ์บก์ณ์ฒ˜๋Ÿผ Client authentication๊ฐ€ OFF ๋˜์–ด์žˆ์œผ๋ฉด ๋œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์ด OFF์ด๋ฏ€๋กœ ์„ค์ •์„ ๋ฐ”๊ฟ€ํ•„์š” ์—†๋‹ค.

 

Keycloak Users ์ค€๋น„

Clients ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋Š” User๋ฅผ ์ƒ์„ฑํ•ด๋‘”๋‹ค.
ํ…Œ์ŠคํŠธ ์œ ์ €๋Š” ์ž„์˜ ID๋กœ ์ƒ์„ฑํ•œ ํ›„ (์—ฌ๊ธฐ์—์„œ๋Š” user01๋กœ ์ƒ์„ฑํ–ˆ๋‹ค), Credentialsํƒญ์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•œ๋‹ค.

์ดํ›„ Realm roles์—์„œ myrole์„ ์ƒ์„ฑํ•œ๋‹ค. 

myrole ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ role์„ ์ƒ์„ฑํ–ˆ๋‹ค๋ฉด, ์œ„์—์„œ ์ƒ์„ฑํ•œ user01์— ๋‹ค์‹œ ์ ‘์†ํ•˜์—ฌ Role Mapping์—์„œ myrole์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

์ด ์ž‘์—…์„ ํ•˜๋Š” ์ด์œ ๋Š” ch5/backend/app.js ์ฝ”๋“œ์—์„œ keycloak.protect('realm:myrole')๋กœ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ myrole์— ๋งคํ•‘๋˜์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด Invoke service ๋‹จ๊ณ„์—์„œ ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•œ๋‹ค.

 

 


์‹ค์Šตํ•˜๊ธฐ์ „์—) Access Token์˜ ์ ‘๊ทผ๊ถŒํ•œ ์ œํ•œ์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ธฐ

AccessToken์€ ๋ฆฌ์†Œ์Šค์„œ๋ฒ„(์‹ค์Šต์—์„œ๋Š” Backend)์— ์ ‘๊ทผํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” Token์ด๋‹ค.
์ด ํ† ํฐ์œผ๋กœ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ ‘๊ทผ๊ถŒํ•œ์„ ์ œ์–ดํ•˜๋Š”๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด AccessToken์˜ ์ ‘๊ทผ ๊ถŒํ•œ ์ œ์–ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•ด ํ™•์ธํ•˜๊ณ  ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€์ž!

Keycloak Authorization Service Guide

 

์ ‘๊ทผ์ œํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์„ค๋ช…
Attribute-based access control
(ABAC)
์‚ฌ์šฉ์ž, ๋ฆฌ์†Œ์Šค, ํ™˜๊ฒฝ์˜ ์†์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘๊ทผ์„ ์ œ์–ด
์˜ˆ: ์‚ฌ์šฉ์ž์˜ ๋ถ€์„œ, ์ง๊ธ‰, ์œ„์น˜ ๋“ฑ์˜ ์†์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘๊ทผ ๊ถŒํ•œ ํ—ˆ์šฉ
Role-based access control
(RBAC)
์‚ฌ์šฉ์ž์—๊ฒŒ ํ• ๋‹น๋œ ์—ญํ• ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘๊ทผ์„ ์ œ์–ด
์˜ˆ: Realm Role์— ๋งคํ•‘๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ๊ถŒํ•œ ํ—ˆ์šฉ
User-based access control
(UBAC)
ํŠน์ • ์‚ฌ์šฉ์ž๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘๊ทผ์„ ์ œ์–ด (๊ฐœ๋ณ„ ์‚ฌ์šฉ์ž ๋‹จ์œ„์˜ ์„ธ๋ฐ€ํ•œ ๊ถŒํ•œ ์ œ์–ด ๊ฐ€๋Šฅ)
์˜ˆ, user01์—๊ฒŒ๋งŒ ์ ‘๊ทผ ๊ถŒํ•œ ํ—ˆ์šฉ
Context-based access control
(CBAC)
์š”์ฒญ์˜ ์ปจํ…์ŠคํŠธ(์‹œ๊ฐ„, ์œ„์น˜, ๋””๋ฐ”์ด์Šค ๋“ฑ)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘๊ทผ์„ ์ œ์–ด
์˜ˆ: ํŠน์ • IP ๋ฒ”์œ„์—์„œ๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉ
Rule-based access control: JavaScript๋ฅผ ์‚ฌ์šฉํ•œ ์ปค์Šคํ…€ ๊ทœ์น™ ์ •์˜ (๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ)
Time-based access control: ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜์˜ ์ ‘๊ทผ ์ œ์–ด (ํŠน์ • ์‹œ๊ฐ„๋Œ€๋‚˜ ๊ธฐ๊ฐ„ ๋™์•ˆ๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉ)
Custom ACMs through SPI: Service Provider Interface๋ฅผ ํ†ตํ•œ ์ปค์Šคํ…€ ์ ‘๊ทผ ์ œ์–ด ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
(ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ž์ฒด ์ •์ฑ… ํƒ€์ž…์„ ๊ฐœ๋ฐœํ•˜์—ฌ ํ™•์žฅ ๊ฐ€๋Šฅ)

์ด๋Ÿฐ ์ ‘๊ทผ ๊ถŒํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ์ดํ•ดํ•˜๊ณ  ์žˆ์–ด์•ผํ•  Keycloak ๊ฐœ๋…์ด ์žˆ๋‹ค.
์•„๋ž˜ ์‹ค์Šต์—์„œ๋„ ๊ณ„์†ํ•ด์„œ ๋ณผ ์˜ˆ์ •์ด๋‹ค.

  • Role:
    • Role์€ ์‹œ์Šคํ…œ ๋‚ด์—์„œ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์„ ์ •์˜ํ•œ๋‹ค
    • RBAC ๊ตฌํ˜„์— ์‚ฌ์šฉ๋œ๋‹ค.
  • Scpoe:
    • ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์ด๋‚˜ ๊ถŒํ•œ์˜ ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•œ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, read, write, delete ๋“ฑ์˜ ์ž‘์—…์„ Scope๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Audience (aud):
    • JWT ํ† ํฐ์˜ aud ํด๋ ˆ์ž„์„ ๋งํ•œ๋‹ค.
    • ํ† ํฐ์ด ์‚ฌ์šฉ๋  . ์ˆ˜์žˆ๋Š” ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„ ํ˜น์€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ง€์ •ํ•œ๋‹ค.
    • ํŠน์ • ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„๋งŒ ํ•ด๋‹น ํ† ํฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค

 

 


์‹ค์Šต #1) ๋ฐฑ์—”๋“œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๊ธฐ (Role ํ™œ์šฉ ํ™•์ธํ•˜๊ธฐ)

์‹ค์Šตํ•˜๊ธฐ

http://localhost:8000 ์— ์ ‘์†ํ•˜๊ณ , 2 - Authorization๋ฅผ ํด๋ฆญํ•œ๋‹ค.
Send Authorization Request๋ฅผ ํด๋ฆญํ•˜๋ฉด Access Token ๋ฐ›๊ฒŒ ๋œ๋‹ค. ํŽ˜์ด์ง€์—์„œ ์„ธ๋ถ€ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

3 - Invoke Service ๋กœ ๋“ค์–ด๊ฐ€์„œ Invvoke๋ฅผ ํด๋ฆญํ•˜๋ฉด Response๊ฐ’์œผ๋กœ Secret message!๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ดํ•ดํ•˜๊ธฐ

์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” Frontend์—์„œ Backend์„œ๋ฒ„์— AccessToken์„ ํ†ตํ•ด ์ ‘๊ทผํ•˜๋Š” ๋ถ€๋ถ„์„ ์‹ค์Šตํ•œ๊ฒƒ์ด๋‹ค.
๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ํ‘œ๊ธฐํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • Access Token ๊ฒ€์ฆ (6-7๋‹จ๊ณ„)
  • Role ํ™•์ธ (8๋‹จ๊ณ„)
  • ๊ถŒํ•œ์ด ํ™•์ธ๋œ ๊ฒฝ์šฐ์—๋งŒ Secret Message! ๋ฐ˜ํ™˜ (9๋‹จ๊ณ„)
[ํด๋ผ์ด์–ธํŠธ(frontend)]       [Keycloak]           [๋ฆฌ์†Œ์Šค ์„œ๋ฒ„(backend)]
     โ”‚                        โ”‚                        โ”‚
     โ”‚   1. ์ธ์ฆ ์š”์ฒญ           โ”‚                        โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚                        โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚   2. Authorization Codeโ”‚                        โ”‚
     โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚                        โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚   3. Token ๊ตํ™˜         โ”‚                        โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚                        โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚   4. Access Token      โ”‚                        โ”‚
     โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚                        โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚           5. Access Token์œผ๋กœ ๋ฆฌ์†Œ์Šค ์š”์ฒญ           โ”‚
     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚                        โ”‚    6. Token ๊ฒ€์ฆ ์š”์ฒญ    โ”‚
     โ”‚                        โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚                        โ”‚    7. Token ์œ ํšจ์„ฑ ์‘๋‹ต   โ”‚
     โ”‚                        โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
     โ”‚                        โ”‚                        โ”‚
     โ”‚                        โ”‚   8. realm:myrole      โ”‚
     โ”‚                        โ”‚        ๊ถŒํ•œ ํ™•์ธ         โ”‚
     โ”‚                        โ”‚      <โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
     โ”‚                        โ”‚              โ”‚         โ”‚
     โ”‚             9. Secret Message! ์‘๋‹ต              โ”‚
     โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚

 

 


์‹ค์Šต #2) ์ ‘๊ทผ๊ถŒํ•œ ๋ถ€์—ฌ ๋™์˜(Concent) ์š”์ฒญํ•˜๊ธฐ (Scope ํ™œ์šฉ ํ™•์ธํ•˜๊ธฐ)

 

์‹ค์Šตํ•˜๊ธฐ

http://localhost:8080 Keycloak ์— ์ ‘์†ํ•˜๊ณ , Clients -> oauth-playground ์—์„œ ์•„๋ž˜ ์„ค์ •์„ ๋ณ€๊ฒฝํ•œ๋‹ค.

  • Consent required: On
  • Display client on screen: On
  • Consent screen text: ๊ถŒํ•œ(Concent) ๋ถ€์—ฌ ๋™์˜ ์š”์ฒญ

์ด์ œ ๋‹ค์‹œ http://localhost:8000/๋กœ ์ ‘๊ทผํ•˜๊ณ  2-Authorization์—์„œ ๋‹ค์‹œํ•œ๋ฒˆ Send Authorization Request๋ฅผ ํด๋ฆญํ•œ๋‹ค.
์ด๋ฏธ ๋กœ๊ทธ์ธํ•œ ์„ธ์…˜์ด ์žˆ๋Š” ๊ฒฝ์šฐ Grant Access ํŒ์—…์ด ๋‚˜์˜ค๊ณ , ๋กœ๊ทธ์ธ์„ธ์…˜์ด ์—†๋Š”๊ฒฝ์šฐ์—๋Š” ๋‹ค์‹œํ•œ๋ฒˆ ๋กœ๊ทธ์ธ ํ•˜๋„๋ก ํ•œ๋‹ค.

์ด์ œ ์ด ์‚ฌ์šฉ์ž์— Consents๋ฅผ ํ™•์ธํ•ด๋ณด์ž.
http://localhost:8080 Keycloak ์— ์ ‘์†ํ•˜๊ณ , Users -> user01 -> Consentsํƒญ์„ ํ™•์ธํ•ด๋ณด์ž.
์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ถ€์—ฌ๋œ ๊ถŒํ•œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒˆ์—๋Š” ์ƒˆ๋กœ์šด Client Scope๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ถ”๊ฐ€ํ•ด๋ณด์ž.
Client Scopes์—์„œ ์ƒˆ๋กœ์šด Client Scope๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  • Name: (์•„๋ฌด๊ฑฐ๋‚˜) my-custom-scope
  • Display on consent screen: On

์ด์ œ Clients -> oauth-playground -> Client Scopeํƒญ์œผ๋กœ ์ด๋™ํ•˜๊ณ , ์œ„์—์„œ ์ƒ์„ฑํ•œ my-custom-scope๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

์ด์ œ ๋‹ค์‹œ http://localhost:8000/๋กœ ์ ‘๊ทผํ•˜๊ณ  ๋‹ค์‹œํ•œ๋ฒˆ Send Authorization Request๋ฅผ ํด๋ฆญํ•œ๋‹ค.
์ด๋•Œ ์œ„์—์„œ ๋™์˜์š”์ฒญ ํŽ˜์ด์ง€์—์„œ ์ถ”๊ฐ€ํ•œ ์ƒˆ๋กœ์šด Scope๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž์—๊ฒŒ ์–ด๋–ค Scope๊ฐ€ ํ—ˆ์šฉ๋˜์—ˆ๋Š”์ง€๋Š” Users -> ์‚ฌ์šฉ์ž -> Consents ํƒญ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Scope๋Š” Keycloak User์˜ Consents ํƒญ์—์„œ ์ œ๊ฑฐํ• ์ˆ˜๋„ ์žˆ๋‹ค.
์ œ๊ฑฐํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ๋™์˜๋ฅผ ๋‹ค์‹œ ๋ฐ›์•„์•ผ ํ•œ๋‹ค.

 

์ดํ•ดํ•˜๊ธฐ

Scope (๊ถŒํ•œ ๋ฒ”์œ„)๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์˜ ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•œ๋‹ค.

  • openid: OIDC ์ธ์ฆ ์š”์ฒญ
  • profile: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด
  • email: ์ด๋ฉ”์ผ ์ •๋ณด
  • address: ์ฃผ์†Œ ์ •๋ณด
  • phone: ์ „ํ™”๋ฒˆํ˜ธ ์ •๋ณด

Consent (์‚ฌ์šฉ์ž ๋™์˜)๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ํด๋ผ์ด์–ธํŠธ์˜ ์Šค์ฝ”ํ”„ ์š”์ฒญ์— ๋Œ€ํ•œ ๋™์˜๋ฅผ ๋ฐ›๋Š” ๊ณผ์ •์„ ๋งํ•˜๋ฉฐ, 
์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์Šน์ธํ•˜๋Š”๊ฒƒ์ด๋‹ค.

[Client(frontend)]     [Auth Server(Keycloak)]  [Resource Server(backend)]
   โ”‚                         โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€ 1. Scope ์š”์ฒญ โ”€โ”€โ”€โ”€โ”€>โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚<โ”€โ”€โ”€ 2. Consent Screen โ”€โ”€โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€ 3. ์‚ฌ์šฉ์ž ๋™์˜ โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚<โ”€โ”€โ”€ 4. Auth Code โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€ 5. Code ๊ตํ™˜ โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚<โ”€โ”€โ”€ 6. Access Token โ”€โ”€โ”€โ”€โ”‚                           โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 7. Access Token์œผ๋กœ ์š”์ฒญ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
   โ”‚                         โ”‚                           โ”‚
   โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 8. Protected Resource โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚

Scope๋ฅผ Sample์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์—ฌ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (์ƒ˜ํ”Œ์ฝ”๋“œ์—์„œ๋Š” ๊ตฌํ˜„๋˜์–ด์žˆ์ง€ ์•Š๋‹ค)

# AS-IS
app.get('/secured', keycloak.protect('realm:myrole'), function (req, res) {
  res.setHeader('content-type', 'text/plain');
  res.send('Secret message!');
});

# TO-BE
# ๋‹จ์ผ ์Šค์ฝ”ํ”„ ํ™•์ธ
app.get('/secured', keycloak.protect('scope:my-custom-scope'), function (req, res) {
  res.send('Secret message!');
});
# ์—ฌ๋Ÿฌ ์Šค์ฝ”ํ”„ ํ™•์ธ
app.get('/secured', keycloak.protect(['scope:my-custom-scope', 'scope:another-scope']), function (req, res) {
  res.send('Secret message!');
});

 

 


์‹ค์Šต #3) Audience๋กœ ์ ‘๊ทผ๊ถŒํ•œ ์ œํ•œ ํ•˜๊ธฐ (Audience ํ™œ์šฉ ํ™•์ธํ•˜๊ธฐ)

 

์‹ค์Šตํ•˜๊ธฐ

ch5/backend/keycloak.json ํŒŒ์ผ์„ ์ˆ˜์ •ํ•œ๋‹ค.

{
  "realm": "myrealm",
  "bearer-only": true,
  "auth-server-url": "${env.KC_URL:http://localhost:8080}",
  "resource": "oauth-playground",  // ์œ„์—์„œ ์ƒ์„ฑํ•œ Client์ด๋ฆ„์œผ๋กœ ์ˆ˜์ •
  "verify-token-audience": true    // true๋กœ ์ˆ˜์ •
}
  • realm: Keycloak์—์„œ ์ธ์ฆ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋…ผ๋ฆฌ์  ๋‹จ์œ„
  • bearer-only: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค๋กœ๋งŒ ์‚ฌ์šฉ๋  ๋•Œ ์„ค์ •
                           true๋กœ ์„ค์ •ํ•˜๋ฉด Keycloak์ด ์ด ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•ด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Œ
  • auth-server-url: Keycloak ์ธ์ฆ ์„œ๋ฒ„์˜ URL
  • resource: ํด๋ผ์ด์–ธํŠธ ID, Keycloak์—์„œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•จ
  • verify-token-audience: ํ† ํฐ์˜ aud(Audience) ํด๋ ˆ์ž„์„ ๊ฒ€์ฆํ• ์ง€ ์—ฌ๋ถ€ ์„ค์ •
                                               true๋กœ ์„ค์ •ํ•˜๋ฉด ํ† ํฐ์ด ์˜ฌ๋ฐ”๋ฅธ ๋Œ€์ƒ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•ด ๋ฐœ๊ธ‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•จ

 

์ด์ œ http://localhost:8000/ ์— ์ ‘๊ทผํ•˜์—ฌ Send Authorization Request๋ฅผ ํด๋ฆญํ•˜์—ฌ Access Token์„ ๋ฐœ๊ธ‰๋ฐ›๊ณ 
3-Invoke Service์—์„œ Invoke๋ฅผ ํด๋ฆญํ•ด๋ณด์ž.

์ด๋•Œ Access Deny๊ฐ€ ๋ณด์ธ๋‹ค.

"resource": "oauth-playground"๋Š” ํด๋ผ์ด์–ธํŠธ ID๋ฅผ ์˜๋ฏธํ•˜๊ณ , "verify-token-audience": true๋กœ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ
"aud" ๊ฐ’์— ClientID๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ account๋งŒ ์žˆ์–ด์„œ Access Deny๊ฐ€ ๋‚˜์™”๋‹ค.

 

์ด์ œ aud๊ฐ’์— ClientID๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ์ˆ˜์ •ํ•ด๋ณด์ž

๋จผ์ €, Client(http://localhost:8080/admin/master/console/#/myrealm/clients/) ์—์„œ oauth-playground์„ ํด๋ฆญํ•œ๋‹ค.
Clients scope ํƒญ์—์„œ oauth-playground-dedicated๋ฅผ ํด๋ฆญํ•œ๋‹ค. (ํด๋ผ์ด์–ธํŠธ๋ณ„๋กœ ๊ณ ์œ ํ•œ ๊ถŒํ•œ์ด๋‚˜ ์†์„ฑ์„ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉ)

Mapper ํƒญ์—์„œ Configure a new mapper๋ฅผ ํด๋ฆญํ•˜๊ณ , Audience๋ฅผ ์„ ํƒํ•œ๋‹ค.

client id์™€ ๊ฐ™์€ ์ด๋ฆ„(oauth-playground)์˜ Audiance type์˜ ๋งคํผ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 

 

์ด์ œ ๋‹ค์‹œํ•œ๋ฒˆ  http://localhost:8000/ ์— ์ ‘๊ทผํ•˜์—ฌ Send Authorization Request๋ฅผ ํด๋ฆญํ•˜์—ฌ Access Token์„ ๋ฐœ๊ธ‰๋ฐ›๊ณ 
3-Invoke Service์—์„œ Invoke๋ฅผ ํด๋ฆญํ•ด๋ณด์ž.

์ด์ œ aud ํด๋ ˆ์ž„์— ClientID์™€ ๊ฐ™์€ ๊ฐ’์ด ๋“ค์–ด์žˆ์–ด Access ๊ฐ€ ํ—ˆ์šฉ ๋˜์—ˆ๋‹ค.

 

 

Audience ์ดํ•ดํ•˜๊ธฐ

verify-token-audience ๊ฐ’์„ False ์—์„œ True๋กœ ์ˆ˜์ •ํ•˜๋ฉด, Audience ๊ฒ€์ฆ ํ™œ์„ฑํ™”๋œ๋‹ค.
Audience ๊ฒ€์ฆ์€ ์ž˜๋ชป๋œ ๋Œ€์ƒ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค. ํ† ํฐ์ด ์˜๋„๋œ ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋„๋ก ๋ณด์žฅํ•œ๋‹ค.

  • ๋ณ€๊ฒฝ ์ „ (false): Audience ๊ฒ€์ฆ ๋น„ํ™œ์„ฑํ™”
    • ํ† ํฐ์˜ aud(Audience) ํด๋ ˆ์ž„์„ ๊ฒ€์ฆํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ํ† ํฐ์ด ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋ฐœ๊ธ‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ์— ์ทจ์•ฝํ•˜๋‹ค.
  • ๋ณ€๊ฒฝ ํ›„ (true): Audience ๊ฒ€์ฆ ํ™œ์„ฑํ™”
    • ํ† ํฐ์˜ aud ํด๋ ˆ์ž„์„ ๊ฒ€์ฆํ•œ๋‹ค.
    • ํ† ํฐ์ด ์˜ฌ๋ฐ”๋ฅธ ๋Œ€์ƒ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•ด ๋ฐœ๊ธ‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž์‹ ์—๊ฒŒ ๋ฐœ๊ธ‰๋œ ํ† ํฐ๋งŒ ์ˆ˜๋ฝํ•˜๊ฒŒ ๋˜์–ด ๋ณด์•ˆ์ด ๊ฐ•ํ™”๋œ๋‹ค.

 

 


์‹ค์Šต #4) Role๋กœ ์ ‘๊ทผ๊ถŒํ•œ ์ œํ•œ ํ•˜๊ธฐ (Role ํ™œ์šฉ ํ™•์ธํ•˜๊ธฐ)

 

Client์˜ Full scope allowed ์ดํ•ดํ•˜๊ธฐ

Full scope allowed ์„ค์ •์€ ํด๋ผ์ด์–ธํŠธ ์Šค์ฝ”ํ”„์˜ ๊ถŒํ•œ ๋ฒ”์œ„๋ฅผ ์ œ์–ดํ•˜๋Š” ์ค‘์š”ํ•œ ์˜ต์…˜์ด๋‹ค.
Full scope allowed๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ON์œผ๋กœ ๋˜์–ด์žˆ๋Š”๋ฐ, ์ด์œ ๋Š” ์ดˆ๊ธฐ ์„ค์ • ์‹œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ชจ๋“  ์—ญํ• ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ ์„ค์ •์˜ ํŽธ์˜์„ฑ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. 
ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ณด์•ˆ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ์—ญํ• ๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ๋งคํ•‘ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.

  • Full scope allowed๊ฐ€ ON์ธ ๊ฒฝ์šฐ:
    • ํด๋ผ์ด์–ธํŠธ๋Š” ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์—ญํ• (Realm ์—ญํ• ๊ณผ ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ์˜ ์—ญํ•  ํฌํ•จ)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ณ„๋„์˜ ์—ญํ•  ์Šค์ฝ”ํ”„ ๋งคํ•‘ ์—†์ด๋„ ๋ชจ๋“  ์—ญํ• ์— ๋Œ€ํ•œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ๋ณด์•ˆ์ƒ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•œ ์„ค์ •์ด๋ฉฐ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.
  • Full scope allowed๊ฐ€ OFF์ธ ๊ฒฝ์šฐ:
    • ํด๋ผ์ด์–ธํŠธ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ๋งคํ•‘๋œ ์—ญํ• ์—๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์Šค์ฝ”ํ”„ ๋งคํ•‘์„ ํ†ตํ•ด ํŠน์ • ์—ญํ• ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
# Full scope allowed: ON
{
  "realm_access": {
    "roles": ["role1", "role2", "role3"] #realm๋‚ด์˜ ๋ชจ๋“  ์—ญํ•  ์ ‘๊ทผ ๊ฐ€๋Šฅ
  }
}

# Full scope allowed: OFF
{
  "realm_access": {
    "roles": ["role1"] # client์— ๋ช…์‹œ์ ์œผ๋กœ ๋งคํ•‘๋œ ์—ญํ• ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  }
}

 

์‹ค์Šตํ•˜๊ธฐ

๋จผ์ €, ์œ„์—์„œ ์„ค๋ช…ํ•œ๋ฐ๋กœ Full scope allowed ๋ฅผ OFF๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค. (์œ„ ์Šคํฌ๋ฆฐ์ƒท ์ฐธ๊ณ )
ON์—์„œ OFF๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ AccessToken์ด ๋ณ€๊ฒฝ๋œ๋‹ค.

์‹ค์Šต์ค€๋น„ ๋‹จ๊ณ„์—์„œ ์–ธ๊ธ‰ํ•œ๋ฐ๋กœ,
ch5/backend/app.js ์ฝ”๋“œ์—์„œ๋Š” keycloak.protect('realm:myrole')๋กœ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์žˆ๋‹ค.

app.get('/secured', keycloak.protect('realm:myrole'), function (req, res) {
  res.setHeader('content-type', 'text/plain');
  res.send('Secret message!');
});

myrole์— ๋งคํ•‘๋˜์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด Invoke service ๋‹จ๊ณ„์—์„œ ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•œ๋‹ค.

 

์ด์ œ myrole์„ ๋ช…์‹œ์ ์œผ๋กœ Client์— ์—ฐ๊ฒฐํ•ด๋ณด์ž.

 

Client์— realm_access Role ๋งคํ•‘๋ฐฉ๋ฒ• 1) {client id}-dedicated(์ „์šฉ์Šค์ฝ”ํ”„)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Role ๋งคํ•‘

oauth-playground์—์„œ Client scope -> {client id}-dedicated -> Scope -> Assign role -> myrole -> Assign์œผ๋กœ myrole์„ ๋งคํ•‘ํ•œ๋‹ค.

์ด์ œ http://localhost:8000/์—์„œ Access Token์„ ํ™•์ธํ•ด๋ณด์ž. myrole์ด ํฌํ•จ๋˜์–ด์žˆ๊ณ  Invoke๋„ ์„ฑ๊ณตํ–ˆ๋‹ค.

 

์„ค๋ช… ํด๋ผ์ด์–ธํŠธ์— ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ์ „์šฉ ์Šค์ฝ”ํ”„({client id}-dedicated)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ญํ• ์„ ๋งคํ•‘ํ•œ๋‹ค.
์ด ๋ฐฉ๋ฒ•์€ ํŠน์ • ํด๋ผ์ด์–ธํŠธ์—๋งŒ ์ ์šฉ๋˜๋Š” ์ „์šฉ ์„ค์ •์„ ์‚ฌ์šฉํ•œ๋‹ค.
์žฅ์  ํด๋ผ์ด์–ธํŠธ๋ณ„๋กœ ๊ณ ์œ ํ•œ ์„ค์ •์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
๋‹จ์  ์ „์šฉ ์Šค์ฝ”ํ”„์— ์˜์กดํ•˜๋ฏ€๋กœ, ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ์— ๋™์ผํ•œ ์„ค์ •์„ ์ ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

 

 

Client์— realm_access Role ๋งคํ•‘๋ฐฉ๋ฒ• 2) Client Scope๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Client์— Role ๋งคํ•‘

Client Scope์—์„œ ์ƒˆ๋กœ์šด ClientScope๋ฅผ ์ƒ์„ฑํ•˜๊ณ , Scopeํƒญ์—์„œ myrole์„ ์—ฐ๊ฒฐํ•œ๋‹ค.

์ด์ œ Client (oauth-playground)์— ์œ„์—์„œ ์ƒ์„ฑํ•œ Client Scope๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

๊ทธ๋Ÿผ ์ด์ œ ๋‹ค์‹œํ•œ๋ฒˆ ์‹คํ–‰ํ•ด๋ณด์ž.
๋งˆ์ฐฌ๊ฐ€์ง€๋กœ realm_access.role์— myrole์ด ์ถ”๊ฐ€๋˜์–ด์žˆ๊ณ  Invoke๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค.

 

์„ค๋ช… ์ƒˆ๋กœ์šด Client Scope๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ•ด๋‹น ์Šค์ฝ”ํ”„์— ์—ญํ• ์„ ๋งคํ•‘ํ•œ ํ›„, ํด๋ผ์ด์–ธํŠธ์— ์ด ์Šค์ฝ”ํ”„๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
์žฅ์  ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋™์ผํ•œ ์„ค์ •์„ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์Šค์ฝ”ํ”„๋ฅผ ํ†ตํ•ด ์—ญํ• ๊ณผ ๊ถŒํ•œ์„ ์ค‘์•™์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
๋‹จ์  ์ ์šฉํ•œ ํด๋ผ์ด์–ธํŠธ์— ๋™์ผํ•œ ์Šค์ฝ”ํ”„๊ฐ€ ์ ์šฉ๋˜๋ฏ€๋กœ, ๊ฐœ๋ณ„ ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•œ ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค.

 

์‹ค์Šต #5) Scope ํ™œ์šฉํ•˜๊ธฐ (Scope ๋ช…๋ช…๊ทœ์น™/ Default&Optional)

 

Scope ๋ช…๋ช…๊ทœ์น™

Scope์˜ ๋ช…๋ช…๊ทœ์น™์€ ์ •ํ•ด์ง„ ํ‘œ์ค€์ด ์—†๋‹ค. ๋ฆฌ์†Œ์Šค์™€ ์ž‘์—…์„ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ทœ์น™์„ ์ •ํ•˜๋Š”๊ฒƒ์ด ๊ถŒํ•œ๊ด€๋ฆฌ์— ์šฉ์ด๋‹ค.
์˜ˆ๋ฅผ๋“ค์–ด resource:action ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. (AWS IAM Policy๋ฅผ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ์Œ)

  •    ๊ธฐ๋ณธ์ ์ธ CRUD ์ž‘์—…
    • users:read
    • users:write
    • users:delete
    • users:update
  • ์„ธ๋ถ€์ ์ธ ๊ถŒํ•œ๊ตฌ๋ถ„
    • users:profile:read
    • users:settings:write
  • ํŠน์ • ๋ฆฌ์†Œ์Šค์˜ ๋ชจ๋“  ๊ถŒํ•œ: resource:*
  • ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์˜ ์ฝ๊ธฐ ๊ถŒํ•œ: *:read
  • ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์˜ ๋ชจ๋“  ๊ถŒํ•œ: *:*

 

Scope ์œ ํ˜• (Default/ Optional)

Keycloak์˜ Client Scope์—๋Š” Default์™€ Optional ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ๋‹ค.

  • Default
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์š”์ฒญํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ํฌํ•จ๋˜๋Š” ์Šค์ฝ”ํ”„
    • ๋ชจ๋“  ํ† ํฐ ์š”์ฒญ์— ๊ธฐ๋ณธ์ ์œผ๋กœ ํฌํ•จ๋จ
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ scope ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™ ํฌํ•จ
    • ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ์ž ์ •๋ณด๋‚˜ ๊ถŒํ•œ์— ์‚ฌ์šฉ
  • Optional
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์š”์ฒญํ•  ๋•Œ๋งŒ ํฌํ•จ๋˜๋Š” ์Šค์ฝ”ํ”„
    • scope ํŒŒ๋ผ๋ฏธํ„ฐ์— ํฌํ•จ์‹œ์ผœ์•ผ ํ† ํฐ์— ํฌํ•จ๋จ
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ ์š”์ฒญ
    • ์ถ”๊ฐ€์ ์ธ ๊ถŒํ•œ์ด๋‚˜ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ

 

์‹ค์Šตํ•˜๊ธฐ

๋‚ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— Read, Write๊ถŒํ•œ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” Read๋ฅผ ํ•„์š”ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ Write๊ถŒํ•œ์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

๋จผ์ € Role์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • myapp:read  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ Role์ด๋ผ๊ณ  ๊ฐ€์ •
  • myapp:write  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์“ฐ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ Role์ด๋ผ๊ณ  ๊ฐ€์ •

๋ฐฉ๊ธˆ ์ƒ์„ฑํ•œ Role์„ User์— ๋งคํ•‘ํ•œ๋‹ค.
์ง€๊ธˆ์€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์ด๋‹ˆ ์œ ์ €์— Role์„ ๋งคํ•‘ํ–ˆ์ง€๋งŒ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” Group์„ ๋งŒ๋“ค์–ด ํ•ด๋‹น ๊ทธ๋ฃน์— Role์„ ๋งคํ•‘ํ•˜๋Š”๊ฒƒ์ด ์ข‹๋‹ค.

 

์ด์ œ Client Scope๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  • myapp:read  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ๋ฒ”์œ„๋ผ๊ณ  ๊ฐ€์ •
  • myapp:write  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์“ฐ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ๋ฒ”์œ„๋ผ๊ณ  ๊ฐ€์ •

์ด์ œ Client Scope์— ๊ฐ๊ฐ ์˜ฌ๋ฐ”๋ฅธ Role์„ ๋งคํ•‘ํ•œ๋‹ค.

  • myapp:read  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ๋ฒ”์œ„๋ผ๊ณ  ๊ฐ€์ •
  • myapp:write  ํ…Œ์ŠคํŠธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์“ฐ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ๋ฒ”์œ„๋ผ๊ณ  ๊ฐ€์ •

 

์ด์ œ Client(oauth-playground) -> Client Scopeํƒญ์—์„œ ์œ„์—์„œ ์ƒ์„ฑํ•œ ๋‘๊ฐœ ๋ชจ๋‘ ์ถ”๊ฐ€ํ•œ๋‹ค.
์ด๋•Œ, read๋Š” Default๋กœ, write๋Š” Optional๋กœ ์„ค์ •ํ•œ๋‹ค.

์ด์ œ http://localhost:8000/์—์„œ Send Authorization Request ์ƒ์„ฑ์‹œ ์ƒˆ๋กœ์šด ๊ถŒํ•œ ํ—ˆ์šฉ์ด ๋‚˜์˜ค๊ณ , 
ํ—ˆ์šฉํ•˜๋ฉด realm_access์— ์ƒˆ๋กœ์šด myapp:read ์—ญํ• ์ด ์ถ”๊ฐ€๋œ๊ฒƒ์„ ๋ณผ ์ˆ˜์žˆ๋‹ค.

์ด์ œ Write๊ถŒํ•œ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์š”์ฒญ์„ ์ƒ์„ฑํ•œ๋‹ค.
Scope์— myapp:write๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•˜๊ณ , Send Authorization Request๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
realm_access์— ์ƒˆ๋กœ์šด myapp:write ์—ญํ• ์ด ์ถ”๊ฐ€๋œ๊ฒƒ์„ ํ™•์ธํ•œ๋‹ค.

\

 

์ถ”๊ฐ€์„ค๋ช…) Role์€ User(or Group) ๊ทธ๋ฆฌ๊ณ  Scope์— ๋ชจ๋‘ ํ• ๋‹น๋˜์–ด์žˆ์–ด์•ผํ•œ๋‹ค.

Role์€ User์™€ Scope์— ๋ชจ๋‘ ํ• ๋‹น๋˜์–ด์žˆ์–ด์•ผ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
User -> Role -> Scope -> Client ์˜ ์—ฐ๊ฒฐ ๊ณ ๋ฆฌ๊ฐ€ ๋ชจ๋‘ ์™„์„ฑ๋˜์–ด์•ผ ์ •์ƒ์ ์œผ๋กœ ์ ‘๊ทผ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
ํ”„๋กœ๋•์…˜์—์„œ๋Š” User๋Œ€์‹  Group์„ ์‚ฌ์šฉํ•˜์ž!

  • Client Scope์— Role ํ• ๋‹น
    • ๋ชฉ์ : ํ•ด๋‹น ์Šค์ฝ”ํ”„๊ฐ€ ์–ด๋–ค ์—ญํ• ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ •์˜
    • ์„ค์ • ์œ„์น˜: Client Scope > {scope-name} > Scope ํƒญ
    • ์˜๋ฏธ: "์ด ์Šค์ฝ”ํ”„๋Š” ์ด๋Ÿฌํ•œ ์—ญํ• ๋“ค์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค"
  • User์— Role ํ• ๋‹น
    • ๋ชฉ์ : ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋–ค ์—ญํ• ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ์ •์˜
    • ์„ค์ • ์œ„์น˜: Users > {user-name} > Role Mappings
    • ์˜๋ฏธ: "์ด ์‚ฌ์šฉ์ž๋Š” ์ด๋Ÿฌํ•œ ์—ญํ• ๋“ค์„ ๊ฐ€์ง„๋‹ค"

 


์‹ค์Šต #6) Access Token ๊ฒ€์ฆํ•˜๊ธฐ

 

์ด์ œ Introspect ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ด์šฉํ•ด์„œ Access Token์„ ๊ฒ€์ฆํ•ด๋ณธ๋‹ค.

# http://localhost:8080/realms/myrealm/.well-known/openid-configuration ์—์„œ 
# introspection_endpointํ™•์ธ
"introspection_endpoint": "http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect"

 

Introspection_endpoint ์ดํ•ดํ•˜๊ธฐ

Token Introspection ์—”๋“œํฌ์ธํŠธ๋Š” OAuth 2.0 ํ† ํฐ์˜ ์ƒํƒœ์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ‘œ์ค€ ์—”๋“œํฌ์ธํŠธ์ด๋‹ค.
AccessToken์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๊ณ , ํ† ํฐ์˜ ํ˜„์žฌ์ƒํƒœ(active/inactive) ํ™•์ธ์„ ํ•ด๋‹น ์—”๋“œํฌ์ธํŠธ์—์„œ ์ง„ํ–‰ํ•œ๋‹ค.

# ์š”์ฒญ์˜ˆ์‹œ
curl -X POST \
'http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Authorization: Basic {์—ฌ๊ธฐ์—_Base64_์ธ์ฝ”๋”ฉ๋œ_๊ฐ’}' \
-d 'token={your_access_token}'

# ์‘๋‹ต์˜ˆ์‹œ
{
 "active": true,                    // ํ† ํฐ ์œ ํšจ์„ฑ
 "exp": 1678901234,                // ๋งŒ๋ฃŒ ์‹œ๊ฐ„
 "sub": "user-id",                 // ์‚ฌ์šฉ์ž ID
 "username": "user01",             // ์‚ฌ์šฉ์ž๋ช…
 "scope": "myapp:read myapp:write",// ์Šค์ฝ”ํ”„
 "client_id": "oauth-playground",  // ํด๋ผ์ด์–ธํŠธ ID
 "token_type": "Bearer"            // ํ† ํฐ ํƒ€์ž…
}

 

์‹ค์Šตํ•˜๊ธฐ

๋จผ์ € Client๊ฐ€ Confidential๋กœ ์„ค์ •๋˜์–ด์žˆ์–ด์•ผ ํ•œ๋‹ค.

Client (oauth-playground) Settingํƒญ์—์„œ Client authentication์„ On์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
Service accounts roles๋„ Enable ํ•œ๋‹ค.
Saveํ•˜๋ฉด Credentials ํƒญ์ด ์ƒ๊ธด๋‹ค. 

Credentials ํƒญ์—์„œ Client Secret ๊ฐ’์„ ๋ณต์‚ฌํ•œ๋‹ค.

Access Token์„ curl๋กœ ๋ฐ›์•„์˜ค์ž! (Credentials ์„ค์ •์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด http://localhost:8000/์—์„œ๋Š” AccessToken๋ฐœ๊ธ‰ ์•ˆ๋จ)
์•„๋ž˜ 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=oauth-playground" \
  -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 "oauth-playground:${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