快速開始

最後更新

本頁從安裝到完成第一次登入、驗證與登出,串起一個可執行的 Express 應用。

前置需求

項目 需求
Node.js 20 以上(package.json engines)
Express 4.18 以上(peer dependency)
Redis 可連線的 Redis 伺服器
Cookie 解析 cookie-parser 或同等中介軟體;套件從 req.cookies 讀取 Token
金鑰 EC P-256 金鑰對;簽章演算法固定為 ES256

安裝

npm install @pardnchiu/jwt-auth express cookie-parser

產生 ES256 金鑰

mkdir -p keys
openssl ecparam -name prime256v1 -genkey -noout -out keys/private.pem
openssl ec -in keys/private.pem -pubout -out keys/public.pem

RSA 或 HMAC 金鑰無法以 ES256 簽章,CreateJWT() 會拋出錯誤。

第一個應用

import express, { Request, Response, NextFunction } from "express";
import cookieParser from "cookie-parser";
import { JWTAuth } from "@pardnchiu/jwt-auth";

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

async function requireAuth(req: Request, res: Response, next: NextFunction) {
  try {
    const result = await JWTAuth.VerifyJWT(req, res);
    if (!result.isAuth) {
      // isError 為 true 代表可疑請求,否則為一般訪客
      return res.status(result.isError ? 400 : 401).json({ error: "unauthorized" });
    }
    res.locals.user = result.data;
    next();
  } catch (err) {
    res.status(500).json({ error: (err as Error).message });
  }
}

app.post("/login", async (req, res) => {
  try {
    // 帳密驗證完成後簽發 Token
    const result = await JWTAuth.CreateJWT(req, res, {
      id: "user123",
      name: "John",
      email: "john@example.com",
    });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: (err as Error).message });
  }
});

app.get("/me", requireAuth, (req, res) => {
  res.json({ user: res.locals.user });
});

app.post("/logout", async (req, res) => {
  await JWTAuth.RevokeJWT(req, res);
  res.json({ ok: true });
});

async function main() {
  await JWTAuth.init({
    privateKeyPath: "./keys/private.pem",
    publicKeyPath: "./keys/public.pem",
    accessTokenExpires: 900,
    refreshTokenExpires: 604800,
    isProd: false,
    AccessTokenCookieKey: "access_token",
    RefreshTokenCookieKey: "refresh_id",
    redis: { host: "localhost", port: 6379 },
    checkUserExists: async (userId) => true,
  });
  app.listen(3000);
}

main().catch((err) => {
  // 金鑰讀取失敗或 Redis 無法連線
  console.error(err);
  process.exit(1);
});

驗證是否運作

curl -i -c jar.txt -X POST http://localhost:3000/login
curl -i -b jar.txt http://localhost:3000/me
curl -i -b jar.txt -X POST http://localhost:3000/logout
curl -i -b jar.txt http://localhost:3000/me

isProd: false 時 cookie 的 domain 固定為 localhost,請以 http://localhost 而非 127.0.0.1 存取,否則瀏覽器與 curl 都不會回送 cookie。

下一步

EN