nuxtjsに認証機能を付ける

ログインしたいなーっと思ったのでやってみた。

[認証ルート](https://ja.nuxtjs.org/examples/auth-routes)

# express-sessionをインストールする

[express-sessionのインストール](https://www.npmjs.com/package/express-session)

```sh
$ npm install express-session --save
```

#ストアを作る

認証(login)してユーザ情報をストアに保存する。

```javascript:store/auth.js
export const state = () => ({
  authUser: null
})

export const mutations = {
  SET_USER(state, authUser) {
    state.authUser = authUser
  }
}

export const actions = {
  async login({ commit }, { username, password }) {
    try {
      const { data } = await this.$axios.post('/auth/login', { username, password })
      commit('auth/SET_USER', data)
    } catch(error) {
      if (error.response && error.response.status === 401) {
        throw new Error(error.response.data.error)
      }
      throw error
    }
  },
  async logout({ commit }) {
    await this.$axios.post('/auth/logout')
    commit('auth/SET_USER', null)
  }
}
```

セッションに保存しているユーザ情報をストアに保存する。
[nuxtServerInitアクション](https://ja.nuxtjs.org/guide/vuex-store#nuxtserverinit-%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)

```javascript:store/index.js
export const actions = {
  nuxtServerInit({ commit }, { req }) {
    if (req.session && req.session.authUser) {
      commit('auth/SET_USER', req.session.authUser)
    }
  }
}
```

# apiを作る

sessionの設定を記述する。

```javascript:nuxt.config.js
import bodyParser from 'body-parser'
import session from 'express-session'

export default {
  serverMiddleware: [
    bodyParser.json(),
    session({
      secret: 'super-secret-key',
      resave: false,
      saveUninitialized: true,
      cookie: { maxAge: 60000 }
    }),
    '~/api/auth.js'
  ]
}
```

ユーザ、パスワードを認証してユーザ情報をセッションに保存する。

```javascript:api/auth.js
const express = require('express')
const app = express()

app.post('/login', (req, res) => {
  const username = req.body.username
  const password = req.body.password

  if (username === 'demo' && password === 'demo') {
    req.session.authUser = { username }
    return res.json({ username })
  }

  res.status(401).json({ error: '認証失敗' })
})

app.post('/logout', (req, res) => {
  delete req.session.authUser
  res.json({ ok: true })
})

module.exports = {
  path: '/api/auth',
  handler: app
}
```

# middlewareを作る

ストアにユーザ情報が保存されていなければログインページにリダイレクトする。
[middlewareプロパティ](https://ja.nuxtjs.org/api/pages-middleware)

```javascript:middleware/auth.js
export default ({ store, redirect }) => {
  if (!store.state.auth.authUser) {
    return redirect('/login')
  }
}
```

middlewareプロパティに`auth`を設定する。

```vue:pages/index.vue
<template>
  <div>
    ログイン成功 <button @click="logout">ログアウト</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  middleware: 'auth'
  methods: {
    ...mapActions('auth', { postLogout: 'logout' }),
    async logout() {
      await this.postLogout()
      this.$router.push('/login')
    }
  }
}
</script>
```

# ログインページを作る

```vue:pages/login.vue
<template>
  <div>
    <form @submit.prevent="login" :class="{ error: isError }">
      <input v-model="username" type="text" name="username" placeholder="ユーザ名" />
      <input v-model="password" type="password" name="password" placeholder="パスワード" />
      <button type="submit">ログイン</button>
    </form>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  data: () => ({
    username: '',
    password: '',
    isError: false
  }),
  methods: {
    ...mapActions('auth', { postLogin: 'login' }),
    async login() {
      try {
        await this.postLogin({ username: this.username, password: this.password })
        this.isError = false
        this.$router.push('/')
      } catch(e) {
        this.isError = true
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.error {
  input {
    background: salmon;
    border-color: red;
  }
}
</style>
```

# その他

他にも[外部認証API(JWT)](https://ja.nuxtjs.org/examples/auth-external-jwt/)なるものがあるのか。
後でやってみようかな？気になるし。。。