# 快速開始

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

## 前置需求

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

## 安裝

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

## 產生 ES256 金鑰

```bash
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()` 會拋出錯誤。

## 第一個應用

```typescript
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);
});
```

## 驗證是否運作

```bash
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。

## 下一步

- Token 何時續期、何時換發：[Refresh Token 輪替](/zh/token-refresh)
- 所有設定欄位：[設定](/zh/configuration)
- 行動 App 等非瀏覽器客戶端：[客戶端整合](/zh/client-integration)
