Pages

Cara Menggunakan JWT dengan Node.js


Pada praktikum kali ini, kita akan belajar menggunakan JSON Web Token (JWT) dengan Node.js.

Namun sebelumnya, kita bahas dulu apa itu JWT, kenapa JWT digunakan, dan bagaimana cara kerjanya.

Artikel ini dibantu dengan source code, dapatkan di sini.

Apa itu JWT dan Bagaimana Cara Kerjanya?

JWT adalah sebuah string yang ter-encode yang aman untuk dikirimkan antar komputer, jika kedua komputer tersebut terkoneksi dengan HTTPS.

Token tersebut merepresentasikan sebuah nilai yang hanya dapat diakses hanya oleh komputer yang memiliki akses ke secret key yang darimana token tersebut dienkripsi.

Misalnya, client memasukkan email dan passwordnya ke sebuah server untuk login.

Jika email dan passwordnya valid, maka server akan membuatkan sebuah token yang berisi payload dan sebuah secret key.

String yang dihasilkan dari enkripsi inilah yang disebut token.

Server kemudian mengirimkan kembali token tadi ke client.

Client akan men-save token ini yang kemudian nantinya akan digunakan untuk request lainnya (yang membutuhkan credentials tadi, seperti member area atau semacamnya).

Mengapa kita Menggunakan JWT?

Alasan utama kita menggunakan JWT adalah karena kita ingin memastikan bahwa client data yang dikirimkan berasal dari sumber yang otentik.

Mungkin alasannya serupa dengan mengapa kita menggunakan session dan cookies.

Menggunakan JWT dengan Node.js

Sekarang kita akan mempelajari praktek penggunaan JWT dengan Node.js.

Pertama-tama, buatlah sebuah folder baru bernama "ls-nodejs-jwt-sederhana".

Kemudian, masuk ke dalam folder tersebut dengan Power Shell dan ketikkan:

npm init

Selanjutnya, isi pertanyaannya dengan jawaban sesuka Anda.

Hanya saja, pastikan bahwa main script-nya adalah index.js agar sesuai dengan pembahasan di artikel ini.

Dari langkah tadi, diharapkan akan dihasilkan package.json seperti ini:

{
  "name": "ls-nodejs-jwt-sederhana",
  "version": "1.0.0",
  "description": "Penggunaan JWT dalam Node.js",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Lusfikar Sheba",
  "license": "MIT"
}

Menginstall Modul

Selanjutnya, install modul-modul yang dibutuhkan berikut ini.

npm install bcryptjs --save

npm install express --save

npm install jsonwebtoken --save

Coding

Selanjutnya, buat file baru bernama "index.js", "config.js", dan "user.js" di dalam folder "ls-nodejs-jwt-sederhana".

Isi dari file "config.js":

module.exports = {
    'secret': 'blablasecret'
};

File tersebut berisi secret key untuk menggenerate token JWT.

Berikut ini isi dari file "user.js":

var bcrypt = require('bcryptjs');

function User(){
}

User.findOne = function(email, callback) {
    if(email == "admin@lusfikars.space"){
        callback(null, {
            _id : 0,
            email: email,
            password: bcrypt.hashSync("adminpassword", 8)
        });
    } else if(email == "guest@lusfikars.space"){
        callback(null, {
            _id : 1,
            email: email,
            password: bcrypt.hashSync("guestpassword", 8)
        });
    }
};

User.findById = function(id, callback) {
    if(id == 0){
        callback(null, {
            _id : 0,
            email: "admin@lusfikars.space",
            password: bcrypt.hashSync("adminpassword", 8)
        });
    } else if(id == 1){
        callback(null, {
            _id : 1,
            email: "guest@lusfikars.space",
            password: bcrypt.hashSync("guestpassword", 8)
        });
    }
};

module.exports = User;

Di sini, kita mengemulasikan model dari database.

Anda bisa perhatikan ada method "findOne" dan "findById".

Kedua method tersebut tidak ada hubungannya dengan DBMS apapun atau dengan kata lain kita tidak perlu menggunakan DBMS apapun untuk menyederhanakan pembahasan ini.

Akan tetapi, emulasi model database tetap ada sehingga menyerupai request ke DBMS dalam hal ini Mongodb.

Berikut ini isi file "index.js":

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
var config = require('./config');
var User = require('./user');

function verify(req, res, next) {
    var token = req.headers['x-access-token'];

    if (!token) 
        return res.status(403).send({ auth: false, message: 'Tidak ada token.' });
    
    jwt.verify(token, config.secret, function(err, decoded) {      
        if (err) 
            return res.status(500).send({ auth: false, message: 'Gagal authenticate token.' });    
        
        req.userId = decoded.id;
        console.log(req.userId);
        next();
    });
} 

app.set('port', (process.env.PORT || 5000));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.get('/', function (req, res) {
    res.status(200).send('Server jalan bro!');
});

app.post('/login', function(req, res) {  
    User.findOne(req.body.email, function (err, user) {
        if (err) 
            return res.status(500).send('Error ajah.');

        if (!user)
            return res.status(404).send('User gak ada.');
        
        var passwordIsValid = bcrypt.compareSync(req.body.password, user.password);

        if (!passwordIsValid)
            return res.status(401).send({ 
                auth: false, 
                token: null 
            });
        
        var token = jwt.sign({ id: user._id }, config.secret, {
            expiresIn: 86400
        });
       
        res.status(200).send({ 
            auth: true, 
            token: token 
        });
    });
});

app.get('/logout', function(req, res) {
    res.status(200).send({ 
        auth: false, 
        token: null 
    });
});

app.get('/member', verify, function(req, res, next) {

    User.findById(req.userId, function (err, user) {
        if (err)
            return res.status(500).send("Error ajah.");

        if (!user)
            return res.status(404).send("Usernya gak ketemu.");

        res.status(200).send(user);
    });
});

app.listen(app.get('port'), function () {
    console.log('Node app berjalan di port', app.get('port'));
});   

Yang perlu diperhatikan dari kode di atas adalah bahwa kode ini tidak melakukan registrasi user baru.

Mengapa?

Jawabannya ada pada script "user.js" di mana kita mendefinisikan user terregristasi secara emulasi.

Jadi, user yang ada adalah statis dan tidak bisa ditambahkan lagi.

Jadi hanya ada 2 user valid di sini:

  1. admin@lusfikars.space, dengan password: "adminpassword" dan _id:0
  2. guest@lusfikars.space dengan password: "guestpassword" dan _id:1
Tidak ada penambahan user apapun lagi.

Dan itu semua didefinisikan secara statis di kode "user.js".

Pada file "index.js", hal pertama yang kita lakukan adalah, me-require semua modul yang dibutuhkan:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
var config = require('./config');
var User = require('./user');

Yang kedua adalah mempersiapkan Express.js untuk melayani client melalui kode ini:

app.set('port', (process.env.PORT || 5000));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Dan kode ini:

app.listen(app.get('port'), function () {
    console.log('Node app berjalan di port', app.get('port'));
}); 

Kemudian menulis respons untuk login, logout dan mengakses member area:

app.post('/login', function(req, res) {  
    User.findOne(req.body.email, function (err, user) {
        if (err) 
            return res.status(500).send('Error ajah.');

        if (!user)
            return res.status(404).send('User gak ada.');
        
        var passwordIsValid = bcrypt.compareSync(req.body.password, user.password);

        if (!passwordIsValid)
            return res.status(401).send({ 
                auth: false, 
                token: null 
            });
        
        var token = jwt.sign({ id: user._id }, config.secret, {
            expiresIn: 86400
        });
       
        res.status(200).send({ 
            auth: true, 
            token: token 
        });
    });
});

app.get('/logout', function(req, res) {
    res.status(200).send({ 
        auth: false, 
        token: null 
    });
});

app.get('/member', verify, function(req, res, next) {

    User.findById(req.userId, function (err, user) {
        if (err)
            return res.status(500).send("Error ajah.");

        if (!user)
            return res.status(404).send("Usernya gak ketemu.");

        res.status(200).send(user);
    });
});

Adapun kode ini bisa diabaikan karena hanya untuk mengecek bahwa server telah berjalan:

app.get('/', function (req, res) {
    res.status(200).send('Server jalan bro!');
});

Pada saat login, sesuai dengan cara kerjanya, kita mengecek kevalidan credentials dan jika valid, maka hadiahkan client dengan token:

        var passwordIsValid = bcrypt.compareSync(req.body.password, user.password);

        if (!passwordIsValid)
            return res.status(401).send({ 
                auth: false, 
                token: null 
            });
        
        var token = jwt.sign({ id: user._id }, config.secret, {
            expiresIn: 86400
        });
       
        res.status(200).send({ 
            auth: true, 
            token: token 
        });

Tentunya kita akan mengemulasikan model User terlebih dahulu untuk mendapatkan user.password dengan method User.findOne.

Pada saat logout, bersihkan token:

    res.status(200).send({ 
        auth: false, 
        token: null 
    });

Dan ketika user ingin masuk member area, pastikan dia adalah user yang valid.

Caranya adalah dengan menggunakan middleware bernama "verify" yang didefiniskan seperti ini:

function verify(req, res, next) {
    var token = req.headers['x-access-token'];

    if (!token) 
        return res.status(403).send({ auth: false, message: 'Tidak ada token.' });
    
    jwt.verify(token, config.secret, function(err, decoded) {      
        if (err) 
            return res.status(500).send({ auth: false, message: 'Gagal authenticate token.' });    
        
        req.userId = decoded.id;
        console.log(req.userId);
        next();
    });
} 

Yang penggunaannya dengan cara seperti ini:

app.get('/member', verify, function(req, res, next) {

Dari fungsi verify tadi, req.userId didapatkan dari decoded.id yang notabene ada di dalam JWT.

Lalu, bagaimana server mendapatkan JWT yang tadinya ada di client? Begini:

var token = req.headers['x-access-token'];

Mencoba Aplikasi Ini

Sekarang, kita akan mencoba penggunaan kode ini sebagai client dengan menggunakan Postman.

Lakukan login terlebih dahulu:


Isi email dan password pada key-value pair-nya.

Kemudian pilih post dan alamatkan ke http://localhost:5000/login.

Nanti akan didapatkan auth dan token.

Copy isi token tersebut.

Sekarang akses member area:


Di member area, masukkan key dengan "x-access-token". Value-nya adalah token yang dicopy tadi.

Sekarang akses GET dengan URL http://localhost:5000/member.

Hasilnya, admin@lusfikars.space telah teridentifikasi dengan password yang sudah di-hash.

Terakhir, kita logout dengan GET http://localhost:5000/logout:


Nanti bisa kita lihat bahwa token menjadi NULL.

Sekian.

Sumber:
  • https://medium.freecodecamp.org/securing-node-js-restful-apis-with-json-web-tokens-9f811a92bb52
  • https://github.com/adnanrahic/securing-restful-apis-with-jwt