Express上でPassport.jsを使い最小限のログインの仕組みを作る

ExpressにPassport.jsでログイン処理を入れようとしたものの、つまづいたので、まずは最小限から始めてみようと実装した事をまとめました。

# 作るもの

- loginページと成功後の遷移ページを用意しpassport.jsでログインの仕組みを入れる。
- ユーザー名は"user"、パスワードは"password"でログイン成功する。
- 失敗した場合は、ページ遷移せずに再度loginページを表示する。
- 各ページはejsで作成する。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/1dc5d2d2-af53-992a-650f-0ab74b16e58a.jpeg" width="300">


### 作成
- app.js
- router.js
- views/
  - login.ejs　…　ログインページ
  - ok.ejs　…　ログイン成功後に遷移するページ

### npmインストール
- express : 4.17.1
- ejs : 2.6.2
- body-parser : 1.19.0　…　ログインフォームからの値を受け取るため
- passport : 0.4.0
- passport-local : 1.0.0

# 作り方

**1. /loginと/okへルーティングするExpressのWebサーバーを作成**
（app.js、router.js、views/login.ejs、views/ok.ejsを作成）
**2. ログイン処理に必要なモジュールを読む**
（router.jsを変更）
**3. "mylogin"というログイン処理を作成する**
（router.jsを変更）
**4. ログイン処理をミドルウェアとして使う事を宣言**
（router.jsを変更）
**5. どのルーティングにログイン処理を入れるか宣言**
（router.jsを変更）


# 1. /loginと/okへルーティングするExpressのWebサーバーを作成

```:ターミナル
npm install express ejs body-parser -S
```

```js:app.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");

// ログイン画面でフォームを利用するため
app.use(bodyParser.urlencoded({extended:true}));

app.use("/",require("./router.js"));

app.listen(3000);
```

```js:router.js
const router = require("express").Router();

router.get("/login",(req,res) => {
  
  res.render("./login.ejs");
});

router.get("/ok",(req,res) => {
  res.render("./ok.ejs");
});

module.exports = router;
```

```html:/views/login.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ログイン</title>
</head>
<body>
  <main>
    <h1>ログイン</h1>
    <form action="/login" method="POST" class="form">
      <p class="form__text">
        <input type="text" name="username">
      </p>
      <p class="form__text">
        <input type="text" name="password">
      </p>
      <input type="submit" value="ログイン">
    </form>
  </main>
</body>
</html>
```

```html:/views/ok.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ログイン成功</title>
</head>
<body>
  <h1>ログイン成功</h1>
</body>
</html>
```

# 2. ログイン処理に必要なモジュールを読む

```:ターミナル
npm install passport passport-local -S
```

```js:router.js
const router = require("express").Router();

// ★★追加★★
// 2. ログイン処理に必要なモジュールを読む
const passport = require("passport");

// ★★追加★★
// 2. ログイン処理に必要なモジュールを読む
const LocalStrategy = require("passport-local").Strategy;

router.get("/login",(req,res) => {

  res.render("./login.ejs");
});

router.get("/ok",(req,res) => {
  res.render("./ok.ejs");
});

module.exports = router;
```

# 3. "mylogin"というログイン処理を作成する

```js:router.js
const router = require("express").Router();
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;

// ★★追加★★
// 3. "mylogin"というログイン処理を作成する 
passport.use("mylogin",
  new LocalStrategy({
    usernameField: "username", // formのname属性が"username"を指定
    passwordField: "password", // formのname属性が"password"を指定
  },(username,password,done) => {

    // ログイン処理本体
    // formから受け取ったusernameが"user"、passwordが"password"ならばログイン成功とする
    if(username==="user" && password==="password"){
      return done(null,username); // ログイン成功と判定する
    }

    return done(null,false); // ログイン失敗と判定する
  })
);

router.get("/login",(req,res) => {

  res.render("./login.ejs");
});

router.get("/ok",(req,res) => {
  res.render("./ok.ejs");
});

module.exports = router;
```

# 4. ログイン処理をミドルウェアとして使う事を宣言

```JS:router.js
const router = require("express").Router();
const passport = require("passport"); 
const LocalStrategy = require("passport-local").Strategy;

passport.use("mylogin",
  new LocalStrategy({
    usernameField: "username",
    passwordField: "password",
  },(username,password,done) => {

    if(username==="user" && password==="password"){
      return done(null,username);
    }

    return done(null,false);
  })
);


// ★★追加★★
// 4. ログイン処理をミドルウェアとしてルーターが使う事を宣言
router.use(passport.initialize());

router.get("/login",(req,res) => {

  res.render("./login.ejs");
});

router.get("/ok",(req,res) => {
  res.render("./ok.ejs");
});

module.exports = router;
```

# 5. どのルーティングにログイン処理を入れるか宣言

ここで追加する**`passport.authenticate()`というメソッドに、`session: false`を渡す事が、最小限の実装をするにあたってのポイント。**
そうしない場合、「3. "mylogin"というログイン処理を作成する」で、`passport.serializeUser`と`passport.deserializeUser`を実装する必要がある。
これらはログイン情報をセッションに保存したり取り出して照合するもの。

```JS:router.js
const router = require("express").Router();
const passport = require("passport"); 
const LocalStrategy = require("passport-local").Strategy;

passport.use("mylogin",
  new LocalStrategy({
    usernameField: "username",
    passwordField: "password",
  },(username,password,done) => {

    if(username==="user" && password==="password"){
      return done(null,username);
    }

    return done(null,false);
  })
);


router.use(passport.initialize());

router.get("/login",(req,res) => {

  res.render("./login.ejs");
});

// ★★追加★★
// 5. どのルーティングにログイン処理を入れるか宣言
// /loginにpostリクエストがあった場合は、上で作成した"mylogin"というログイン処理をする
// ログインに成功したら"/ok"へ、失敗したら"/login"にリダイレクトする
router.post("/login",passport.authenticate(
  "mylogin",
  {
    successRedirect: "/ok",
    failureRedirect: "/login",
    session: false // セッションにログイン情報を保存しない。trueとすると、passport.serializeUserやpassport.deserializeUserというメソッドを実装する事でセッションに保存したログイン情報が正しいか判別出来る。
  }
));

router.get("/ok",(req,res) => {
  res.render("./ok.ejs");
});


module.exports = router;
```

以上です。

# おわりに

とりあえず認証のスタート地点に立てた気がします。