# [ALPHA Camp] M3 todo list: 認證系統 & 第三方工具
***
> **即使再小的帆也能遠航**
>###### tags: `ALPHA Camp` `DevC4後端`
***
# 整合 passport 實做登入
## 安裝套件 passport 與 passport-local
* 安裝passport套件,然後安裝passport-local進行本地端使用者資料驗證
* cookie-based authentication
```bash
npm install passport@0.6.0 passport-local@1.0.0
```
## 設定 passport middleware
app.js
```javascript
//...
const passport = require('passport')
// if (process.env.NODE_ENV === 'development') {
// require('dotenv').config()
// }
//...
// app.use(flash())
app.use(passport.initialize())
// app.use(messageHandler)
//...
```
## 設定本地驗證策略
```javascript
const express = require('express')
const router = express.Router()
const passport = require('passport')
const LocalStrategy = require('passport-local')
const db = require('../models')
const User = db.User
passport.use(new LocalStrategy({ usernameField: 'email' },(username, password, done) => {
return User.findOne({
attributes: ['id', 'name', 'email', 'password'],
where: { email: username },
raw: true
})
.then((user) => {
if (!user || user.password !== password) {
return done(null, false, { message: 'email 或密碼錯誤' })
}
return done(null, user)
})
.catch((error) => {
error.errorMessage = '登入失敗'
done(error)
})
}))
passport.serializeUser((user, done) => {
const { id, name, email } = user
return done(null, { id, name, email })
})
```
## 實作「登入後保持登入狀態」
利用session取得資訊
```javascript
//...
app.use(passport.initialize())
app.use(passport.session())
app.use(messageHandler)
//...
```
## 驗證使用者是否為登入
建立檢查的middleware
```javascript
module.exports = (req, res, next) => {
if (req.isAuthenticated()) {
return next()
}
req.flash('error', '尚未登入')
return res.redirect('/login')
}
```
## 確認使用者身份
從session中取得使用者資訊
```javascript
passport.deserializeUser((user, done) => {
done(null, { id: user.id })
})
```
## 更新特定需要登入的路由
```javascript
router.use('/todos', authHandler, todos)
```
## 登出功能
```javascript
router.post('/logout', (req, res) => {
req.logout((error) => {
if (error) {
next(error)
}
return res.redirect('/login')
})
})
```
# 利用 bcrypt 實做註冊、登入的密碼驗證
## 安裝 bcrypt.js
```bash
npm install bcryptjs@2.4.3
```
## 完成註冊的加鹽及雜湊
```javascript
const express = require('express')
const router = express.Router()
const bcrypt = require('bcryptjs')
const db = require('../models')
//...
return User.count({ where: { email }})
.then((rowCount) => {
if (rowCount > 0) {
req.flash('error', 'email 已註冊')
return
}
return bcrypt.hash(password, 10)
.then((hash) => User.create({ email, name, password: hash }))
})
.then((user) => {
//...
```
## transaction
```javascript
async up (queryInterface, Sequelize) {
let transaction
try {
transaction = await queryInterface.sequelize.transaction()
const salt = await bcrypt.genSalt(10)
const hash = await bcrypt.hash('password', salt)
await queryInterface.bulkInsert('Users', [
{
id: 1,
name: 'user1',
email: 'user1@example.com',
password: hash,
createdAt: new Date(),
updatedAt: new Date()
}
],
{ transaction }
)
await queryInterface.bulkInsert('Todos',
Array.from({ length: 10 }).map((_, i) =>
({
name: `todo-${i}`,
userId: 1,
createdAt: new Date(),
updatedAt: new Date()
})
),
{ transaction }
)
await transaction.commit()
} catch (error) {
if (transaction) await transaction.rollback()
}
}
```
# 整合 OAuth 2.0
```bash
npm install passport-facebook@3.0.0
```
## 註冊應用程式
* 取得應用程式編號與密鑰
[facebook for developers](https://developers.facebook.com/)
# Question
1. [課程] 確認oauh2 facebook strategy的程式流程


2. [課程] M3課程最後對 routes/index 的 passport 相關程式做整理,形成 config/passport.js,想問為什麼在重新require該檔案時,會選擇在 app.js 而不是原本的 routes/index.js?然後不知道可不可以解釋在 app.js 和 routes/index.js 兩檔案中,他們各自所使用的passport是原生的還是後來自定義的這個? => BTW 我有嘗試將require換過去routes/index.js,程式是可以正常運行的。
3. [實作狀況] 指標作業實作時,想透過找官方文件來完成onDelete, onUpdate設定,卻發現sequelize官網將這兩個設定寫在model的associations,而非教案的migration當中,資訊不一致讓我有點卡關 => 後來自己是理解成,這裡算是在設定model/schema的屬性,對於可以使用的option兩者應該是通用的 => 衍伸(想確認):在設定都能滿足需求下,schema的設定應該要比model精確?
4. [課程] 建立seeder時,transaction為因應try...catch而調整作用域,課程部分選擇將transaction以let宣告是否有特別用意?為何不直接將const的宣告搬到外面就好?
* 用於處理多個commit
# JWT
```bash
npm install jsonwebtoken passport-jwt
```