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/