Node.js+Express+Passportでログイン認証をしたい

## はじめに
「Node.jsってどんなもんなんだろう？」ってところから始まり。
　　↓
「へぇ〜APIとか簡単にできるじゃん」となり。
　　↓
「ログイン認証とかも割と簡単なんじゃね」と思ったのでやってみました。

## 環境
* macOS Mojave

## Express
`npm`を使ってExpressをインストールする。
entry pointは`app.js`に設定する。

~~~
$ mkdir myapp
$ cd myapp
$ npm init
$ npm install express
~~~

myappに`app.js`を作成し、以下を実装する。

~~~javascript:app.js
const express = require('express')
const app = express()

app.get('/', (req, res) => {
    res.send('Hello World')
})
    
app.listen(3000, () => {
  console.log('Listening on prot 3000')
})
~~~

普通に`node app.js`で起動しても良いが、変更に対して自動的に起動するようにしてほしいので`nodemon`をインストールする。
起動したら、`http://localhost:3000`にアクセスし期待通りの動きをしているか確認する。

~~~
$ npm install nodemon -g
$ npx nodemon app.js
    [nodemon] starting `node app.js`
    Listening on prot 3000
~~~

## Passport
ログイン認証に必要なものをインストールします。
（詳しく説明しませんが、ググれば大丈夫）

~~~
$ npm install body-parser
$ npm install cookie-parser
$ npm install express-session
$ npm install passport
$ npm install passport-local
$ npm install connect-ensure-login
~~~

`public`フォルダを作成し、`login.html`、`ok.html`、`ng.html`を作成する

~~~javascript:app.js
const express = require('express')
const app = express()
const path = require('path')
const session = require('express-session')
const passport = require('passport')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const LocalStrategy = require('passport-local').Strategy;

app.use(express.static(path.join(__dirname, 'public')))
app.use(cookieParser())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(session({ 
  secret: 'secret',
  resave: false,
  saveUninitialized: false,
}))
app.use(passport.initialize())
app.use(passport.session())
passport.use(new LocalStrategy((username, password, done) => {
  if (username !== 'user' || password !== 'passwd') {
    return // ログイン失敗
  }
  return done(null, username) // ログイン成功
}))

passport.serializeUser((user, done) => {
  done(null, user)
})
passport.deserializeUser((user, done) => {
  done(null, user)
})
// ...
~~~

~~~html:login.html
<html>
  <head lang="ja">
    <meta charset="UTF-8">
    <title>ログイン</title>
  </head>
  <body>
    <form action="/login" method="post">
      <div>
        <label>ユーザID：</label>
        <input type="text" name="username">
      </div>
      <div>
        <label>パスワード：</label>
        <input type="password" name="password">
      </div>
      <div>
        <input type="submit" value="Login">
      </div>
    </form>
  </body>
</html>
~~~

## その他
ログインに成功したら`/login/ok`へリダイレクト、失敗したら`/login/ng`へリダイレクトするような実装をします。
ただ、普通にやってしまうとログインに成功していないにも関わらず、`http://localhost:3000/login/ok`と叩けばアクセスできてしまいます。。。
**そこで便利なものがあります！**
`require('connect-ensure-login').ensureLoggedIn('/login')`を挟んでやれば、ログインしていない場合は指定した`/login`にリダイレクトされるようにできます。

~~~javascript:app.js
// ...
app.get('/', (req, res) => {
  res.redirect('/login')
})

app.get('/login', (req, res) => {
  res.sendFile('login.html', { root: path.join(__dirname, 'public') })
})

app.post('/login',
  passport.authenticate('local', { failureRedirect: '/login/ng', session: true }),
      (req, res) => {
        res.redirect('/login/ok')
      }
)

app.get('/login/ng', (req, res) => {
  res.sendFile('ng.html', { root: path.join(__dirname, 'public') })
})

app.get('/login/ok', 
    require('connect-ensure-login').ensureLoggedIn('/login'), // <--- こいつ！！
        (req, res) => {
            res.sendFile('ok.html', { root: path.join(__dirname, 'public') })
});

app.get('/logout', (req, res) => {
  req.logout()
  res.redirect('/login')
})
// ...
~~~

## 最後に
ログイン認証のセッション管理などを`req.isAuthenticated()`で制御させてみたんですけど、想定通りの動きはしなかった。。。

これからは`connect-ensure-login`を使っていこーっと
一応、[ソースコード](https://github.com/nmbry/pass-app)を載せておきます。