Otentikasi API Laravel-Vue.js Menggunakan Laravel Passport

whynwd

whynwd Selasa, 08 Desember 2020

Otentikasi API Laravel-Vue.js Menggunakan Laravel Passport

Perlu memang memberikan keamanan yang ekstra untuk API kita. Seperti jaminan keamanan ketika pengguna harus melewati autentikasi saat masuk ke aplikasi.

Ketika aplikasi memerlukan login pengguna, salah satu cara pengamanan yang bisa dan biasa dilakukan adalah dengan menggunakan token untuk mengautentikasi user.

Disini Laravel passport yang menyediakan implementasi dari server 0Auth2 bisa kita gunakan untuk mengauentikasi user aplikasi kita.

Cara Penggunaan

Contoh serta penggunaannya seperti apa, disini kita akan terapkan dalam otentikasi user login dan registrasi laravel-vue.js yang akan kita buat pada tutorial ini.

Persyaratan

Melihat bagaimana tutorial ini dibuat, yang mungkin tidak terlalu memberikan penjelasan mendetail tentang bagaimana suatu fungsi bisa bekerja, diharapkan telah memahami Framework atau library berikut.

  • Laravel
  • Vue.js
  • Bootstrap
  • Vue-Router

Membuat Autentikasi user Laravel Passport

Kita akan mulai membuat autentikasi login dan registrasi user dengan laravel dan vue.js dan menggunakan laravel passport untuk membuat token yang akan digunakan dalam otentikasi dan otorisasi pengguna.

Install Laravel

Mari kita mulai dan kita awali dengan melakukan instalasi laravel.

laravel new lavuepassport
#atau
composer global require laravel/installer

Atur Database

Kemudian silahkan buat database mysql baru dan hubungkan dengan project.

//.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db_lavuepassport
DB_USERNAME=root
DB_PASSWORD=

Install Passport

Selanjutnya kita install laravel passport dan membuat tabel database nya.

composer require laravel/passport

Setelah instalasi laravel/passport selesai, kita jalankan perintah migrasi untuk membuat tabel.

php artisan migrate

Tabel yang terbuat:

MariaDB [db_lavuepassport]> show tables;
+-------------------------------+
| Tables_in_db_lavuepassport    |
+-------------------------------+
| failed_jobs                   |
| migrations                    |
| oauth_access_tokens           |
| oauth_auth_codes              |
| oauth_clients                 |
| oauth_personal_access_clients |
| oauth_refresh_tokens          |
| password_resets               |
| users                         |
+-------------------------------+
9 rows in set (0.001 sec)

Setelah itu jalankan perintah dibawah ini untuk generate keys.

php artisan passport:install

Selanjutnya kita setting Passport di project. Silahkan buka model User.php, dan tambahkan trait HasApiTokens seperti dibawah ini.

<?php

namespace App\Models;

...

use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens;

    ...
}

Kemudian buka AuthServiceProvider.php dan tambahkan Passport::routes seperti dibawah ini.

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
       'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

Setelah itu silahkan buka config/auth.php dan atur guards api driver menjadi 'passport' seperti dibawah ini.

'guards' => [
  'web' => [
      'driver' => 'session',
      'provider' => 'users',
  ],
  
  'api' => [
     'driver' => 'passport',
     'provider' => 'users',
     'hash' => false,
  ],
],

Install Vue

Setelah selesai dengan passport, kita lanjutnya dengan menginstall vue.js dan mengatur vue instance pada project.

npm install

npm install vue vue-template-compiler --save-dev 

Setelah selesai diinstall, kita import modul vue dan membuat instance di app.js.

//resources/js/app.js
window.Vue = require('vue');

import App from './components/App'


const app = new Vue({
  el: '#app', 
  components: { App }
});

Silahkan buat folder baru dengan nama components di direktori resources/js dan tambahkan komponen App.vue.

//resources/js/components/App.vue
<template>
  <div>
    <h1>Hello, World!</h1>
  </div>
</template>

Kemudian buka welcome.blade.php dan ubah dengan html di bawah ini.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{csrf_token()}}">
    <title>Laravel</title>
    <link href=" {{ mix('css/app.css') }}" rel="stylesheet">
  </head>
  <body>
    <div id="app">
      <app></app>
    </div>
    <script src="{{ mix('js/app.js') }}"></script>
  </body>
</html>

Lalu route yang ada pada routes/web.php silahkan ubah seperti dibawah ini.

Route::get('/{any}', function(){
    return view('welcome');
})->where('any', '.*');

Lihat juga: Cara Instal Vue.js di Laravel 8

Install Bootstrap

Kita lanjutkan dengan menginstall bootstrap dan mengaturnya pada project.

npm install bootstrap jquery popper.js sass sass-loader --save-dev

Silahkan buat folder baru pada direktori resources dengan nama sass dan tambahkan file baru dengan nama app.scss, dan kita import bootstrap.

//resources/sass/app.scss
@import '~bootstrap/scss/bootstrap';

Lalu buka bootstrap.js dan tambahkan dibawah ini.

//resources/js/bootstrap.js
try {
  window.Popper = require('popper.js').default;
  window.$ = window.jQuery = require('jquery');

  require('bootstrap');
} catch (e) {}

Kemudian buka webpack.mix.js dan buat seperti dibawah ini untuk dilakukan compiling css dan js.

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css')
    .sourceMaps();

Terakhir jalankan npm run dev untuk compiling, dan setelah selesai mari kita lihat pada browser.

npm run dev

php artisan serve
Otentikasi API Laravel-Vue.js Menggunakan Laravel Passport

Lihat juga: Cara Instal Bootstrap di Laravel 8

Membuat Requests

Kita akan lanjutkan dengan membuat permintaan api untuk login, register, dan data user.

Mari kita buat sebuah kontroller dengan nama UserController, dan buat seperti dibawah.

php artisan make:controller UserController
//UserController.php
<?php

namespace App\Http\Controllers;

use Auth;
use App\Models\User;
use Validator;
use Illuminate\Http\Request;

class UserController extends Controller
{
     
    public function login(Request $request)
    {  
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            $status = 200;
            $response = [
                'user' => Auth::user(),
                'token' => Auth::user()->createToken('userToken')->accessToken,
            ];
        }else{
            $status = 401;
            $response = ['error' => 'The email or password is incorrect.'];
        }

        return response()->json($response, $status);
    }

    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|max:25',
            'email' => 'required|unique:users|email',
            'password' => 'required|min:8',
            'password_confirmation' => 'required|same:password',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => $validator->errors()], 401);
        } 

        $input = $request->all();
        $input['password'] = bcrypt($input['password']);
        
        $user = User::create($input);
 
        $response = [
            'user' => $user,
            'token' => $user->createToken('userToken')->accessToken,
        ];
        
        return response()->json($response, 200);
    } 

    public function show(User $user)
    {
        return response()->json($user,200);
    }

}

Kemudian buat route di routes/api.php seperti dibawah ini.

//api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;


Route::post('login', [UserController::class, 'login']);
Route::post('register', [UserController::class, 'register']);

Route::group(['middleware' => 'auth:api'], function(){
Route::get('users/{user}', [UserController::class, 'show']);
});

Install Vue-Router

Mari kita install vue router untuk navigasi komponen.

npm install vue-router

Kemudian silahkan buat komponen baru dengan nama Home.vue, Login.vue, Register.vue, dan Profile.vue pada folder components, dan buat seperti dibawah.

//Register.vue
<template>
  <div class="container ">
    <div class="row justify-content-center ">
      <div class="col-md-6">
        <div class="card card-default">
          <div class="card-header">Register</div>
          <div v-if="errors" class="alert bg-danger text-white m-4">
            <div v-for="(v, k) in errors" :key="k">
              <p v-for="error in v" :key="error" class="text-sm">
                {{ error }}
              </p>
            </div>
          </div>
          <div class="card-body">
            <form @submit.prevent="submit">
              <div class="form-group">
                <label for="name">Name</label>
                <input type="text" v-model="user.name" class="form-control">
              </div>
              <div class="form-group">
                <label for="email">Email</label>
                <input type="email" v-model="user.email" class="form-control">
              </div>
              <div class="form-group">
                <label for="password">Password</label>
                <input type="password" v-model="user.password" class="form-control">
              </div>
              <div class="form-group">
                <label for="password_confirmation">Confirm Password</label>
                <input type="password" v-model="user.password_confirmation" class="form-control">
              </div>
              <button type="submit" class="btn btn-primary" >
              Register
              </button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default { 
      data(){
          return {
              user:{
                  name : "",
                  email : "",
                  password : "",
                  password_confirmation : "",
              }, 
              errors: null,
          }
      },
      methods : {
          submit() { 
                 axios.post('api/register', this.user)
                    .then(response => {
                      let data = response.data; 
                      localStorage.setItem('user',JSON.stringify(data.user))
                      localStorage.setItem('jwt', data.token)
                      
                      this.$router.push({ name: 'user', params: { userId: data.user.id } });
                    })
                    .catch(error => {
                      this.errors = error.response.data.errors;
                    });
          }
      }
  }
</script>
//Login.vue
<template>
  <div class="container ">
    <div class="row justify-content-center">
      <div class="col-md-6">
        <div class="card card-default">
          <div class="card-header">Login</div>
          <div v-if="error">
            <div class="alert bg-danger text-white m-4">
              {{ error }}
            </div>
          </div>
          <div class="card-body">
            <form @submit.prevent="submit">
              <div class="form-group">
                <label for="email">Email</label>
                <input type="email" v-model="email" class="form-control" required>
              </div>
              <div class="form-group">
                <label for="password">Password</label>
                <input type="password"  v-model="password" class="form-control" required>
              </div>
              <button type="submit" class="btn btn-primary">
              Login
              </button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
      data() {
          return {
              email: "",
              password: "",
              error: "",
          }
      },
      methods: {
          submit() { 
              axios.post('api/login', {
                  email: this.email,
                  password: this.password
                })
                .then(response => { 
                  let data = response.data; 
                  localStorage.setItem('user',JSON.stringify(data.user))
                  localStorage.setItem('jwt',data.token)
                  this.$emit('setData') 
                   
                  this.$router.push({ name: 'user', params: { userId: data.user.id } });
                })
               .catch(error => { 
                  this.error = error.response.data.error;
                });
          }
               
      }
  }
</script>
//Profile.vue
<template>
  <div>
    <div class="container">
      <div class="card">
        <h5 class="card-header"></h5>
        <div class="card-body">
          <h1 class="card-title">{{user.name}}</h1>
          <p class="card-text">{{user.email}}</p>
          <a href="#" class="btn btn-primary">Follow</a>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
      data(){
          return {
              user : '', 
          }
      },
      beforeMount(){ 
          axios.defaults.headers.common['Content-Type'] = 'application/json'
          axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('jwt')
  
          axios.get(`api/users/${this.$route.params.userId}`)
          .then(response => { 
              this.user = response.data 
          })
          .catch(error => {
              console.error(error);
          }) 
      }
  }
</script>
//Home.vue
<template>
  <div>
    <div class="container">
      <div class="jumbotron">
        <h1 class="display-4">Hello, world!</h1>
        <p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
        <hr class="my-4">
        <p>It uses utility classes for typography and spacing to space content out within the larger container.</p>
        <p class="lead">
          <a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>
        </p>
      </div>
    </div>
  </div>
</template>
//App.vue
<template>
  <div>
    <nav class="navbar navbar-expand-lg navbar-light bg-info  shadow-sm">
      <router-link :to="{name: 'home' }" class="navbar-brand">Navbar</router-link>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav ml-4">
          <li class="nav-item active">
            <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Link</a>
          </li>
        </ul>
        <form class="form-inline my-2 my-lg-0 ml-4">
          <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
          <button class="btn btn-outline-dark text-dark my-2 my-sm-0" type="submit">Search</button>
        </form>
        <ul class="navbar-nav ml-auto">
          <template v-if="!isLoggedIn">
            <li class="nav-item">
              <router-link :to="{ name: 'login' }" class="nav-link" >Login</router-link>
            </li>
            <li class="nav-item">
              <router-link :to="{ name: 'register' }" class="nav-link">Register</router-link>
            </li>
          </template>
          <template v-if="isLoggedIn">
            <li class="nav-item">
              <router-link :to="{name: 'user', params: { userId: user.id}}" class="nav-link">{{user.name}}</router-link>
            </li>
            <li class="nav-item">
              <a class="nav-link"  @click="logout"> Logout</a>
            </li>
          </template>
        </ul>
      </div>
    </nav>
    <main class="pt-4">
      <router-view @setData="getData"></router-view>
    </main>
  </div>
</template>
<script>
  export default {
    data(){
        return { 
         user: null,
         isLoggedIn: null, 
        }
    },
    beforeMount() {
      this.getData()   
    }, 
    methods : {  
      getData(){  
          this.isLoggedIn = localStorage.getItem('jwt') !== null; 
          this.user = JSON.parse(localStorage.getItem('user')) 
      },
      logout(){
          localStorage.removeItem('jwt')
          localStorage.removeItem('user')
          this.getData()  
          this.$router.push('/')
      }
    }
  }
</script>

Selanjutnya kita buat route vue router di app.js seperti dibawah ini.

//app.js
require('./bootstrap');

window.Vue = require('vue');

import VueRouter from 'vue-router'

Vue.use(VueRouter)

import App from './components/App'
import Home from './components/Home'
import Login from './components/Login'
import Register from './components/Register'
import Profile from './components/Profile'

const router = new VueRouter({
  mode: 'history',
  routes: [
      {
          path: '/',
          name: 'home',
          component: Home
      },
      {
          path: '/login',
          name: 'login',
          component: Login,
          meta: { guest: true } 
      },
      {
          path: '/register',
          name: 'register',
          component: Register,
          meta: { guest: true } 
      }, 
      {
        path: '/:userId',
        name: 'user',
        component: Profile,
        props: true,
        meta: { requiresAuth: true }
    }, 
  ],
})

router.beforeEach((to, from, next) => {  
  const token = localStorage.getItem('jwt') == null;  
  if (to.matched.some(record => record.meta.guest)) {
    if (!token) next({ name: 'home' })
    else next()
  } 
  if (to.matched.some(record => record.meta.requiresAuth)) { 
    if (token) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    } 
 }else{
  next()  
}
})

const app = new Vue({
  el: '#app',
  components: { App },
  router,
});

Sampai disini kembali kita jalankan npm run dev dan silahkan mecobanya untuk melakukan registrasi ataupun login.

Penutup

Kita telah membuat autentikasi user login dan registrasi dengan laravel-vue.js dan menggunakan laravel passport untuk membuat token user yang digunakan untuk otirisasi maupun otentikasi.

Ketika user melalukan login ataupun registrasi, kita atur token pengguna pada localstorage untuk proses otentikasi dan pada header untuk proses otoriasi.

Silahkan dibuat dan dikembangkan, semoga bermanfaat.

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel