6 min read

NUXT2 Login dengan express-session

Req:

  • Nuxt 2 dengan backend express.js dan axios
  • express-session: Untuk menyimpan session user yang login
  • body-parser: Untuk menangkap request dari form body.

Skenario:

  • Aplikasi memiliki 3 halaman
    • Home (index): Bisa diakses sebelum atau sesudah login
    • Login: Hanya bisa diakses jika belum login, saat diakses setelah login akan diarahkan ke home
    • Secret: Hanya bisa diakses setelah login.

Memasang paket express-session dan body-parser

npm install  express-session body-parser

Menyiapkan Halaman

Seperti yang disebut tadi di skenario atas, kita akan memiliki 3 halaman, buat halaman di folder pages

Home ( pages/index.vue )

<template>
  <section class="container">
    <div>
      <logo/>
      <h1 class="title">
        loginnuxt
      </h1>
      <h2 class="subtitle">
        Home Page
      </h2>
      <div class="links">
        <nuxt-link
          to="/secret"
          class="button--green">Secret Page</nuxt-link>
        <nuxt-link
          to="/login"
          class="button--grey">Login</nuxt-link>
      </div>
    </div>
  </section>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  }
}
</script>

<style>

.container {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

Halaman Secret (pages/secret.vue)

<template>
  <section class="container">
    <h1 class="title">
      Halaman rahasia, hanya yang sudah login bisa akses halaman ini.
    </h1>
  </section>
</template>

Halaman Login(pages/secret.vue)

<template>
  <section class="container">
    <form
      method="POST"
      action="/login">
      <div class="form-group">
        <label for="exampleInputEmail1">Email address</label>
        <input
          id="exampleInputEmail1"
          name="email"
          type="email"
          class="form-control"
          aria-describedby="emailHelp"
          placeholder="Enter email">
      </div>
      <div class="form-group">
        <label>Password</label>
        <input
          id="password"
          type="password"
          class="form-control"
          name="password"
          placeholder="Password">
      </div>
      <div class="form-check">
        <button
          type="submit"
          class="btn btn-primary">Submit</button>
      </div>

    </form>
  </section>
</template>

Fungsi Login

Membuat fungsi login kita perlu tambahkan paket yang sudah dipasang tadi di berkas server/index.js. Untuk simulasi login saya menggunakan fake t̶a̶x̶y̶ api dari https://reqres.in/.

Di bagian atas, kita tambahkan paket yang sudah diunduh.

const bodyParser = require('body-parser')
const session = require('express-session')
const axios = require('axios')

Tambahkan konfigurasi agar bisa memabca request dari body form dan membentuk objek “req.session

// agar bisa menammngkap req body dari form
app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())

// Sessions untuk membentuk `req.session`
app.use(session({
  secret: 'super-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 60000 }
}))

Di paling bawah tambahkan route untuk login

app.post('/login', function (req, res) {
  const body = {
    email: req.body.email,
    password: req.body.password
  }
  // res.json(body)
  axios.post('https://reqres.in/api/login', body)
    .then((response) => {
      req.session.userToken = response.data.token
      res.redirect('http://localhost:3000/secret')
    })
    .catch((error) => {
      console.log('caught', error.message)
      res.redirect('http://localhost:3000/login?info=periksa kembali email dan password anda')
    })
})

Keseluruhan berkas menjadi seperti ini

const express = require('express')
const consola = require('consola')
// menambahkan body parser, express sesion, dan axios
const bodyParser = require('body-parser')
const session = require('express-session')
const axios = require('axios')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000

app.set('port', port)

// agar bisa menammngkap req body dari form
app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())

// Sessions untuk membentuk `req.session`
app.use(session({
  secret: 'super-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 60000 }
}))


// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')

async function start() {
  // Init Nuxt.js
  const nuxt = new Nuxt(config)

  // Build only in dev mode
  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  }

  // Give nuxt middleware to express
  app.use(nuxt.render)

  // Listen the server
  app.listen(port, host)
  consola.ready({
    message: `Server listening on http://${host}:${port}`,
    badge: true
  })
}
start()

app.post('/login', function (req, res) {
  const body = {
    email: req.body.email,
    password: req.body.password
  }
  // res.json(body)
  axios.post('https://reqres.in/api/login', body)
    .then((response) => {
      req.session.userToken = response.data.token
      res.redirect('http://localhost:3000/secret')
    })
    .catch((error) => {
      console.log('caught', error.message)
      res.redirect('http://localhost:3000/login?info=periksa kembali email dan password anda')
    })
})

Kode di atas terlihat menyimpan respon token setelah login di req.session.userToken, untuk memanfaatkan objek token tersebut kita membutuhkan vuex di nuxt. Pertama buat dulu berkas index.js di folder store.

export const state = () => ({
  token: null,
  login: false
})


// getter
export const getters = {
  getToken (state) {
    return state.token
  },
  getLogin (state) {
    return state.login
  }
}

export const mutations = {
  setLogin (state, data) {
    state.token = data.token
    state.login = data.login
  }
}


// actions
export const actions = {

  async nuxtServerInit ({ commit }, { req }) {
    if (req.session && req.session.userToken) {
      const data = {
        token: req.session.userToken,
        login: true
      }
      commit('setLogin', data)
    }
  }
}

Kode di atas sederhana saja, state berfungsi sebagai “variabel” yang akan menyimpan nilai token dan status login. Sedangkan getters berfungsi untuk mengambil nilai state agar tidak langsung mengakses state. Sedangkan mutation berfungsi untuk mengubah nilai state, di dalam action ada fungsi nuxtServerInit() dan fungsi inilah yang kita manfaatkan untuk mendapatkan token.

Fungsi nuxtServerInit ini otomatis dieksekusi saat proses di sisi server, sehingga cocok untuk mengambil data hasil pengolahan server ( dalam kasus ini adalah si express.js ).

Sampai saat ini alur yang sudah terbentuk adalah:

  • User akses halaman login dan memasukan email dan password
  • Fungsi login di server/index.js mengecek ke reqres.in dan jika berhasil akan mengirimkan token
  • Token disimpan di objek req.session.userToken
  • Saat selesai proses dan kembali ke halaman awal, nuxtServerInit() bekerja dan mengecek objek req.session, jika ada maka akan disimpan ke dalam state melalu mutation setLogin().
  • Mutation setLogin() sendiri hanya “menyimpan” token dan mengubah data login sesuai yang ditentukan. Token berisi dari req.session.token, dan login menjadi true.

Sampai sini walaupun token dan status login sudah terbaca namun halaman secret belum benar-benar dibatasi aksesnya, oleh karena itu kita butuh middleware auth.

Pada folder middleware kita buat satu berkas auth.js dan unauth.js

auth.js

export default function ({ store, error, redirect }) {
  if (!store.getters.getLogin) {
    return redirect('/')
  }
}

unauth.js

export default function ({ store, error, redirect }) {
  if (store.getters.getLogin) {
    return redirect('/')
  }
}

Lalu pada halaman secret dan login kita tambahkan porperty middleware

secret.vue

<template>
  <section class="container">
    <h1 class="title">
      Halaman rahasia, hanya yang sudah login bisa akses halaman ini.
    </h1>
  </section>
</template>

<script>
  export default {
    middleware: "auth"
  }
</script>

login.vue

<template>
  <section class="container">
    {{ info }}
    <form
      method="POST"
      action="/login">
      <div class="form-group">
        <label for="exampleInputEmail1">Email address</label>
        <input
          id="exampleInputEmail1"
          name="email"
          type="email"
          class="form-control"
          aria-describedby="emailHelp"
          placeholder="Enter email">
      </div>
      <div class="form-group">
        <label>Password</label>
        <input
          id="password"
          type="password"
          class="form-control"
          name="password"
          placeholder="Password">
      </div>
      <div class="form-check">
        <button
          type="submit"
          class="btn btn-primary">Submit</button>
      </div>

    </form>
  </section>
</template>

<script>
  export default {
    middleware: "unauth",
    watchQuery: ['info'],
    asyncData({query}) {
      return {
        info: query.info
      }
    }
  }
</script>

Logout

Untuk logout sebenarnya lebih mudah, yaitu cukup menghapus req.session yang sudah dibentuk. Sebelumnya kita buat tombol logout, menggantikan tombol login jika kita sudah memiliki session.

index.vue

<template>
  <section class="container">
    <div>
      <logo/>
      <h1 class="title">
        loginnuxt
      </h1>
      <h2 class="subtitle">
        Home Page
      </h2>
      <div class="links">
        <nuxt-link
          to="/secret"
          class="button--green">Secret Page</nuxt-link>
        <div
          v-if="!isLogin">
          <nuxt-link
            to="/login"
            class="button--grey">Login</nuxt-link>
        </div>
        <div v-else>
          <a
            href="javascript:void(1);"
            class="button--grey "
            @click="logout">Logout</a>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  computed: {
    isLogin() {
      return this.$store.getters.getLogin
    }
  },
  methods: {
    logout() {
      this.$axios.post('http://localhost:3000/logout')
        .then((res) => {
          window.location.reload(true)
        })
        .catch(function (e) {
          alert('maaf terjadi kesalaahan')
        })
    }
  }
}
</script>

<style>

.container {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

Lalu tambahkan route logout di server/index.js, tambahkan script ini di paling bawah

app.post('/logout', function (req, res) {
  delete req.session.userToken
  res.json({ ok: true })
  res.send()
})

Sekarang silahkan login.

 

Kode: https://gitlab.com/ariesmaulana/loginnuxt

Referensi: https://nuxtjs.org/examples/auth-routes/