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/