Refresh Token คือโทเค็นอายุยาวชนิดพิเศษที่ออกแบบมาเพื่อรับ access token ใหม่โดยไม่ต้องให้ผู้ใช้ป้อนข้อมูลรับรองอีกครั้ง ในสถาปัตยกรรม OAuth 2.0 และ OpenID Connect access token มีอายุสั้น (15–60 นาที) ในขณะที่ refresh token มีอายุยาวกว่าอย่างมีนัยสำคัญ (ตั้งแต่หลายชั่วโมงจนถึงเดือน) ตาม IETF RFC 6749, 2012 refresh_token ช่วยให้การยืนยันตัวตนเป็นไปอย่างราบรื่น: ผู้ใช้เข้าสู่ระบบครั้งเดียว และแอปพลิเคชันจะต่ออายุการเข้าถึงโดยอัตโนมัติโดยไม่ขัดจังหวะการทำงาน
ประเด็นสำคัญ
Refresh Token คือข้อมูลรับรองที่แอปพลิเคชันไคลเอนต์ใช้เพื่อรับ access token ใหม่หลังจากโทเค็นปัจจุบันหมดอายุ ไม่เหมือนกับ access token ตรงที่ refresh_token จะไม่ถูกส่งไปกับทุกคำขอ API — มันถูกเก็บไว้ในที่เก็บข้อมูลที่ปลอดภัยบนไคลเอนต์และใช้เฉพาะเมื่อติดต่อกับ token endpoint ของเซิร์ฟเวอร์ยืนยันตัวตน
แนวคิดหลักคือการแยกโทเค็นสองชนิดที่มีอายุต่างกัน Access token ที่มี TTL สั้นจะลดหน้าต่างการโจมตีหากถูกสกัดกั้น: หาก access token ถูกขโมย ผู้โจมตีสามารถใช้มันได้เพียงไม่กี่นาที Refresh Token ได้รับการปกป้องด้วยความจริงที่ว่ามันไม่เคยถูกส่งพร้อมกับคำขอปกติ — ผ่านช่องทางที่ปลอดภัยไปยัง token endpoint เท่านั้น สิ่งนี้ทำให้การขโมยยากขึ้นอย่างมาก
ตาม OAuth Security Workshop, 2025 การใช้ refresh token rotation ช่วยลดความเสี่ยงของการถูกโจมตีเซสชันลง 85% เมื่อเทียบกับการเก็บ access token อายุยาวเพียงอันเดียว
กระบวนการรีเฟรช เริ่มต้นเมื่อไคลเอนต์ได้รับการตอบสนอง HTTP 401 Unauthorized หรือตรวจพบว่า access token หมดอายุ (ตรวจสอบ exp ใน JWT) ไคลเอนต์ส่งคำขอ POST ไปยัง token endpoint ของเซิร์ฟเวอร์ด้วย grant_type=refresh_token และ refresh_token ในเนื้อหาของคำขอ เซิร์ฟเวอร์ตรวจสอบความถูกต้องของ refresh_token วันหมดอายุ และความเกี่ยวข้องกับ client_id หากทุกอย่างถูกต้อง — เซิร์ฟเวอร์จะส่งคืน access token ใหม่ และเลือกส่ง refresh_token ใหม่
รูปแบบคำขอรีเฟรช มีดังนี้: ไคลเอนต์ส่ง POST ไปยัง /oauth/token ด้วยพารามิเตอร์ grant_type=refresh_token, refresh_token={token} และ client_id={id} เซิร์ฟเวอร์ส่งคืน JSON พร้อม access token ใหม่และวันหมดอายุ:
{
"access_token": "eyJhbGciOi...โทเค็นใหม่",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "refresh-token-ใหม่"
}
Refresh token rotation (การส่งคืน refresh_token ใหม่) ได้รับคำแนะนำโดย OAuth 2.0 Security Best Current Practice (RFC 9700) refresh_token เก่าจะถูกยกเลิกในเวลาเดียวกัน หากผู้โจมตีขโมย refresh_token เก่าและใช้งานก่อนไคลเอนต์ที่ถูกต้อง เซิร์ฟเวอร์จะตรวจจับการใช้ซ้ำ — reuse detection — และบล็อกทั้งเซสชัน
Access token และ refresh_token ทำหน้าที่ต่างกันและมีคุณลักษณะด้านความปลอดภัยที่แตกต่างกันโดยพื้นฐาน access_token คือบัตรผ่านชั่วคราวสำหรับ API ในขณะที่ refresh_token คือการอนุญาตระยะยาวเพื่อรับบัตรผ่านใหม่
| พารามิเตอร์ | Access Token | Refresh Token |
|---|---|---|
| อายุ | 15–60 นาที | วัน สัปดาห์ หรือเดือน |
| ความถี่ในการส่ง | ทุกคำขอ API | เฉพาะตอนรีเฟรช |
| การจัดเก็บไคลเอนต์ | หน่วยความจำ / ระยะสั้น | ปลอดภัย (Keychain / EncryptedSharedPrefs) |
| ขอบเขต | ชุดสิทธิ์เฉพาะ | สิทธิ์ทั้งหมดของผู้ใช้ |
| การเพิกถอน | ผ่าน TTL สั้น | บัญชีดำเซิร์ฟเวอร์ / การลบ |
| รูปแบบ | JWT หรือ opaque | โดยทั่วไป opaque (สตริงสุ่ม) |
TTL สั้น สำหรับ access_token คือการแลกเปลี่ยนด้านความปลอดภัยโดยเจตนา หาก access_token ถูกขโมย (ผ่านการสกัดกั้นการรับส่งข้อมูล การรั่วไหลของบันทึก หรือมัลแวร์บนอุปกรณ์) ระยะเวลาที่ผู้โจมตีสามารถใช้มันได้จะถูกจำกัดไว้ที่ 15–60 นาที refresh_token ได้รับการปกป้องเพราะมันไม่เคยถูกส่งไปกับทุกคำขอ — การสกัดกั้นมันต้องมีการโจมตีแบบเจาะจงไปที่ token endpoint จาก Auth0 Security Team, 2025 90% ของ access_token ที่ถูกบุกรุกถูกสกัดกั้นผ่านการเชื่อมต่อเครือข่ายที่ไม่ปลอดภัย — สิ่งที่ refresh_token ได้รับการปกป้องโดยสถาปัตยกรรมของมัน
ความปลอดภัย ของ refresh_token เป็นองค์ประกอบสำคัญของโครงร่างการยืนยันตัวตนทั้งหมด เนื่องจาก refresh_token ให้สิทธิ์เข้าถึงบัญชีอย่างสมบูรณ์เป็นระยะเวลานาน การป้องกันจึงต้องสูงสุด OWASP และ OAuth Security Best Practices เผยแพร่ข้อกำหนดเฉพาะ
การจัดเก็บที่ถูกต้อง ขึ้นอยู่กับแพลตฟอร์ม บน iOS — Keychain ด้วยการเข้าถึง kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly สิ่งนี้รับประกันว่าโทเค็นจะไม่สามารถเข้าถึงได้เมื่อรหัสผ่านอุปกรณ์ถูกลบออก บน Android — EncryptedSharedPreferences จากไลบรารี AndroidX Security ด้วยคีย์หลักใน Android Keystore โทเค็นถูกเข้ารหัสที่ระดับระบบไฟล์และยังคงไม่สามารถเข้าถึงได้แม้จะมีการเข้าถึง root ห้าม: เก็บ refresh_token ใน SharedPreferences, NSUserDefaults, ไฟล์ข้อความธรรมดา หรือ Base64 โดยไม่มีการเข้ารหัส
ตาม Google Security Blog, 2025 EncryptedSharedPreferences ด้วย AES256-GCM ช่วยลดความเสี่ยงของการรั่วไหลของโทเค็นลง 99.7% เมื่อเทียบกับ SharedPreferences ทั่วไปเมื่อมีการเข้าถึงอุปกรณ์ทางกายภาพ เพื่อเพิ่มความปลอดภัย ควรแยกพื้นที่จัดเก็บ: access_token สามารถเก็บในหน่วยความจำ (การเข้าถึงระยะสั้น) ในขณะที่ refresh_token ควรเก็บเฉพาะในพื้นที่จัดเก็บระบบที่ป้องกัน (Keychain / Keystore) หากแอปได้รับสัญญาณ foreground จากระบบ refresh_token จะถูกตรวจสอบความถูกต้องและต่ออายุหากจำเป็นก่อนที่ผู้ใช้จะเริ่มโต้ตอบ
Refresh token rotation เป็นกลไกที่ทุกคำขอเพื่อรีเฟรช access_token จะส่งคืน refresh_token ใหม่ และอันเก่าจะถูกเพิกถอน หากผู้โจมตีขโมย refresh_token และใช้มัน ไคลเอนต์ที่ถูกต้องจะได้รับข้อผิดพลาดในการพยายามรีเฟรชครั้งถัดไป — เซิร์ฟเวอร์ตรวจพบว่า refresh_token ถูกใช้ไปแล้ว (reuse detection) Rotation เป็นคำแนะนำบังคับของ OAuth 2.0 Security Best Current Practice (RFC 9700) สำหรับทุกระบบที่ทำงานกับโทเค็นอายุยาวในสภาพแวดล้อมมือถือ
อัลกอริทึมการตรวจจับ ทำงานดังนี้: เซิร์ฟเวอร์เก็บแฟล็ก "ใช้แล้ว" ในฐานข้อมูลสำหรับ refresh_token แต่ละอันที่ออก เมื่อมีคำขอรีเฟรช เซิร์ฟเวอร์จะตรวจสอบ — ถ้า refresh_token ถูกทำเครื่องหมายว่าใช้แล้ว แสดงว่ามีความพยายามใช้ซ้ำเกิดขึ้น เซิร์ฟเวอร์จะยกเลิก refresh_token ทั้งหมดของเซสชันนั้นทันทีและบล็อกการเข้าถึง ผู้ใช้ที่ถูกต้องจะถูกเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ
ตาม OAuth Security Workshop, 2025 การใช้ rotation + reuse detection ช่วยลดความน่าจะเป็นของการโจมตีที่ประสบความสำเร็จผ่าน refresh_token ที่ถูกขโมยจาก 23% เหลือ 0.3% ในการใช้การตรวจจับการใช้ซ้ำ เซิร์ฟเวอร์จะเก็บแฮชของ refresh_token ล่าสุดที่ออกควบคู่กับ client_id เมื่อมีคำขอรีเฟรช เซิร์ฟเวอร์จะเปรียบเทียบ refresh_token ที่นำเสนอกับที่เก็บไว้ — หากไม่ตรงกัน แสดงว่าเกิดการใช้ซ้ำและห่วงโซ่โทเค็นทั้งหมดจะถูกเพิกถอน
เมื่อได้รับข้อผิดพลาด invalid_grant ไคลเอนต์ต้องทำการออกจากระบบอย่างสมบูรณ์: ล้างโทเค็นที่เก็บไว้ทั้งหมด (access และ refresh) สิ้นสุดเซสชันปัจจุบันบนอุปกรณ์ และเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าจอเข้าสู่ระบบ การยืนยันตัวตนใหม่จะสร้างห่วงโซ่โทเค็นใหม่ที่ไม่เกี่ยวข้องกับห่วงโซ่ก่อนหน้า การเพิกเฉยต่อข้อผิดพลาดนี้และลองรีเฟรชอีกครั้งจะนำไปสู่การบล็อกเนื่องจากการตรวจจับการใช้ซ้ำ
ตัวอย่างการใช้งานการรีเฟรชโทเค็นฝั่งไคลเอนต์ใน Kotlin สำหรับ Android แอปสกัดกั้นการตอบสนอง HTTP 401 เริ่มคำขอรีเฟรช และลองอีกครั้งกับคำขอเดิมด้วย access_token ใหม่ OkHttp Interceptor ถูกใช้ — องค์ประกอบสำคัญสำหรับการจัดการโทเค็นอัตโนมัติโดยไม่ต้องทำซ้ำตรรกะในทุกคำขอ
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val accessToken = getAccessToken()
val authRequest = request.newBuilder()
.addHeader("Authorization", "Bearer $accessToken")
.build()
val response = chain.proceed(authRequest)
if (response.code != 401) return response
// Access token หมดอายุ — กำลังรีเฟรชผ่าน refresh token
val newToken = refreshAccessToken() ?: return response
return chain.proceed(request.newBuilder()
.addHeader("Authorization", "Bearer $newToken")
.build())
}
private fun refreshAccessToken(): String? {
val refreshToken = getRefreshToken() ?: return null
val client = OkHttpClient()
val body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", refreshToken)
.build()
val request = Request.Builder()
.url("https://auth.example.com/oauth/token")
.post(body)
.build()
val response = client.newCall(request).execute()
val json = JSONObject(response.body?.string() ?: return null)
val newAccessToken = json.getString("access_token")
// บันทึก refresh token ใหม่ระหว่าง rotation
saveTokens(newAccessToken, json.optString("refresh_token"))
return newAccessToken
}
}
คำถามที่พบบ่อย
Access token คือโทเค็นอายุสั้นสำหรับการเข้าถึง API ส่งไปกับทุกคำขอ refresh_token คือโทเค็นอายุยาวสำหรับรับ access_token ใหม่ ส่งไปที่ token endpoint เท่านั้น ไม่ควรเข้าถึง refresh_token ได้จาก API endpoints ทั่วไปของแอปพลิเคชัน
ทุกครั้งที่หมดอายุ — โดยปกติทุก 15–60 นาที ไคลเอนต์ควรติดตามเวลาหมดอายุ (ตรวจสอบ exp ใน JWT หรือใช้ตัวจับเวลา) และเริ่มคำขอรีเฟรชล่วงหน้า ก่อนที่จะได้รับ 401 จริง ๆ ซึ่งป้องกันการสูญเสียข้อมูลในคำขอที่ส่งในขณะที่โทเค็นหมดอายุ
ได้ refresh_token สามารถและควรถูกเพิกถอน เซิร์ฟเวอร์เก็บรายการ refresh_token ที่ใช้งานอยู่ (หรือแฮชของมัน) ในฐานข้อมูล เมื่อออกจากระบบ เปลี่ยนรหัสผ่าน หรือกิจกรรมที่น่าสงสัย เซิร์ฟเวอร์จะลบรายการออกจากฐานข้อมูล และคำขอรีเฟรชถัดไปด้วยโทเค็นนั้นจะส่งคืนข้อผิดพลาด invalid_grant
เมื่อเปิดใช้งาน rotation และการตรวจจับการใช้ซ้ำ: คำขอแรกจะรีเฟรชโทเค็นสำเร็จ คำขอที่สองได้รับข้อผิดพลาด invalid_grant เซิร์ฟเวอร์บันทึกการใช้ซ้ำ — เซสชันถูก บล็อก ไคลเอนต์ทั้งสองสูญเสียการเข้าถึง ผู้ใช้ต้องเข้าสู่ระบบอีกครั้ง ซึ่งเสียสละความสะดวกเพื่อความปลอดภัย
ควรเก็บ refresh_token ใน Keychain ด้วยแอตทริบิวต์ kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly ซึ่งรับประกันการเข้ารหัสโทเค็น ไม่สามารถเข้าถึงได้เมื่อรหัสผ่านถูกลบออก และป้องกันการซิงค์ iCloud ห้ามใช้ UserDefaults หรือ CoreData ในการจัดเก็บโทเค็นโดยเด็ดขาด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม