# [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的程式流程 ![image](https://hackmd.io/_uploads/rJaLHcT8a.png) ![image](https://hackmd.io/_uploads/Byi9HcaLT.png) 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 ```