# 🏅 Day 31 - 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
```

> [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 連結貼至底下回報就算完成了喔!
解答位置請參考下圖(需打開程式碼的部分觀看)

<!-- 解答:
```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 / 答案 |
|:-------------:|:-----------------:|
| Tau | [CodePen](https://codepen.io/Tau-Hsu/pen/zxYBLXm?editors=0010) |
| poyi | [CodePen](https://codepen.io/poyi-the-flexboxer/pen/xbxOago) |
|janetlai|[CodePen](https://codepen.io/eiddkqxz-the-builder/pen/pvobxRM)
|hannahpun|[CodePen](https://codepen.io/Tau-Hsu/pen/zxYBLXm?editors=0010)
|adengg|[CodePen](https://codepen.io/Osases/pen/KwKMEqN?editors=0010)
|bian_yang_mofa|[CodePen](https://codepen.io/cssf998811/pen/GgRqeMV?editors=0010)
|HarryKuo|[CodePen](https://codepen.io/harry_kuo/pen/NPWbvjd?editors=0010)|
|helena|[CodePen](https://codepen.io/helena27/pen/gbORggW)|
|hsin yu|[CodePen](https://codepen.io/tina2793778/pen/yyLPwbM)|