ประเภทของ Token
ประเภทของ Token ใน Keycloak
หัวข้อที่มีชื่อว่า “ประเภทของ Token ใน Keycloak”Keycloak ออก token สามประเภทในขั้นตอน OIDC flow แต่ละประเภทมีวัตถุประสงค์ต่างกัน
Access Token
หัวข้อที่มีชื่อว่า “Access Token”ใช้อนุญาต API call — เป็น JWT อายุสั้นที่ API ของคุณตรวจสอบในทุก request Bearer token นี้คือสิ่งที่คุณส่งไปใน Authorization header เมื่อเรียก backend service
ID Token
หัวข้อที่มีชื่อว่า “ID Token”บอก client ว่าผู้ใช้คือใคร — ใช้ได้เฉพาะใน client เท่านั้น เช่น เพื่อแสดงชื่อผู้ใช้ใน UI ห้ามส่ง ID token ไปยัง backend API เด็ดขาด
Refresh Token
หัวข้อที่มีชื่อว่า “Refresh Token”อายุยาวกว่า access token — ใช้ขอ access token ใหม่โดยไม่ต้อง login ซ้ำ ส่งได้เฉพาะไปยัง Keycloak token endpoint เท่านั้น
ตัวอย่าง Decoded JWT
หัวข้อที่มีชื่อว่า “ตัวอย่าง Decoded JWT”ด้านล่างนี้คือ access token ที่ถูก decode แล้ว แสดง header, payload และ claim ต่างๆ ที่ Keycloak ใส่ไว้:
{
"header": {
"alg": "RS256",
"typ": "JWT",
"kid": "abc123"
},
"payload": {
"exp": 1719878400,
"iat": 1719874800,
"jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"iss": "https://auth.example.com/realms/my-app",
"aud": "account",
"sub": "user-uuid-here",
"typ": "Bearer",
"azp": "my-app-frontend",
"scope": "openid profile email roles",
"realm_access": {
"roles": ["offline_access", "uma_authorization", "user"]
},
"resource_access": {
"my-app-frontend": {
"roles": ["app-user"]
}
},
"email_verified": true,
"name": "Alice Example",
"preferred_username": "alice",
"email": "[email protected]"
}
}Claim สำคัญใน Access Token
หัวข้อที่มีชื่อว่า “Claim สำคัญใน Access Token”| Claim | ความหมาย |
|---|---|
sub | Unique ID ของผู้ใช้ |
iss | URL ของ realm ที่ออก token |
aud | ผู้รับ token ที่ตั้งใจ |
exp | เวลาที่ token หมดอายุ (Unix timestamp) |
azp | Client ที่ขอ token |
scope | Scope ที่ถูก grant |
realm_access.roles | Realm roles ของผู้ใช้ |
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
ขอ ID token พร้อม access token (scope=openid) | Client รู้ตัวตนผู้ใช้ทันทีโดยไม่ต้องเรียก userinfo endpoint เพิ่ม | Response ใหญ่ขึ้น และมี token อีกใบที่ต้อง handle ให้ถูก — ห้ามส่งไป backend เด็ดขาด |
| ใช้ refresh token เพื่อขอ access token ใหม่ | ผู้ใช้ไม่ต้อง login ซ้ำ แม้ access token จะหมดอายุเร็ว | ต้องเก็บ refresh token อย่างปลอดภัย เพราะมีอายุยาวกว่าและมีสิทธิ์สูงกว่า access token |
| Public client (SPA) ใช้ PKCE แทน client secret | ไม่ต้อง embed secret ไว้ในโค้ด frontend ที่ใครก็ inspect ได้ | ต้องพึ่ง redirect URI ที่ตั้งไว้อย่างเข้มงวด เพื่อป้องกัน authorization code ถูกขโมยระหว่าง redirect |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ส่ง ID token ไปยัง backend API — ID token มีไว้ให้ client อ่านเพื่อแสดงข้อมูลผู้ใช้ใน UI เท่านั้น ไม่ได้ออกแบบมาให้ API ตรวจสอบ ให้ส่ง access token เป็น Bearer token เสมอ
- เก็บ token (โดยเฉพาะ refresh token) ไว้ใน localStorage — JavaScript ใดๆ บนหน้าเว็บอ่าน localStorage ได้ ถ้าแอปมีช่องโหว่ XSS แม้เล็กน้อย token ก็หลุดไปกับผู้โจมตีทันที ควรเก็บใน memory หรือ httpOnly cookie แทน
- ใช้ access token เป็นตัวระบุ session ระยะยาว — access token หมดอายุและเปลี่ยนค่าไปเรื่อยๆ ตาม lifespan อย่าผูก logic ที่ต้องคงอยู่นานกับค่า token ตรงๆ ให้ผูกกับ
subclaim หรือ session ฝั่ง server แทน
💡 ตัวอย่างจากของจริง
Auth0 SPA SDK / Google Identity Services — เก็บ access token และ refresh token ไว้ใน memory ของ JavaScript runtime เท่านั้น (ไม่แตะ localStorage) แล้วให้ session กลับมาผ่าน silent refresh หรือ refresh token rotation เมื่อ reload หน้า
แอปธนาคารบนมือถือ — แยกการใช้งาน ID token ชัดเจนว่าใช้แสดงชื่อ/รูปโปรไฟล์ในแอปเท่านั้น ส่วนทุก call ไปยัง core banking API จะแนบเฉพาะ access token ผ่าน
Authorization: Bearerheader