Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9145

【Node.js】ExpressとPassport.jsで認証機能を実装する

$
0
0
はじめに 普段はVue.js, Nuxt.jsに使っている者です。最近バックエンド側の知見も得ようということでExpressで個人アプリを作っているのですが、ログイン・ログアウトの実装において初めてPassport.jsというモジュールを使ったので忘れないうちにメモします。 実装の過程で躓いた箇所や不明で調べた箇所を実装コードとともに解説をしていきます。 server.js if (process.env.NODE_ENV !== "production") { require("dotenv").config(); } const express = require("express"); const app = express(); const bcrypt = require("bcrypt"); const passport = require("passport"); const methodOverride = require("method-override"); const flash = require("express-flash"); const session = require("express-session"); const initializePassport = require("./middleware/authMiddleware.js"); const users = []; initializePassport( passport, (email) => users.find((user) => user.email === email), (id) => users.find((user) => user.id === id) ); app.set("view-engine", "ejs"); app.use(express.urlencoded({ extended: false })); app.use(express.static(__dirname + "/public")); app.use(flash()); app.use( session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, }) ); app.use(passport.initialize()); app.use(passport.session()); app.use(methodOverride("_method")); app.get("/", checkAuthenticated, (req, res) => { res.render("index.ejs", { name: req.user.name }); }); app.get("/login", checkNotAuthenticated, (req, res) => { res.render("login.ejs"); }); app.post( "/login", checkNotAuthenticated, passport.authenticate("local", { successRedirect: "/", failureRedirect: "/login", failureFlash: true, }) ); app.get("/register", checkNotAuthenticated, (req, res) => { res.render("register.ejs"); }); app.post("/register", checkNotAuthenticated, async (req, res) => { try { const hashedPassword = await bcrypt.hash(req.body.password, 10); users.push({ id: Date.now().toString(), name: req.body.name, email: req.body.email, password: hashedPassword, }); res.redirect("/login"); } catch { res.redirect("/register"); } }); app.delete("/logout", (req, res) => { req.logOut(); res.redirect("/login"); }); function checkAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect("/login"); } function checkNotAuthenticated(req, res, next) { if (req.isAuthenticated()) { return res.redirect("/"); } next(); } app.listen(3000); 解説①  app.use(passport.session()); Q:express-sessionとpassport.session( )の違いは何か? A:express-sessionはセッション変数を使うためのミドルウェア。passport.session( )とセッション管理を連携させるために呼んでいる。セッション管理をする上でexpress.sessionとpassport.session( )は必ず必要であり、express-sessionを先に呼ばなくてはならない。 req.isAuthenticated = function() { ~~~ } Q:req.isAuthenticatedはどこから来ているのか A:Passport側で元から作られている関数なので、宣言していなくても使用することができる //passport側で既に作られている関数 req.isAuthenticated = function() { var property = 'user'; if (this._passport && this._passport.instance._userProperty) { property = this._passport.instance._userProperty; } return (this[property]) ? true : false; }; 上記の関数がPassport側で記述されている( https://github.com/jaredhanson/passport/blob/a892b9dc54dce34b7170ad5d73d8ccfba87f4fcf/lib/passport/http/request.js#L74 ) middlware/authMiddlwere.js const LocalStrategy = require("passport-local").Strategy; const bcrypt = require("bcrypt"); function initialize(passport, getUserByEmail, getUserById) { const authenticateUser = async (email, password, done) => { const user = getUserByEmail(email); if (user == null) { return done(null, false, { message: "No User With That Email" }); } try { if (await bcrypt.compare(password, user.password)) { return done(null, user); } else { return done(null, false, { message: "Password Incorrect" }); } } catch (e) { return done(e); } }; passport.use(new LocalStrategy({ usernameField: "email" }, authenticateUser)); passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { return done(null, getUserById(id)); }); } module.exports = initialize; 解説② const LocalStrategy = require("passport-local").Strategy; Q:passport-localは何をしているのか? A:Passportでは認証時の動作をストラテジーと呼んでいて、実装ではこのストラテジーの設定をする必要があるとのこと。 passport-localはユーザーIDとパスワード(emailなどに変更可)で認証するストラテジーの雛形を提供している。公式でlocal以外のストラテジーを確認することができる(http://www.passportjs.org/packages/ ) ストラテジー=認証方法で覚えても良さそうですね。 passport.use(new LocalStrategy( ~~~ ); Q:passport.use( new LocalStrategy( ) )は何をしているのか? A:passportとストラテジーの連携(紐付け)を行なっている。セッション管理の紐付けはpassport.sessionでおこない、認証方法の紐付けはpassport.use( new LocalStrategy( ) )でおこなう必要がある。 passport.serializeUser((user, done) => { ~~~ } Q:シリアライズとデシリアライズは何をしているのか? A:詳細は添付記事に詳しく書かれていたので省略します。 データのやり取りではJSONオブジェクトは大きすぎるので一旦、直列化(シリアライズ)してデータ受信後に復元(デシリアライズ)をする。 ちなみにJSフレームワークではアプリケーションレベルでシリアライズ・デシリアライズを行っていてピュアなJSでシリアライズ・でシリアライズをする時はJSON.stringify()とJSON.parseを使う。 さいごに 何気なくアプリ開発をしている中では認証機能をFirebase Authenticationなどに丸投げしていましたが、一から作るとなるとかなり労力がかかるなと思いました。しかしいつまでもブラックボックスにしているわけにもいかず、これを気にログイン・ログアウト機能に触れることができたのはよかったです。次はTokenでの実装もやってみたいです。 参考URL

Viewing all articles
Browse latest Browse all 9145

Trending Articles