- 1. Kong API Gateway คืออะไร
- 2. JWT Authentication คืออะไร
- 3. สถาปัตยกรรมของ Workshop
- 4. JWT Plugin ไม่ได้ทำหน้าที่ Login
- 5. สิ่งที่ต้องติดตั้ง
- 6. สร้าง Project
- 7. สร้าง REST API ด้วย Node.js + Express
- 8. สร้าง REST API
- 9. ความสัมพันธ์ระหว่าง iss กับ Kong JWT Credential
- 10. สร้าง Dockerfile
- 11. สร้าง Docker Compose
- 12. Port ที่ใช้ใน Workshop
- 13. Start ระบบ
- 14. ตรวจสอบ Log
- 15. ตรวจสอบ Kong Admin API
- 16. ทดสอบ Backend API จากภายใน Docker Network
- 17. สร้าง Kong Service
- 18. สร้าง Public Route
- 19. สร้าง Protected Route
- 20. ตรวจสอบ Route
- 21. ทดสอบ Login ก่อนเปิด JWT Plugin
- 22. ทดสอบ Products ก่อนเปิด JWT
- 23. Kong Consumer คืออะไร
- 24. สร้าง Consumer
- 25. สร้าง JWT Credential
- 26. ทำไม key ต้องตรงกับ iss
- 27. เปิด JWT Plugin ให้ api-route
- 28. ตรวจสอบ JWT Plugin
- 29. ทดสอบ Protected API โดยไม่ส่ง JWT
- 30. ขอ JWT Token
- 31. เรียก Protected API ด้วย Bearer Token
- 32. ตัวอย่าง Response
- 33. Kong ส่ง Consumer Header ไปยัง Backend
- 34. ทดสอบ /profile
- 35. ทดสอบ Token ไม่ถูกต้อง
- 36. ทดสอบ Token ที่ถูกแก้ Payload
- 37. ทดสอบบน Windows PowerShell
- 38. ทดสอบด้วย Postman
- 39. Postman Tests เพื่อเก็บ Token อัตโนมัติ
- 40. ดู Services ทั้งหมด
- 41. ดู Routes ทั้งหมด
- 42. ดู Consumers ทั้งหมด
- 43. ดู JWT Credentials
- 44. ดู Plugins
- 45. Plugin สามารถติดตั้งระดับใดได้บ้าง
- 46. Sequence Diagram
- 47. Error: 404 no Route matched with those values
- 48. Error: 502 Bad Gateway
- 49. Error: JWT ถูกสร้างแล้วแต่ยัง 401
- 50. Error: Token หมดอายุ
- 51. Reset Workshop
- 52. ทำไม Workshop นี้ใช้ HS256
- 53. Production ควรพิจารณา RS256
- 54. JWT กับ OAuth 2.0 / OpenID Connect ไม่ใช่สิ่งเดียวกัน
- 55. Security Checklist สำหรับ Production
- 56. Workshop Challenge
- 57. สรุปองค์ประกอบสำคัญ
- 58. Final Architecture
- 59. สรุป
- References
การนำ REST API ไปใช้งานจริงไม่ได้มีเพียงการสร้าง Endpoint ให้ Client เรียกใช้งานเท่านั้น แต่ยังต้องจัดการเรื่อง Authentication, Security, Routing, Rate Limiting, Logging และการควบคุมการเข้าถึง API ด้วย
แนวทางหนึ่งที่นิยมคือการวาง API Gateway ไว้ด้านหน้า Backend API เพื่อให้ Gateway เป็นจุดกลางในการรับ Request ก่อนส่งต่อไปยัง Service ภายในระบบ
บทความนี้เป็น Workshop แบบลงมือทำ โดยจะเริ่มตั้งแต่
- สร้าง REST API ด้วย Node.js + Express
- Dockerize REST API
- ติดตั้ง Kong Gateway และ PostgreSQL ด้วย Docker Compose
- สร้าง Kong Service
- สร้าง Public Route และ Protected Route
- สร้าง Kong Consumer
- สร้าง JWT Credential
- เปิดใช้งาน JWT Plugin
- ขอ JWT จาก Authentication Endpoint
- ส่ง JWT ผ่าน
Authorization: Bearer - ทดสอบกรณี Token ถูกต้อง/ไม่ถูกต้อง
- ตรวจสอบ Header ที่ Kong ส่งต่อให้ Backend
หมายเหตุเรื่องเวอร์ชัน: Workshop นี้ใช้
kong:3.9.3ซึ่งเป็น Kong OSS Docker Official Image ที่เผยแพร่อยู่ในชุด OSS ณ วันที่จัดทำบทความ (30 กันยายน 2026) เพื่อให้สามารถทดลองได้โดยไม่ต้องใช้ Kong Enterprise license
#1. Kong API Gateway คืออะไร
Kong Gateway ทำหน้าที่เป็น Reverse Proxy/API Gateway อยู่ระหว่าง Client และ Backend API
Client
|
v
Kong API Gateway
|
+-- Authentication
+-- Routing
+-- Rate Limiting
+-- Logging
+-- Security Plugins
|
v
Backend API
แทนที่ Client จะเรียก Backend โดยตรง เช่น
http://backend:3000/products
Client จะเรียกผ่าน Kong เช่น
http://localhost:8000/api/products
จากนั้น Kong จะตรวจสอบ Request และส่งต่อไปยัง Backend
#2. JWT Authentication คืออะไร
JWT หรือ JSON Web Token เป็น Token ที่ประกอบด้วย 3 ส่วน
HEADER.PAYLOAD.SIGNATURE
ตัวอย่าง Payload
{
"iss": "demo-client",
"sub": "student",
"role": "user",
"iat": 1790740000,
"exp": 1790743600
}
Claim ที่สำคัญใน Workshop นี้คือ
| Claim | ความหมาย |
|---|---|
iss |
ผู้ออก Token หรือ Key ที่ Kong ใช้ค้นหา Credential |
sub |
Subject หรือผู้ใช้ที่ Token แทน |
iat |
เวลาที่ Token ถูกสร้าง |
exp |
เวลาหมดอายุ |
Kong JWT Plugin จะตรวจสอบ Signature ของ JWT ก่อนอนุญาตให้ Request ผ่านไปยัง Backend API
เอกสาร Kong ระบุว่า JWT Plugin รองรับการตรวจ JWT ที่ลงนามด้วยอัลกอริทึม เช่น HS256 และ RS256 และสามารถตรวจ registered claims อย่าง exp และ nbf ได้
#3. สถาปัตยกรรมของ Workshop
เราจะสร้างระบบดังนี้
flowchart LR
C["Client / Postman"]
subgraph KONG["Kong API Gateway :8000"]
AUTH["Public Route<br/>/auth"]
APIROUTE["Protected Route<br/>/api"]
JWT["JWT Plugin"]
CON["Consumer<br/>student-client"]
end
APP["Node.js REST API<br/>api:3000"]
DB["PostgreSQL<br/>Kong Database"]
C -->|"POST /auth/token"| AUTH
AUTH --> APP
C -->|"GET /api/products<br/>Bearer JWT"| APIROUTE
APIROUTE --> JWT
JWT --> CON
JWT -->|"Valid"| APP
KONG --- DB
เราจะแยก Route เป็นสองกลุ่ม
| Route | ตัวอย่าง | JWT |
|---|---|---|
| Public | /auth/token |
ไม่ต้องใช้ |
| Protected | /api/products |
ต้องใช้ |
#4. JWT Plugin ไม่ได้ทำหน้าที่ Login
จุดที่มักเข้าใจผิดคือ Kong JWT Plugin ไม่ได้เป็น Login Server
JWT Plugin มีหน้าที่หลักคือ
รับ JWT
|
v
อ่าน Claim
|
v
ค้นหา Consumer Credential
|
v
ตรวจ Signature
|
v
ตรวจ exp / nbf ตาม Configuration
|
+---- Invalid ---> 401
|
+---- Valid -----> Backend API
ดังนั้นเรายังต้องมีระบบที่ทำหน้าที่ออก JWT
ใน Workshop นี้ เราจะสร้าง Endpoint
POST /token
ใน Node.js API เพื่อจำลอง Authentication Service
#5. สิ่งที่ต้องติดตั้ง
เครื่องที่ใช้ Workshop ควรมี
- Docker
- Docker Compose
- cURL หรือ Postman
- Text Editor เช่น Visual Studio Code
ตรวจสอบ Docker
docker --version
ตรวจสอบ Docker Compose
docker compose version
#6. สร้าง Project
สร้างโฟลเดอร์
mkdir kong-jwt-workshop
cd kong-jwt-workshop
สร้างโฟลเดอร์ API
mkdir api
โครงสร้าง Project จะเป็น
kong-jwt-workshop/
├── docker-compose.yml
└── api/
├── Dockerfile
├── package.json
└── app.js
#7. สร้าง REST API ด้วย Node.js + Express
สร้างไฟล์
api/package.json
{
"name": "kong-jwt-demo-api",
"version": "1.0.0",
"description": "REST API for Kong JWT workshop",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^5.1.0",
"jsonwebtoken": "^9.0.2"
}
}
#8. สร้าง REST API
สร้างไฟล์
api/app.js
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;
const DEMO_USERNAME =
process.env.DEMO_USERNAME || "student";
const DEMO_PASSWORD =
process.env.DEMO_PASSWORD || "1234";
const JWT_KEY =
process.env.JWT_KEY || "demo-client";
const JWT_SECRET =
process.env.JWT_SECRET || "super-secret-change-me";
// ---------------------------------------------------
// Public endpoint
// ---------------------------------------------------
app.get("/", (req, res) => {
res.json({
message: "Kong JWT Workshop API",
status: "running"
});
});
// ---------------------------------------------------
// Login / Token endpoint
// ---------------------------------------------------
app.post("/token", (req, res) => {
const {
username,
password
} = req.body;
if (
username !== DEMO_USERNAME ||
password !== DEMO_PASSWORD
) {
return res.status(401).json({
message: "Invalid username or password"
});
}
const token = jwt.sign(
{
iss: JWT_KEY,
sub: username,
role: "user"
},
JWT_SECRET,
{
algorithm: "HS256",
expiresIn: "1h"
}
);
res.json({
access_token: token,
token_type: "Bearer",
expires_in: 3600
});
});
// ---------------------------------------------------
// Protected API
// ---------------------------------------------------
app.get("/products", (req, res) => {
res.json({
message: "Protected products API",
kong_consumer: {
id:
req.headers["x-consumer-id"] || null,
username:
req.headers["x-consumer-username"] || null,
custom_id:
req.headers["x-consumer-custom-id"] || null,
credential:
req.headers["x-credential-identifier"] || null
},
data: [
{
id: 1,
name: "Mechanical Keyboard",
price: 2490
},
{
id: 2,
name: "Wireless Mouse",
price: 1290
},
{
id: 3,
name: "USB-C Hub",
price: 1590
}
]
});
});
app.get("/profile", (req, res) => {
res.json({
message: "Protected profile API",
consumer:
req.headers["x-consumer-username"] || null
});
});
app.listen(
PORT,
"0.0.0.0",
() => {
console.log(
`API running on port ${PORT}`
);
}
);
#9. ความสัมพันธ์ระหว่าง iss กับ Kong JWT Credential
ใน Code มี
{
iss: JWT_KEY
}
และกำหนด
JWT_KEY=demo-client
ดังนั้น JWT Payload จะมี
{
"iss": "demo-client"
}
ภายหลังเราจะสร้าง JWT Credential ใน Kong โดยกำหนด
key = demo-client
จึงต้องจำความสัมพันธ์นี้ไว้
JWT Payload
iss = demo-client
==
Kong JWT Credential
key = demo-client
#10. สร้าง Dockerfile
สร้างไฟล์
api/Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
#11. สร้าง Docker Compose
กลับมาที่ Root ของ Project
kong-jwt-workshop/
สร้างไฟล์
docker-compose.yml
services:
kong-db:
image: postgres:17-alpine
container_name: kong-db
restart: unless-stopped
environment:
POSTGRES_DB: kong
POSTGRES_USER: kong
POSTGRES_PASSWORD: kong
volumes:
- kong_db_data:/var/lib/postgresql/data
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U kong -d kong"
]
interval: 5s
timeout: 5s
retries: 10
networks:
- kong-net
kong-migrations:
image: kong:3.9.3
container_name: kong-migrations
command: kong migrations bootstrap
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: kong
depends_on:
kong-db:
condition: service_healthy
networks:
- kong-net
restart: "no"
kong:
image: kong:3.9.3
container_name: kong
restart: unless-stopped
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: kong
KONG_PROXY_LISTEN:
0.0.0.0:8000
KONG_ADMIN_LISTEN:
0.0.0.0:8001
depends_on:
kong-migrations:
condition: service_completed_successfully
ports:
- "8000:8000"
- "8001:8001"
networks:
- kong-net
api:
build:
context: ./api
container_name: demo-api
restart: unless-stopped
environment:
PORT: 3000
DEMO_USERNAME: student
DEMO_PASSWORD: "1234"
JWT_KEY: demo-client
JWT_SECRET: super-secret-change-me
networks:
- kong-net
volumes:
kong_db_data:
networks:
kong-net:
driver: bridge
#12. Port ที่ใช้ใน Workshop
| Port | หน้าที่ |
|---|---|
8000 |
Kong Proxy |
8001 |
Kong Admin API |
3000 |
Backend API ภายใน Docker Network |
5432 |
PostgreSQL ภายใน Docker Network |
Client จะเรียก API ผ่าน
http://localhost:8000
ไม่ใช่
http://localhost:3000
เพราะเราไม่ได้ Publish Port ของ Backend API ออกมาที่ Host
นี่เป็นการบังคับให้ Request ผ่าน API Gateway
#13. Start ระบบ
รัน
docker compose up -d --build
ตรวจสอบ Container
docker compose ps
ควรพบอย่างน้อย
kong-db
kong
demo-api
ส่วน
kong-migrations
จะทำงานเพียงครั้งเดียวเพื่อสร้าง Database Schema แล้วจบการทำงาน
#14. ตรวจสอบ Log
ดู Kong Log
docker compose logs -f kong
ดู API Log
docker compose logs -f api
#15. ตรวจสอบ Kong Admin API
เรียก
curl http://localhost:8001
ถ้าระบบทำงานถูกต้อง Kong จะตอบข้อมูล Gateway กลับมา
Port
8001เป็น Admin API จึงไม่ควรเปิดสู่ Public Internet ในระบบ Production
#16. ทดสอบ Backend API จากภายใน Docker Network
สามารถทดสอบจาก Kong Container ได้ เช่น
docker exec kong \
curl http://api:3000
ถ้าสำเร็จควรได้
{
"message": "Kong JWT Workshop API",
"status": "running"
}
ชื่อ
api
มาจาก Service Name ใน Docker Compose
Docker DNS จะ Resolve ชื่อนี้ไปยัง Container ของ API
#17. สร้าง Kong Service
Kong Service ใช้แทน Backend หรือ Upstream API
เราจะสร้าง Service ชื่อ
demo-api
รัน
curl -i \
-X POST \
http://localhost:8001/services \
--data name=demo-api \
--data url=http://api:3000
ตรวจสอบ
curl \
http://localhost:8001/services/demo-api
Architecture ตอนนี้คือ
Kong
|
v
Service: demo-api
|
v
http://api:3000
#18. สร้าง Public Route
สร้าง Route สำหรับ Authentication
curl -i \
-X POST \
http://localhost:8001/services/demo-api/routes \
--data name=auth-route \
--data 'paths[]=/auth' \
--data strip_path=true \
--data 'protocols[]=http' \
--data 'protocols[]=https'
เมื่อ Client เรียก
POST /auth/token
Kong จะตัด /auth ออก
เพราะ
strip_path=true
จึงส่ง Request ไป Backend เป็น
POST /token
Flow
POST http://localhost:8000/auth/token
|
v
Kong
|
strip /auth
|
v
http://api:3000/token
#19. สร้าง Protected Route
สร้าง Route สำหรับ Business API
curl -i \
-X POST \
http://localhost:8001/services/demo-api/routes \
--data name=api-route \
--data 'paths[]=/api' \
--data strip_path=true \
--data 'protocols[]=http' \
--data 'protocols[]=https'
ดังนั้น
GET /api/products
จะถูกส่งไปยัง
GET /products
ที่ Backend
#20. ตรวจสอบ Route
curl \
http://localhost:8001/routes
หรือดูเฉพาะ Route ของ Service
curl \
http://localhost:8001/services/demo-api/routes
#21. ทดสอบ Login ก่อนเปิด JWT Plugin
เรียก
curl \
-X POST \
http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{
"username": "student",
"password": "1234"
}'
ตัวอย่าง Response
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
#22. ทดสอบ Products ก่อนเปิด JWT
curl \
http://localhost:8000/api/products
ขณะนี้ยังสามารถเรียกได้ เพราะเรายังไม่ได้เปิด JWT Plugin
#23. Kong Consumer คืออะไร
Consumer เป็น Entity ที่ Kong ใช้แทน Client หรือ Application ที่ได้รับอนุญาตให้เรียก API
ตัวอย่าง
Web Frontend
Mobile Application
Partner Application
Internal Service
IoT Device
Workshop นี้สร้าง Consumer ชื่อ
student-client
#24. สร้าง Consumer
curl -i \
-X POST \
http://localhost:8001/consumers \
--data username=student-client \
--data custom_id=student-app
ตรวจสอบ
curl \
http://localhost:8001/consumers/student-client
#25. สร้าง JWT Credential
สร้าง JWT Credential ให้ Consumer
curl -i \
-X POST \
http://localhost:8001/consumers/student-client/jwt \
--data algorithm=HS256 \
--data key=demo-client \
--data secret=super-secret-change-me
ค่าใน Workshop
algorithm = HS256
key = demo-client
secret = super-secret-change-me
ตรวจสอบ
curl \
http://localhost:8001/consumers/student-client/jwt
#26. ทำไม key ต้องตรงกับ iss
JWT ที่ Node.js สร้างมี
{
"iss": "demo-client"
}
Credential ของ Kong มี
key = demo-client
Kong จะใช้ Claim ที่กำหนดใน
config.key_claim_name
เพื่อค้นหา Credential
Workshop นี้จะใช้
config.key_claim_name = iss
ดังนั้น Flow คือ
JWT
iss = demo-client
|
v
Kong JWT Plugin
|
v
ค้นหา Credential
key = demo-client
#27. เปิด JWT Plugin ให้ api-route
เราจะเปิด JWT เฉพาะ
api-route
เพื่อให้
/auth/token
ยังเป็น Public Endpoint
รัน
curl -i \
-X POST \
http://localhost:8001/routes/api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "jwt",
"config": {
"key_claim_name": "iss",
"header_names": [
"authorization"
],
"claims_to_verify": [
"exp"
]
}
}'
Configuration สำคัญคือ
key_claim_name = iss
และ
claims_to_verify = exp
หมายความว่า Kong จะตรวจ Expiration ของ Token ด้วย
#28. ตรวจสอบ JWT Plugin
curl \
http://localhost:8001/routes/api-route/plugins
ควรพบ Plugin
jwt
#29. ทดสอบ Protected API โดยไม่ส่ง JWT
ลองเรียก
curl -i \
http://localhost:8000/api/products
ควรได้รับสถานะ
401 Unauthorized
Request จะถูกหยุดที่ Kong
Client
|
v
Kong JWT Plugin
|
+---- ไม่มี JWT
|
v
401 Unauthorized
Backend API ไม่จำเป็นต้องจัดการ Request นี้เอง
#30. ขอ JWT Token
curl \
-X POST \
http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{
"username": "student",
"password": "1234"
}'
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
Copy ค่า access_token
#31. เรียก Protected API ด้วย Bearer Token
Linux / macOS / Git Bash
TOKEN="วาง-token-ตรงนี้"
จากนั้นเรียก
curl -i \
http://localhost:8000/api/products \
-H "Authorization: Bearer $TOKEN"
ถ้า Token ถูกต้องจะได้
HTTP/1.1 200 OK
#32. ตัวอย่าง Response
{
"message": "Protected products API",
"kong_consumer": {
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"username": "student-client",
"custom_id": "student-app",
"credential": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"data": [
{
"id": 1,
"name": "Mechanical Keyboard",
"price": 2490
},
{
"id": 2,
"name": "Wireless Mouse",
"price": 1290
},
{
"id": 3,
"name": "USB-C Hub",
"price": 1590
}
]
}
#33. Kong ส่ง Consumer Header ไปยัง Backend
เมื่อ Authentication สำเร็จ Kong สามารถเพิ่ม Header เกี่ยวกับ Consumer ให้ Upstream
ตัวอย่าง Header
X-Consumer-ID
X-Consumer-Username
X-Consumer-Custom-ID
X-Credential-Identifier
Backend จึงอ่านได้ เช่น
const consumer =
req.headers["x-consumer-username"];
แนวทางนี้มีประโยชน์เมื่อ Backend ต้องการทราบว่า Request ผ่าน Credential ของ Consumer ใด
#34. ทดสอบ /profile
เรียก
curl \
http://localhost:8000/api/profile \
-H "Authorization: Bearer $TOKEN"
ตัวอย่าง Response
{
"message": "Protected profile API",
"consumer": "student-client"
}
#35. ทดสอบ Token ไม่ถูกต้อง
curl -i \
http://localhost:8000/api/products \
-H "Authorization: Bearer invalid-token"
Request ควรถูก Reject
Client
|
v
Kong
|
v
JWT Plugin
|
+--- Invalid JWT
|
v
401
#36. ทดสอบ Token ที่ถูกแก้ Payload
JWT ประกอบด้วย
Header.Payload.Signature
แม้ JWT Payload จะอ่านได้ แต่หากแก้ Payload โดยไม่สร้าง Signature ใหม่ที่ถูกต้อง
Kong จะตรวจ Signature ไม่ผ่าน
นี่คือเหตุผลที่ JWT ไม่ได้มีเป้าหมายเพื่อ ซ่อนข้อมูล
แต่มีเป้าหมายหลักเพื่อทำให้ตรวจสอบได้ว่า Token ถูกแก้ไขหรือไม่ และถูก Sign ด้วย Key ที่เชื่อถือได้หรือไม่
หลีกเลี่ยงการใส่ Password หรือ Secret ลงใน JWT Payload
#37. ทดสอบบน Windows PowerShell
สร้าง Request Body
$body = @{
username = "student"
password = "1234"
} | ConvertTo-Json
Login
$response = Invoke-RestMethod `
-Method Post `
-Uri "http://localhost:8000/auth/token" `
-ContentType "application/json" `
-Body $body
เก็บ Token
$token = $response.access_token
ดู Token
$token
เรียก Protected API
Invoke-RestMethod `
-Method Get `
-Uri "http://localhost:8000/api/products" `
-Headers @{
Authorization = "Bearer $token"
}
#38. ทดสอบด้วย Postman
#Request 1: Login
Method
POST
URL
http://localhost:8000/auth/token
Body → Raw → JSON
{
"username": "student",
"password": "1234"
}
#Request 2: Products
Method
GET
URL
http://localhost:8000/api/products
เลือก
Authorization
Type
Bearer Token
แล้วใส่ Token ที่ได้จาก Login
#39. Postman Tests เพื่อเก็บ Token อัตโนมัติ
ใน Request /auth/token สามารถเพิ่ม Script หลัง Response เพื่อเก็บ Token เป็น Environment Variable
ตัวอย่าง
const json = pm.response.json();
pm.environment.set(
"access_token",
json.access_token
);
จากนั้น Request /api/products ใช้
{{access_token}}
ใน Bearer Token
#40. ดู Services ทั้งหมด
curl \
http://localhost:8001/services
#41. ดู Routes ทั้งหมด
curl \
http://localhost:8001/routes
#42. ดู Consumers ทั้งหมด
curl \
http://localhost:8001/consumers
#43. ดู JWT Credentials
curl \
http://localhost:8001/consumers/student-client/jwt
#44. ดู Plugins
curl \
http://localhost:8001/plugins
#45. Plugin สามารถติดตั้งระดับใดได้บ้าง
โดยทั่วไป Kong Plugin สามารถผูกกับ Context ที่เหมาะสม เช่น Global, Service หรือ Route
Workshop นี้เลือก Route-level
api-route
เพราะต้องการผลลัพธ์ดังนี้
/auth/*
|
+--- Public
/api/*
|
+--- JWT Required
ถ้าเราเปิด JWT ที่ demo-api Service โดยตรง ทั้ง /auth และ /api อาจถูกครอบด้วย Plugin ใน Scope ของ Service ตามการตั้งค่าที่ใช้ ทำให้ Login Endpoint ถูกป้องกันด้วย JWT ไปด้วย ซึ่งขัดกับ Flow ที่ต้องการใน Workshop นี้
#46. Sequence Diagram
sequenceDiagram
participant C as Client
participant K as Kong Gateway
participant A as Node.js API
participant D as Kong DB
C->>K: POST /auth/token
K->>A: POST /token
A-->>K: JWT
K-->>C: JWT
C->>K: GET /api/products + Bearer JWT
K->>D: Find JWT credential from iss
D-->>K: Consumer credential
K->>K: Verify JWT signature
K->>K: Verify exp
alt Token valid
K->>A: GET /products
Note over K,A: X-Consumer-* headers
A-->>K: Products JSON
K-->>C: 200 OK
else Token invalid
K-->>C: 401 Unauthorized
end
#47. Error: 404 no Route matched with those values
หากเรียก API แล้วพบประมาณ
no Route matched with those values
ตรวจสอบ Route
curl \
http://localhost:8001/routes
ตรวจสอบว่า Path ตรงกับที่เรียกหรือไม่
ตัวอย่าง Route
/auth
/api
ต้องเรียกผ่าน Proxy Port
8000
ไม่ใช่ Admin API Port
8001
#48. Error: 502 Bad Gateway
มักเกิดจาก Kong เชื่อมต่อ Backend ไม่ได้
ตรวจ Container
docker compose ps
ดู API Log
docker compose logs api
ทดสอบจาก Kong Container
docker exec kong \
curl http://api:3000
ถ้าเรียกไม่ได้ ให้ตรวจ
- ชื่อ Docker Compose Service
- Docker Network
- API Port
- API Process
- Container Status
#49. Error: JWT ถูกสร้างแล้วแต่ยัง 401
ตรวจค่าต่อไปนี้
JWT Payload
iss = demo-client
Kong Credential
key = demo-client
Node.js
JWT_SECRET=super-secret-change-me
Kong
secret=super-secret-change-me
ความสัมพันธ์ต้องเป็น
JWT iss
==
Kong key
และ
JWT signing secret
==
Kong credential secret
#50. Error: Token หมดอายุ
เราเปิด
claims_to_verify = exp
ดังนั้น Kong จะ Reject Token ที่หมดอายุ
ให้ Login ใหม่
POST /auth/token
เพื่อรับ Token ใหม่
#51. Reset Workshop
หยุดระบบ
docker compose down
หากต้องการลบ Kong Database ด้วย
docker compose down -v
จากนั้นสร้างใหม่
docker compose up -d --build
docker compose down -vจะลบ Named Volume ของ PostgreSQL ใน Workshop นี้ด้วย
#52. ทำไม Workshop นี้ใช้ HS256
HS256 เข้าใจง่ายและเหมาะสำหรับอธิบายพื้นฐาน
Authentication API
|
| Shared Secret
v
Sign JWT
Kong Gateway
|
| Same Shared Secret
v
Verify JWT
ข้อจำกัดคือระบบที่ Verify Token ต้องมี Shared Secret
#53. Production ควรพิจารณา RS256
แนวทาง Asymmetric Key
Authentication Server
|
| Private Key
v
Sign JWT
Kong Gateway
|
| Public Key
v
Verify JWT
ข้อดีคือ Kong ไม่จำเป็นต้องถือ Private Key สำหรับ Sign Token
Kong JWT Plugin รองรับการตรวจ JWT ที่ลงนามด้วย Public/Private-key algorithms ที่รองรับตาม Configuration ของ Plugin
#54. JWT กับ OAuth 2.0 / OpenID Connect ไม่ใช่สิ่งเดียวกัน
JWT เป็น Token Format
OAuth 2.0 เป็น Authorization Framework
OpenID Connect เป็น Identity Layer บน OAuth 2.0
ดังนั้นระบบจริงอาจมี Architecture เช่น
User
|
v
Identity Provider
(Keycloak / Entra ID / Auth0 / etc.)
|
| Access Token
v
Kong Gateway
|
| Verify / Authenticate
v
Backend Services
สำหรับระบบที่ต้องการ Login, Refresh Token, SSO, MFA, User Federation และ Key Rotation ควรพิจารณา Identity Provider แทนการสร้าง Authentication System แบบง่ายเอง
#55. Security Checklist สำหรับ Production
ตัวอย่างแนวทางที่ควรพิจารณา
- ใช้ HTTPS
- ไม่ Hard-code JWT Secret
- เก็บ Secret ใน Secret Manager
- ตั้ง Token Expiration
- ทำ Key Rotation
- จำกัด Network ของ Kong Admin API
- ไม่เปิด Port
8001ให้ Internet - ใช้ Strong Password
- อย่าใส่ข้อมูลลับใน JWT Payload
- เพิ่ม Rate Limiting
- เพิ่ม Logging / Observability
- พิจารณา ACL หรือ Authorization เพิ่มเติม
- แยก Authentication Service จาก Business Service
- ใช้ RS256/ES256 หรือ Identity Provider เมื่อเหมาะสม
- ใช้ Short-lived Access Token
- ออกแบบ Refresh Token อย่างปลอดภัย หากระบบต้องใช้
- ตรวจสอบ CORS ตาม Frontend ที่อนุญาต
- ใช้ Secret ที่มี Entropy สูง
- ไม่ใช้ Username/Password ตัวอย่างจาก Workshop ใน Production
#56. Workshop Challenge
หลังจากทำ Workshop หลักเสร็จ ลองทำโจทย์ต่อไปนี้
#Challenge 1: Orders API
เพิ่ม Endpoint
POST /orders
และให้เข้าถึงผ่าน
POST /api/orders
#Challenge 2: Consumer เพิ่มเติม
สร้าง
mobile-client
และ
partner-client
ให้แต่ละ Consumer มี JWT Credential ของตนเอง
#Challenge 3: Rate Limiting
ติดตั้ง Rate Limiting Plugin ให้ Protected API
แนวคิด
/api/*
|
+--- JWT
|
+--- Rate Limiting
#Challenge 4: Consumer Groups / ACL
แบ่ง Client เป็น
user
admin
partner
และออกแบบ Route ที่แต่ละกลุ่มเข้าถึงได้
#Challenge 5: เปลี่ยนจาก HS256 เป็น RS256
ทดลองใช้
Private Key
Public Key
แทน Shared Secret
#Challenge 6: ใช้ Identity Provider
เปลี่ยน Login Endpoint แบบ Demo ไปใช้ Identity Provider เช่น
- Keycloak
- Microsoft Entra ID
- Auth0
แล้วออกแบบให้ Gateway ตรวจ Token จาก Identity Provider
#57. สรุปองค์ประกอบสำคัญ
การทำ JWT Authentication ด้วย Kong ใน Workshop นี้มีองค์ประกอบหลัก
Service
Route
Consumer
JWT Credential
JWT Plugin
ความสัมพันธ์
Client
|
| Bearer JWT
v
Kong Route
|
v
JWT Plugin
|
| iss
v
Consumer Credential
|
| Signature Valid
v
Kong Service
|
v
Backend API
ค่าที่สำคัญมากคือ
JWT payload
iss = demo-client
ต้องตรงกับ
Kong JWT Credential
key = demo-client
และ Shared Secret สำหรับ HS256 ต้องตรงกัน
Node.js JWT_SECRET
==
Kong JWT Credential secret
#58. Final Architecture
flowchart TB
CLIENT["Client / Postman"]
PROXY["Kong Proxy<br/>localhost:8000"]
AUTH_ROUTE["auth-route<br/>/auth<br/>Public"]
API_ROUTE["api-route<br/>/api<br/>Protected"]
JWT_PLUGIN["JWT Plugin<br/>Verify iss + signature + exp"]
CONSUMER["Consumer<br/>student-client"]
CREDENTIAL["JWT Credential<br/>key = demo-client<br/>HS256"]
SERVICE["Kong Service<br/>demo-api"]
API["Node.js API<br/>api:3000"]
CLIENT --> PROXY
PROXY --> AUTH_ROUTE
AUTH_ROUTE --> SERVICE
PROXY --> API_ROUTE
API_ROUTE --> JWT_PLUGIN
JWT_PLUGIN --> CONSUMER
CONSUMER --> CREDENTIAL
JWT_PLUGIN --> SERVICE
SERVICE --> API
#59. สรุป
Workshop นี้แสดงให้เห็นการใช้ Kong API Gateway ร่วมกับ JWT Authentication ตั้งแต่ต้นจนจบ
เราได้ทดลองตั้งแต่
สร้าง REST API
|
v
Dockerize API
|
v
ติดตั้ง Kong
|
v
สร้าง Service
|
v
สร้าง Routes
|
v
สร้าง Consumer
|
v
สร้าง JWT Credential
|
v
ติดตั้ง JWT Plugin
|
v
ออก JWT
|
v
เรียก Protected API
แนวทางนี้ช่วยให้ Responsibility ชัดเจนขึ้น
Kong Gateway
|
+--- Routing
+--- JWT Validation
+--- Gateway Security
Backend API
|
+--- Business Logic
+--- Data Processing
สำหรับ Workshop หรือระบบทดลอง การใช้ HS256 ช่วยให้เข้าใจ Flow ได้ง่าย แต่สำหรับ Production ควรพิจารณาเรื่อง Key Management, HTTPS, Admin API Security, Authorization, Key Rotation และ Identity Provider เพิ่มเติมตามระดับความเสี่ยงของระบบ
#References
-
Kong Gateway — JWT Plugin
https://developer.konghq.com/plugins/jwt/ -
Kong Gateway — Authenticate Consumers with JWT
https://developer.konghq.com/how-to/authenticate-consumers-jwt/ -
Kong Gateway — JWT in Authorization Header
https://developer.konghq.com/plugins/jwt/examples/header/ -
Kong Gateway — Verify Registered Claims
https://developer.konghq.com/plugins/jwt/examples/verified-claim/ -
Kong Gateway — Install using Docker Compose
https://developer.konghq.com/gateway/install/docker/ -
Kong Gateway — Consumers
https://developer.konghq.com/gateway/entities/consumer/ -
Kong Gateway — Routes
https://developer.konghq.com/gateway/entities/route/ -
Kong Gateway — Plugins
https://developer.konghq.com/gateway/entities/plugin/ -
Kong Docker Official Image
https://hub.docker.com/_/kong