Memberi Fungsi Authentikasi Pada Aplikasi MEAN Stack

Pada artikel sebelumnya, penulis membuat aplikasi CRUD data guru. Dalam artikel ini penulis akan menambahkan fungsi authentikasi dalam aplikasi tersebut, sehingga hanya pengguna yang terdaftar saja yang bisa mengakses data guru dan meng-editnya. Kode authentikasi pada aplikasi ini diadaptasi dari artikel "Handling User Authentication With the MEAN Stack". Berikut adalah tampilan dari aplikasi yang akan dibuat:
Login
alt Registrasi
alt Data guru
alt Jika pengguna mengakses daftar guru tanpa login terlebih dahulu, maka akan dialihkan ke halaman login. Semua user yang telah terdaftar akan disimpan dalam database mongodb, jadi pengguna tidak perlu mendaftar untuk kedua kalinya jika membuka aplikasi ini di lain waktu, cukup melakukan login. Untuk menjalankan aplikasi ini diperlukan node.js dan database mongodb.

1. Install aplikasi CRUD guru.
Template dari aplikasi daftar guru pada artikel sebelumnya dapat didownload di sini. Selanjutnya install modul-modul yang diperlukan dengan cara, masuk ke folder aplikasi melalui cmd lalu ketik:

npm install  

Sekarang cek apakah aplikasi tersebut bisa berjalan. Jalankan database mongo, melalui cmd ketik:

mongod  

Lalu jalankan webserver, buka cmd pada folder aplikasi lalu ketik:

nodemon  

Ketik localhost:3000 pada browser. Jika berhasil, akan keluar tampilan daftar guru.

2. Install modul-modul yang diperlukan.
Modul yang diperlukan untuk menjalankan aplikasi authentikasi ini adalah:

  • bcrypt-nodejs
  • express-session
  • passport
  • passport-local
  • passport-local-mongoose
Install semua modul yang akan digunakan untuk proses autentikasi dengan cara, pada cmd ketik:

npm install bcrypt-nodejs express-session passport passport-local passport-local-mongoose --save

Selanjutnya ubah isi dari app.js, tambahkan dengan:

//tambahkan variabel modul baru
var expressSession = require('express-session');  
var mongoose = require('mongoose');  
var hash = require('bcrypt-nodejs');  
var passport = require('passport');  
var LocalStrategy = require('passport-local').Strategy;

//var route untuk API autentikasi
var route = require('./routes/api.js');

//middleware session agar user dapat terus login
app.use(require('express-session')({  
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());  
app.use(passport.session());

//route untuk API
app.use('/user', route);

//configurasi passport
var Account = require('./models/user');  
passport.use(new LocalStrategy(Account.authenticate()));  
passport.serializeUser(Account.serializeUser());  
passport.deserializeUser(Account.deserializeUser());

Lihat isi dari app.js di sini.

3. Buat Model dan API untuk user
Buat model baru untuk user, /models/user.js:

// user model
var mongoose = require('mongoose');  
var Schema = mongoose.Schema;  
var passportLocalMongoose = require('passport-local-mongoose');

var User = new Schema({  
  username: String,
  password: String
});

User.plugin(passportLocalMongoose);

module.exports = mongoose.model('users', User);  

Buat API untuk user, /routes/api.js

var express = require('express');  
var router = express.Router();  
var passport = require('passport');

var User = require('../models/user.js');

//API register
router.post('/register', function(req, res) {  
  User.register(new User({ username: req.body.username }),
    req.body.password, function(err, account) {
    if (err) {
      return res.status(500).json({
        err: err
      });
    }
    passport.authenticate('local')(req, res, function () {
      return res.status(200).json({
        status: 'Registration successful!'
      });
    });
  });
});

//API login
router.post('/login', function(req, res, next) {  
  passport.authenticate('local', function(err, user, info) {
    if (err) {
      return next(err);
    }
    if (!user) {
      return res.status(401).json({
        err: info
      });
    }
    req.logIn(user, function(err) {
      if (err) {
        return res.status(500).json({
          err: 'Could not log in user'
        });
      }
      res.status(200).json({
        status: 'Login successful!'
      });
    });
  })(req, res, next);
});

//API logout
router.get('/logout', function(req, res) {  
  req.logout();
  res.status(200).json({
    status: 'Bye!'
  });
});

//API untuk mengecek user login atau tidak
router.get('/status', function(req, res) {  
  if (!req.isAuthenticated()) {
    return res.status(200).json({
      status: false
    });
  }
  res.status(200).json({
    status: true
  });
});

module.exports = router;  


4. View Registrasi dan Login

Selanjutnya buat tampilan registrasi, pada views/index.ejs tambahkan kode:

 <!--template register-->
    <script type="text/ng-template" id="/register.html">
      <h2>Daftar</h2>
        <div ng-show="error">{{errorMessage}}</div>
        <form ng-submit="register()">
        <label>Username</label>
        <input type="text" name="username" ng-model="registerForm.username" required><br><br>
        <label>Password</label>
        <input type="password" name="password" ng-model="registerForm.password" required><br><br>
        <button type="submit"  ng-disabled="disabled">Daftar</button>
       </form>
       <p>Sudah punya akun? silahkan <a href="#/login">login</a>.</p>
    </script>

Untuk tampilan login:

<!--template login-->  
    <script type="text/ng-template" id="/login.html">
      <h2>Login</h2>
        <div ng-show="error">{{errorMessage}}</div>
        <form ng-submit="login()">
        <label>Username</label>
        <input type="text" name="username" ng-model="loginForm.username" required><br><br>
        <label>Password</label>
        <input type="password" name="password" ng-model="loginForm.password" required><br><br>
        <button type="submit"  ng-disabled="disabled">Login</button>
       </form><br>
       <p>Untuk mengakses daftar guru silahkan Login terlebih dahulu.<br>Belum punya akun? silahkan <a href="#/register">registrasi</a>.</p>
    </script>

Selanjutnya ubah template daftar guru untuk menampilkan tombol logout:

//template guru---
<script type="text/ng-template" id="/guru.html">  
      <button ng-controller="logoutController" ng-click='logout()'>Logout</button><br><br>
      Cari Nama Guru: <input type = "text" ng-model="search.nama">
        <ul>
          <li ng-repeat="guru in guru | filter: search">
            <p ng-show="!editing[$index]">Nama: {{guru.nama}}</p>
                  <p ng-show="!editing[$index]">Lokasi: {{guru.lokasi}}</p>
                  <p ng-show="!editing[$index]">Biaya: {{guru.biaya}}</p>
                  <button ng-show="!editing[$index]" ng-click="edit($index)">Edit</button>
                  <button ng-show="!editing[$index]" ng-click="remove($index)">Hapus</button>

                  <form ng-show="editing[$index]">
                  Nama: <input ng-show="editing[$index]" type="text" ng-model="guru.nama"><br><br>
                  Lokasi: <input ng-show="editing[$index]" type="text" ng-model="guru.lokasi"><br><br>
                  Biaya: <input ng-show="editing[$index]" type="number" ng-model="guru.biaya"><br><br>
                  <button ng-show="editing[$index]" ng-click="update($index)">Update</button>
                  <button ng-show="editing[$index]" ng-click="cancel($index)">Cancel</button>
                  </form>
          </li>
        </ul>

        <!--Form input-->   
        <h3>Tambah Data Guru</h3>
        <form ng-submit='save()'>
        Nama: <input ng-model='newNama' type="text"><br><br>
        Lokasi: <input ng-model='newLokasi' type="text"><br><br>
        Biaya: <input ng-model='newBiaya' type="number"><br><br>
        <input type='submit' value='Simpan Data'>
        </form>
    </script>

Terakhir pada bagian library index.ejs tambahkan script untuk memanggil jquery, authController, dan authServices.

<!--library-->  
<script type="text/javascript" src="javascripts/jquery-3.1.1.min.js"></script>  
<script type="text/javascript" src="javascripts/authControllers.js"></script>  
<script type="text/javascript" src="javascripts/authServices.js"></script>  

download jquery dan masukkan ke dalam /public/javascripts.

lihat isi dari index.ejs di github

5. Buat Route Pada controller.js
Tambahkan route pada public/javascripts/controller.js dengan menambahkan $routeProvider:

app.config(['$routeProvider', function($routeProvider){  
    $routeProvider.when('/', {
        templateUrl: '/guru.html',
        controller: 'GuruController',
        access: {restricted: true}
    });
    $routeProvider.when('/login', {
      templateUrl: '/login.html',
      controller: 'loginController',
      access: {restricted: false}
    })
    $routeProvider.when('/logout', {
      controller: 'logoutController',
      access: {restricted: true}
    })
    $routeProvider.when('/register', {
      templateUrl: '/register.html',
      controller: 'registerController',
      access: {restricted: false}
    })
    $routeProvider.when('/check', {
      template: '<h1>Halaman ini hanya bisa diakses oleh pengguna</h1>',
      access: {restricted: true}
    })
    $routeProvider.otherwise({
      redirectTo: '/login'
    });
}]);

Route diperlukan agar aplikasi mengerti template mana yang harus ditampilkan ketika diakses oleh pengguna, parameter restricted pada property access berfungsi untuk membatasi akses pada halaman yang terkait, jika bernilai true berarti halaman tersebut hanya dapat diakses jika pengguna melakukan login terlebih dahulu.

6. Buat Fungsi-Fungsi Autentikasi
buat file baru public/javascripts/authServices lalu isi dengan kode:

//service---
angular.module('app').factory('AuthService',  
  ['$q', '$timeout', '$http',
  function ($q, $timeout, $http) {

    // create user variable
    var user = null;

    // return available functions for use in the controllers
    return ({
      isLoggedIn: isLoggedIn,
      getUserStatus: getUserStatus,
      login: login,
      logout: logout,
      register: register
    });

//fungsi untuk membuat "jika user login maka true"---
    function isLoggedIn() {
      if(user) {
        return true;
      } else {
        return false;
      }
    }

//fungsi untuk mendapatkan status user apakah login atau tidak---
    function getUserStatus() {
      return $http.get('/user/status')
      // handle success
      .success(function (data) {
        if(data.status){
          user = true;
        } else {
          user = false;
        }
      })
      // handle error
      .error(function (data) {
        user = false;
      });
    }

//fungsi login---
    function login(username, password) {

      // create a new instance of deferred
      var deferred = $q.defer();

      // send a post request to the server
      $http.post('/user/login',
        {username: username, password: password})
        // handle success
        .success(function (data, status) {
          if(status === 200 && data.status){
            user = true;
            deferred.resolve();
          } else {
            user = false;
            deferred.reject();
          }
        })
        // handle error
        .error(function (data) {
          user = false;
          deferred.reject();
        });

      // return promise object
      return deferred.promise;

    }

//fungsi logout---
    function logout() {

      // create a new instance of deferred
      var deferred = $q.defer();

      // send a get request to the server
      $http.get('/user/logout')
        // handle success
        .success(function (data) {
          user = false;
          deferred.resolve();
        })
        // handle error
        .error(function (data) {
          user = false;
          deferred.reject();
        });

      // return promise object
      return deferred.promise;

    }

//fungsi registrasi---
    function register(username, password) {

      // create a new instance of deferred
      var deferred = $q.defer();

      // send a post request to the server
      $http.post('/user/register',
        {username: username, password: password})
        // handle success
        .success(function (data, status) {
          if(status === 200 && data.status){
            deferred.resolve();
          } else {
            deferred.reject();
          }
        })
        // handle error
        .error(function (data) {
          deferred.reject();
        });

      // return promise object
      return deferred.promise;

    }

}]);

file authService.js berfungsi untuk memberitahu aplikasi apa yang harus dilakukan ketika fungsi-fungsi seperti login atau registrasi dijalankan.

7. Buat Controller Untuk Template-Template Autentikasi
Buat file baru public/javascripts/authControllers.js, controller ini berfungsi untuk mengontrol data pada template yang bersangkutan, controller dan template saling dihubungkan oleh route yang terdapat dalam controller.js. Untuk authControllers.js isi dengan kode:

//controller untuk login---
angular.module('app').controller('loginController',  
  ['$scope', '$location', 'AuthService',
  function ($scope, $location, AuthService) {

    $scope.login = function () {

      // initial values
      $scope.error = false;
      $scope.disabled = true;

      // call login from service
      AuthService.login($scope.loginForm.username, $scope.loginForm.password)
        // handle success
        .then(function () {
          $location.path('/');
          $scope.disabled = false;
          $scope.loginForm = {};
        })
        // handle error
        .catch(function () {
          $scope.error = true;
          $scope.errorMessage = "Password atau Username yang anda masukkan salah";
          $scope.disabled = false;
          $scope.loginForm = {};
        });

    };

}]);

//controller untuk logout
angular.module('app').controller('logoutController',  
  ['$scope', '$location', 'AuthService',
  function ($scope, $location, AuthService) {

    $scope.logout = function () {

      // call logout from service
      AuthService.logout()
        .then(function () {
          $location.path('/login');
        });

    };

}]);

//controller untuk registrasi---
angular.module('app').controller('registerController',  
  ['$scope', '$location', 'AuthService',
  function ($scope, $location, AuthService) {

    $scope.register = function () {

      // initial values
      $scope.error = false;
      $scope.disabled = true;

      // call register from service
      AuthService.register($scope.registerForm.username, $scope.registerForm.password)
        // handle success
        .then(function () {
          $location.path('/login');
          $scope.disabled = false;
          $scope.registerForm = {};
        })
        // handle error
        .catch(function () {
          $scope.error = true;
          $scope.errorMessage = "Ada kesalahan";
          $scope.disabled = false;
          $scope.registerForm = {};
        });

    };

}]);

Pada file ini terdapat tiga controller, yaitu untuk registrasi, login, dan logout.

8. Persistant Login
pada tahap ini jika user melakukan login lalu merefresh halaman, maka secara otomatis user akan logout secara otomatis. untuk mencegah hal ini, maka pada public/javascripts/controller.js pada akhir baris tambahkan :

app.run(function ($rootScope, $location, $route, AuthService) {  
  $rootScope.$on('$routeChangeStart',
    function (event, next, current) {
      AuthService.getUserStatus()
      .then(function(){
        if (next.access.restricted && !AuthService.isLoggedIn()){
          $location.path('/login');
          $route.reload();
        }
      });
  });
});

Sekarang coba jalankan aplikasi, registrasi dan ubah data guru.

Source code aplikasi artikel ini dapat dilihat di github erabelajar.

Geri Muhano

Web programmer di erabelajar.com. Alumni UIN Jakarta.