[npm] Passport 筆記(Learn to Use Passport JS)
- 原本存在 gist 的檔案
- Dive into Passport JS @ PJCHENder Gist
基本觀念
- Strategies 要先定義好之後才能進入 routes
- 當成功登入後可以得到
req.user
這個物件 - 如果定義的 Strategy 驗證失敗,會在 callback 中回傳 err ,後續的路由不會被執行,並回傳 401 Unauthorized 的 response
在路由中使用設定好的 Passport Strategy
設定好的 Passport Strategy 可以直接在 routes 中使用:
在 routes 中使用
將定義好的 passport Strategy 當成 middleware (passport.authenticate('<strategyName>')
)套用在 routes 中即可:
// ./routes/index.js
app.post(
'/login',
// passport as middleware
passport.authenticate('local'),
// routes handler
(req, res) => {
// 如果這個 function 有執行,表示通過驗證
// 在 req.user 中會回傳被認證的使用者
res.redirect(`/users/${req.user.username}`);
},
);
在預設的情況下,如果認證失敗,Passport 會回傳 401 Unauthorized
的狀態,後續的路由都不會在被處理;如果認證成功,則回觸發 next
,並可以在 res.user
中拿到被認證的使用者。
若有需要也可以修改轉址的路徑:
app.get(
'/signin',
passport.authenticate('local', {
failureRedirect: '/signin',
failureFlash: true,
}),
(req, res) => res.redirect(`/users/${req.user.username}`),
);
Session
在成功登入後,Passport 會建立一個 login session。如果不需要可以把它停用(passport.authenticate('<strategyName>', { session: false })
):
// ./routes/index.js
app.get('/api/users/me', passport.authenticate('basic', { session: false }), (req, res) => {
res.json({ id: req.user.id, username: req.user.username });
});
客制化 callback(常用)
如果內建的驗證請求不足夠使用,可以使用客制化的 callback 來處理,passport.authenticate('<strategyName>', callback<err, user, info>)
:
// ./routes/index.js
app.get('/login', function (req, res, next) {
// 在 routes 的 handler 中使用 passport.authenticate
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err);
}
// 如果找不到使用者
if (!user) {
return res.redirect('/login');
}
// 否則登入
req.logIn(user, function (err) {
if (err) {
return next(err);
}
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
在這個範例中,passport.authenticate('<strategyName>', callback<err, user, info>)
是在 Express 中的路由中去執行,而不是當成 middleware 使用。因此,這是透過 closure 來在 callback 中取得 req
和 res
。
如果認證失敗,user
會被設成 false
;如果例外發生;err
會被設定;info
則可以拿到 strategy 中 verify callback 所提供的更多訊息。
要注意的是,當使用客制化的 callback 時,需要自己透過 req.login()
來設置 session,並且回傳 response。
req.login('user', callback<err>)
來建立 session,若使用者登入成功,user 會被指定到 req.user(一般情況下,req.login() 會在 passport.authenticate 的 middleware 中被執行,但若是客制化的 callback 則要自己帶)。req.logout()
會移除 req.user 這個屬性,並同時清除 login session(如果有的話)。
使用內建的驗證函式
app.post(
'/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
session: false,
}),
);
設定 Passport Strategy
透過 passport.use()
可以設定 Strategies:
// ./middleware/passport
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(
new LocalStrategy(
// 這是 verify callback
function (username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) {
return done(err);
}
// 如果使用者不存在
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
// 如果使用者密碼錯誤
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
// 認證成功,回傳使用者資訊 user
return done(null, user);
});
},
),
);
驗證用的 callback
return done(<error|null>, <user|false>, {message: 'incorrect reason'})
透過 verify callback 來執行驗證後的結果。在 Passport 驗證一個 request 時,它會解析 request 中的登入資訊(credentials),接著以這些 credentials 當作參數來執行 verify callback(在上面的例子就是 username 和 password)。
有錯誤產生時
這裡的錯誤指的是伺服器的錯誤,這時候 err
會被設為非 null
的值;如果是驗證失敗(伺服器沒有錯誤)應該則此值要設定成null
。
return done(error);
驗證成功
如果 credentials 是有效的,那麼 verify callback 會執行 done
,並提供受驗證後的使用者資訊(user
):
return done(null, user);