---
# System prepended metadata

title: "\U0001F3C5 Day 28 - JWT 產生身份驗證 token"
tags: [Node.js 直播班 - 2024 春季班]

---

# 🏅 Day 28 - JWT 產生身份驗證 token
當使用者登入或註冊，後端經過驗證後確認資料格式正確，就會產生一組的 token 回傳至使用者（client 端），此 token 用於身份驗證，接下來 client 端造訪需權限的頁面、發送需要權限的請求，若未帶上此 token 或是 token 驗證錯誤，都會請求失敗

接下來會使用 [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken)（JWT `JSON Web Token`） 套件中的 `sign()` 實作產生 token 的流程（流程圖步驟 2）
```javascript
const jwt = require('jsonwebtoken');
jwt.sign(payload, secretOrPrivateKey, [options, callback])
```
- payload 會是一個物件，為該使用者的相關資訊（如 `id`）
- secretOrPrivateKey 私鑰可以是字串、buffer 或物件，這裡會加入一段字串（如：`secret`）並設定為環境變數 `process.env.JWT_SECRET`
- options 或 callback 函式，options 為一個物件，可根據文件中有提供的選項客製化，例如：設定此 token 的到期日
```javascript
const token = jwt.sign({ id: userId }, process.env.JWT_SECRET, {
  expiresIn: process.env.JWT_EXPIRES_DAY
});
```
產生出的 token 會以 `.` 分隔，其中第一二段的 header 及 payload 都是可以透過 base64 編碼得知內容的
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.  # header
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.  # payload
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ  # signature
```

![](https://i.imgur.com/C2fj19V.png)
> [JWT 流程圖](https://whimsical.com/jwt-UKUY1rj1vfoN6uyic7e4Sm)

<br>

### 參考資源

- [什麼是 JWT](https://5xruby.tw/posts/what-is-jwt)
- [jwt.sign()](https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback)


題目
---
延續前一天，優化登入功能，運用 `jwt.sign()` 產生 JWT，當使用者成功登入時回傳 token 與使用者名稱 name

```javascript=
router.post('/sign_in', handleErrorAsync(async (req, res, next) => {
  const { email, password } = req.body;
  if (!email || !password) {
    next(appError(400, '帳號密碼不可為空', next));
  }
  
  const user = await User.findOne({ email }).select('+password');
  const auth = await bcrypt.compare(password, user.password);
  if (!auth) {
    next(appError(400, '帳號或密碼錯誤，請重新輸入！', next));
  }
  
  const token = ...
  
  res.status(200).json({
    status: 'success',
    user: {
      token,
      name: user.name
    }
  });
}));
```

## 回報流程

將答案寫在 CodePen 並複製 CodePen 連結貼至底下回報就算完成了喔! 
解答位置請參考下圖（需打開程式碼的部分觀看）

![](https://i.imgur.com/vftL5i0.png)

<!-- 解答：  

```javascript=
router.post('/sign_in', handleErrorAsync(async (req, res, next) => {
  const { email, password } = req.body;
  if (!email || !password) {
    next(appError(400, '帳號密碼不可為空', next));
  }
  
  const user = await User.findOne({ email }).select('+password');
  const auth = await bcrypt.compare(password, user.password);
  if (!auth) {
    next(appError(400, '帳號或密碼錯誤，請重新輸入！', next));
  }
  
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
  expiresIn: process.env.JWT_EXPIRES_DAY
}); // JWT_SECRET、JWT_EXPIRES_DAY 需自行在 .env 設定
  
  res.status(200).json({
    status: 'success',
    user: {
      token,
      name: user.name
    }
  });

}));
```
-->




回報區
---
<!-- 
將答案貼至下方表格內，格式：
| Discord 暱稱 | [CodePen](連結) |
-->

|    Discord    |   CodePen ／ 答案  |
|:-------------:|:-----------------:|
| xxx | [CodePen]() |
| 2魚 | [CodePen](https://codepen.io/ijrekmsn-the-sans/pen/eYowWKQ) |
| 苡安 | [hackmd](https://hackmd.io/@L7K9-66lSeagS28AP0_GjQ/S1yhLky7C) |
|Lobinda|[HackMD](https://hackmd.io/@Lobinda/rJ7V91JmC)|
| Chia Pin | [CodePen](https://codepen.io/joker-cat/pen/YzbPjdP) |
| Aida | [CodePen](https://codepen.io/ada23410/pen/qBGEyLG?editors=0010) |
| wei | [CodePen](https://hackmd.io/@xu7yoa5cSsqaron7h9XhUw/H1pd4LJQ0)|
| jenny7532 | [CodePen](https://codepen.io/wei-chen-wu/pen/oNRgOyL)|
| william威良 | [CodePen](https://codepen.io/snowman12320/pen/oNRXXXW?editors=1010)|
| runweiting | [CodePen](https://codepen.io/weiting14/pen/XWwbmMV)|
| william_hsu | [CodePen](https://codepen.io/william8815/pen/XWwbNZj)|
| ej_chuang | [CodePen](https://codepen.io/EJChuang/pen/MWdwRQP)|
| Tiya | [CodePen](https://codepen.io/Tiya_blank/pen/rNbQPbY)|
| Hank | [CodePen](https://codepen.io/tw1720/pen/GRaRJJP)|
| zaoannihao | [CodePen](https://codepen.io/ckhwdvrx-the-solid/pen/mdYeNXV)|
|Mei|[CodePen](https://codepen.io/l_umei/pen/RwmrYam) |
| Fabio20 | [CodePen](https://codepen.io/fabio7621/pen/mdYOPxd) |
