Search by

    Terakhir diperbaharui: Jan 1, 2021

    Implementasi sistem autentikasi - Proteksi API

    Setelah membatasi akses untuk setiap halaman kita akan lanjutkan dengan membatasi setiap API endpoint yang dimiliki.

    Hal ini dilakukan untuk menghindari pihak yang tidak bertanggung jawab menggunakan API endpoint yang bersifat terbuka atau publik.

    Pembatasan ini dilakukan dengan cara untuk setiap request dari client yang dikirimkan ke API harus disertai dengan token sebagai bukti bahwa user yang mengirimkan request adalah user yang sudah memiliki akun atau sudah registrasi di aplikasi. Selain itu maka ditolak.

    Step by step

    Konfigurasi strategi JWT

    Langkah ini dilakukan pada project dinotes-api dan bukan dinotes-client

    Install package atau modul passport-jwt.

    1yarn add passport-jwt

    Tambahkan strategi untuk menangani autentikasi berbasis JWT:

    utils/auth.js

    1...
    2const passportJWT = require('passport-jwt');
    3const { ObjectId } = require('mongodb');
    4
    5const JWTStrategy = passportJWT.Strategy;
    6const ExtractJWT = passportJWT.ExtractJwt;
    7...
    8passport.use(
    9 new JWTStrategy(
    10 {
    11 jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
    12 secretOrKey: 'mys3cret'
    13 },
    14 async (payload, done) => {
    15 try {
    16 const db = getDb();
    17 const user = await db.collection('users').findOne({ _id: ObjectId(payload._id) });
    18
    19 if (!user) {
    20 return done(null, false);
    21 }
    22
    23 return done(null, user);
    24 } catch (error) {
    25 return done(error);
    26 }
    27 }
    28 )
    29);

    Yang perlu diperhatikan disini adalah secret key yang digunakan untuk membongkar token haruslah sama dengan yang digunakan untuk membuat token. Dalam hal ini adalah string 'mys3cret'.

    Proteksi API endpoint

    Update routes.js dengan menambahkan passport sebagai middleware.

    routes.js

    1const express = require('express');
    2const passport = require('passport');
    3
    4const router = express.Router();
    5
    6const { addNote, getAllNotes, getNote, updateNote, deleteNote, register, login } = require('./handler');
    7
    8require('./utils/auth');
    9
    10// notes
    11router.post('/note', passport.authenticate('jwt', { session: false }), addNote);
    12
    13router.get('/notes', passport.authenticate('jwt', { session: false }), getAllNotes);
    14
    15router.get('/note/:id', passport.authenticate('jwt', { session: false }), getNote);
    16
    17router.put('/note/:id', passport.authenticate('jwt', { session: false }), updateNote);
    18
    19router.delete('/note/:id', passport.authenticate('jwt', { session: false }), deleteNote);
    20
    21// user
    22router.post('/register', register);
    23
    24router.post('/login', login);
    25
    26module.exports = router;

    Setelah API endpoint diproteksi maka jika kita mengirim request tanpa token kita akan mendapat error message Unauthorized.

    api unauthorized

    Menambah bearer token

    Langkah terakhir adalah menambahkan token yang didapat dari localStorage ke setiap request yang dikirim ke API.

    Token dikirim sebagai OAuth Bearer Token.

    dinotes-client/features/notes/notesSlice.js

    1...
    2const user = JSON.parse(localStorage.getItem('user'));
    3
    4const initialState = {
    5 data: [],
    6 status: 'idle',
    7 error: null
    8};
    9
    10export const fetchNotes = createAsyncThunk('notes/fetchNotes', async () => {
    11 const requestOptions = {
    12 method: 'GET',
    13 headers: {
    14 Authorization: `Bearer ${user.token}`,
    15 'Content-Type': 'application/json'
    16 }
    17 };
    18
    19 const response = await fetch(`${process.env.REACT_APP_API_URL}/notes`, requestOptions);
    20 if (response.ok) {
    21 const data = await response.json();
    22 return data;
    23 } else {
    24 throw Error(response.statusText);
    25 }
    26});
    27
    28export const addNewNote = createAsyncThunk('notes/AddNewNote', async (initialNotes) => {
    29 const requestOptions = {
    30 method: 'POST',
    31 headers: {
    32 Authorization: `Bearer ${user.token}`,
    33 'Content-Type': 'application/json'
    34 },
    35 body: JSON.stringify(initialNotes)
    36 };
    37
    38 const response = await fetch(`${process.env.REACT_APP_API_URL}/note`, requestOptions);
    39 if (response.ok) {
    40 const data = await response.json();
    41 const noteAdded = { ...initialNotes, _id: data._id };
    42 return noteAdded;
    43 } else {
    44 throw Error(response.statusText);
    45 }
    46});
    47
    48export const updateExistingNote = createAsyncThunk('notes/updateNote', async (currentNote) => {
    49 const requestOptions = {
    50 method: 'PUT',
    51 headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' },
    52 body: JSON.stringify(currentNote)
    53 };
    54
    55 const response = await fetch(`${process.env.REACT_APP_API_URL}/note/${currentNote._id}`, requestOptions);
    56 if (response.ok) {
    57 return currentNote;
    58 } else {
    59 throw Error(response.statusText);
    60 }
    61});
    62
    63export const deleteNote = createAsyncThunk('notes/deleteNote', async (currentNote) => {
    64 const requestOptions = {
    65 method: 'DELETE',
    66 headers: { Authorization: `Bearer ${user.token}`, 'Content-Type': 'application/json' }
    67 };
    68
    69 const response = await fetch(`${process.env.REACT_APP_API_URL}/note/${currentNote._id}`, requestOptions);
    70 if (response.ok) {
    71 return currentNote;
    72 }
    73});
    74...