JWT (JSON Web Token): การสร้าง, ตรวจสอบ, Refresh Token

JWT (JSON Web Token) คือ token รูปแบบ compact ที่มี 3 ส่วนคือ Header.Payload.Signature ใช้ส่ง claims ระหว่างระบบและตรวจสอบความถูกต้องด้วยลายเซ็น

Timeline/ประวัติศาสตร์

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
flowchart LR
  subgraph Era1["ยุค API Key / Simple Token"]
    A["API Key
ระบุตัว client"] end subgraph Era2["ยุค JWT / Signed Claims"] B["Header.Payload.Signature
ข้อมูลพร้อมลายเซ็น"] C["Access Token
อายุสั้น"] end subgraph Era3["ยุค Token Rotation / Refresh Flow"] D["Refresh Token
อายุยาว"] E["httpOnly Cookie
ลดการถูกอ่านด้วย JS"] end A --> B --> C --> D --> E

โครงสร้าง JWT

ตาราง Claims สำคัญ

Claim ความหมาย ตัวอย่าง
sub subject หรือ user id 42
iat issued at เวลาที่ออก token
exp expiry เวลาหมดอายุ
iss issuer ระบบที่ออก token
aud audience ระบบผู้รับ token

Mermaid Diagram: Refresh Token Flow

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
sequenceDiagram
  participant C as Client / ไคลเอนต์
  participant S as Server / เซิร์ฟเวอร์
  C->>S: Login with username/password
  S-->>C: Access Token + Refresh Cookie
  C->>S: API request with Access Token
  S-->>C: Protected data
  C->>S: Refresh request with httpOnly Cookie
  S-->>C: New Access Token

Code Example

// jwt-demo.mjs
// สร้างและตรวจสอบ JWT ด้วย jsonwebtoken
import express from 'express';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret';

function signAccessToken(user) {
  return jwt.sign(
    { sub: user.id, email: user.email },
    JWT_SECRET,
    { expiresIn: '15m' }
  );
}

app.post('/login', (req, res) => {
  const user = { id: 1, email: 'ana@example.com' };
  const accessToken = signAccessToken(user);
  const refreshToken = jwt.sign({ sub: user.id }, JWT_SECRET, { expiresIn: '7d' });

  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    sameSite: 'lax',
    secure: false
  });

  res.json({ accessToken });
});

app.get('/profile', (req, res) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ message: 'missing token' });

  try {
    const payload = jwt.verify(token, JWT_SECRET);
    res.json({ userId: payload.sub });
  } catch {
    res.status(401).json({ message: 'invalid token' });
  }
});

app.listen(3000, () => console.log('JWT demo on http://localhost:3000'));

// ตัวอย่างการใช้งาน:
// npm install express jsonwebtoken
// POST /login แล้วนำ accessToken ไปส่งใน Authorization: Bearer <token>

วิดีโอแนะนำ

กิจกรรมท้ายบท

  1. สร้าง endpoint /login
  2. ออก access token อายุ 15 นาที
  3. เก็บ refresh token ใน httpOnly cookie
  4. เขียน middleware ตรวจ JWT

กลับสัปดาห์ที่ 12