Framework Adapters
Pattern ทั่วไป
หัวข้อที่มีชื่อว่า “Pattern ทั่วไป”การผสาน framework ทุกตัวเป็นไปตาม two-role model เดียวกัน:
- Frontend / OIDC client: ไลบรารีใดก็ตามที่ implement Authorization Code + PKCE flow (keycloak-js สำหรับ browser, AppAuth สำหรับมือถือ, MSAL สำหรับ Microsoft stack เป็นต้น)
- Backend / resource server: ไลบรารีใดก็ตามที่ตรวจสอบ Bearer JWT กับ JWKS endpoint ของ realm
ประเด็นสำคัญคือ backend ไม่ต้องการไลบรารีเฉพาะของ Keycloak — ต้องการเพียงไลบรารีที่ตรวจสอบ JWT กับ JWKS URL ได้ ที่เป็นฟีเจอร์มาตรฐานของ ecosystem OAuth2/OIDC ที่โตแล้วทุกตัว
Spring Boot (Spring Security OAuth2 Resource Server)
หัวข้อที่มีชื่อว่า “Spring Boot (Spring Security OAuth2 Resource Server)”เพิ่ม dependency Spring Security OAuth2 Resource Server ใน pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId></dependency>จากนั้นชี้ Spring ไปยัง issuer URI ของ realm Spring จะดึง JWKS โดยอัตโนมัติ ตรวจสอบ JWT ที่เข้ามา และเติมข้อมูลใน SecurityContextHolder ด้วย claims ที่ถอดรหัสแล้ว:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8080/realms/my-appSpring Security หา JWKS URL โดยอัตโนมัติจาก issuer URI โดยดึงข้อมูลจาก OIDC discovery document ไม่ต้องการโค้ดเฉพาะของ Keycloak
การตั้งค่า security แบบ minimal เพื่อป้องกัน endpoint ทั้งหมด:
@Configuration@EnableWebSecuritypublic class SecurityConfig {
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); }}Node ด้วย express-oauth2-jwt-bearer
หัวข้อที่มีชื่อว่า “Node ด้วย express-oauth2-jwt-bearer”express-oauth2-jwt-bearer คือ middleware ที่ Auth0 สร้างขึ้นภายใต้ MIT license ห่อหุ้ม jose สำหรับ Express ติดตั้ง:
npm install express-oauth2-jwt-bearerimport { auth } from 'express-oauth2-jwt-bearer';import express from 'express';
const checkJwt = auth({ audience: 'my-app-backend', issuerBaseURL: 'http://localhost:8080/realms/my-app',});
const app = express();
app.get('/profile', checkJwt, (req, res) => { res.json({ sub: req.auth?.payload.sub });});Keycloak adapter รุ่นเก่าที่ถูก deprecated
หัวข้อที่มีชื่อว่า “Keycloak adapter รุ่นเก่าที่ถูก deprecated”Keycloak เคยมี adapter เฉพาะสำหรับแต่ละ framework: keycloak-connect สำหรับ Node, Keycloak Spring Boot adapter และอื่น ๆ adapter เหล่านี้ ถูก deprecated ตั้งแต่ Keycloak 19 และอาจถูกลบออกในที่สุด project ใหม่ควรใช้:
| Framework | ไลบรารีที่แนะนำ |
|---|---|
| Browser SPA | keycloak-js (official ยังคงได้รับการดูแล) |
| Spring Boot backend | Spring Security OAuth2 Resource Server |
| Node/Express backend | jose หรือ express-oauth2-jwt-bearer |
| Backend อื่น ๆ | JWT library ใดก็ได้ที่รองรับ JWKS |
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| ใช้ official Keycloak adapter (เช่น keycloak-js, keycloak-connect รุ่นเก่า) | Setup เร็ว มี API เฉพาะทางที่ครอบคลุม use case ของ Keycloak โดยตรง | ผูกกับ Keycloak เจาะจง — adapter ฝั่ง backend อย่าง keycloak-connect ถูก deprecated แล้ว และย้าย identity provider ภายหลังทำได้ยาก |
| implement OIDC resource-server middleware มาตรฐานเอง (jose, express-oauth2-jwt-bearer, Spring Security oauth2ResourceServer) | Portable ข้าม identity provider ใด ๆ ที่รองรับ OIDC/JWKS ได้รับการดูแลต่อเนื่องจาก community ที่กว้างกว่า | ต้องเข้าใจ OIDC spec เอง (issuer, audience, JWKS) แทนที่จะพึ่ง abstraction ของ adapter เฉพาะทาง |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- เชื่อ client-side auth state แทนการ verify ที่ backend — การเช็คว่า frontend มี token อยู่หรือ UI แสดงว่า “login แล้ว” ไม่ใช่ authentication ที่ backend ยอมรับได้ ทุก endpoint ที่ต้องการการป้องกันต้องผ่าน middleware ที่ verify JWT จริงเสมอ ไม่ใช่แค่เชื่อ header ที่ frontend ส่งมาว่า user login แล้ว
- ยังใช้ Keycloak adapter รุ่นเก่าที่ถูก deprecated — keycloak-connect และ Keycloak Spring Boot adapter รุ่นเก่าไม่ได้รับฟีเจอร์ใหม่แล้ว โปรเจกต์ใหม่ควรย้ายไปใช้ OAuth2/OIDC resource server support มาตรฐานของ framework แทน
- ผสม responsibility ระหว่าง authentication กับ authorization ใน middleware เดียว — การตรวจสอบว่า token ถูกต้อง (authentication) กับการเช็คว่า user มีสิทธิ์เข้าถึง resource นั้นหรือไม่ (authorization) ควรแยกเป็นคนละ layer หากผูกรวมกันในที่เดียวจะทดสอบและ debug ยากขึ้นมากเมื่อ role/policy เปลี่ยน
💡 ตัวอย่างจากของจริง
Spring Security oauth2ResourceServer — เป็นตัวอย่าง production-grade ของการใช้ middleware มาตรฐานแทน Keycloak adapter เฉพาะทาง เพียงกำหนด issuer-uri ก็ทำงานได้กับ identity provider ใด ๆ ที่รองรับ OIDC ไม่ใช่แค่ Keycloak
express-oauth2-jwt-bearer (Auth0) — แม้สร้างโดย Auth0 แต่ใช้ verify JWT ของ Keycloak ได้เช่นกัน เพราะยึดตาม OIDC/JWKS มาตรฐาน สะท้อนว่า resource-server middleware ที่ดีไม่ควรผูกกับ vendor ใด vendor หนึ่ง