← Back to list

Fastify Restful API — Bagian 11: Menyimpan Refresh Token dengan Redis

Halo teman-teman, selamat datang lagi di seri Fastify Restful API! Di tutorial sebelumnya, Bagian 10: Autentikasi Aman dengan JWT (JSON…

Hadish · 2024-10-01 08:38 · 0 claps · 9.8 min read
#fastify #nodejs #redis #authentication #cookies
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Fastify Restful API — Bagian 11: Menyimpan Refresh Token dengan Redis

Halo teman-teman, selamat datang lagi di seri Fastify Restful API! Di tutorial sebelumnya, **Bagian 10: Autentikasi Aman dengan JWT (JSON Web Token), kita udah ngebahas gimana caranya bikin autentikasi pakai JWT, lengkap dengan access token dan refresh token di Fastify. Nah, sekarang kita bakal melanjutkan perjalanan ini dengan cara yang lebih keren: Menyimpan Refresh Token dengan Redis**.

Kenapa Redis? Redis itu cepat banget, dan cocok banget buat nyimpen data sementara kayak refresh token. Dengan Redis, kita bisa jaga performa aplikasi tetap ngebut sambil tetap menjaga keamanan data.

Oke, nggak usah lama-lama, yuk kita langsung mulai bahas gimana caranya Redis bisa bantu kita dalam nge-handle refresh token di aplikasi Fastify ini!

Pengenalan Redis dan Penggunaannya dalam Menyimpan Refresh Token

Sebelum lanjut ke teknis, kenalan dulu sama Redis. Redis itu database super cepat yang nyimpen data di RAM, bukan di hard drive. Karena kecepatan itulah, Redis sering dipakai buat hal-hal yang perlu akses cepat, kayak caching atau session data. Nah, di sini kita bakal pakai Redis buat nyimpen refresh token.

Kenapa Redis cocok?

Refresh token itu sifatnya sementara, dan butuh dicek cepat setiap kali user mau dapat access token baru. Redis cocok banget karena:

  • Cepat: Akses data di Redis bisa super cepat karena semua disimpan di memori.
  • Ada TTL (Time-to-Live): Kita bisa set refresh token biar otomatis hilang setelah expired.
  • Mudah dipakai: Redis ringan, gampang di-setup, dan bisa di-scale dengan mudah.

Intinya, Redis bantu kita buat nge-handle refresh token dengan aman dan efisien. Selanjutnya, yuk kita install Redis di proyek Fastify kita!

Instalasi Redis di Proyek Fastify

Sebelum kita mulai menggunakan Redis di proyek Fastify, langkah pertama adalah menginstall Redis di sistem kita. Di bawah ini adalah langkah-langkah instalasi untuk Windows, Linux, dan macOS.

Windows

Redis tidak secara resmi mendukung Windows, tapi kita bisa menggunakan Redis versi port dari Microsoft.

  • Download Redis versi Windows dari Redis GitHub Releases.
  • Pilih versi Redis yang stabil (misalnya Redis 3.2.100), lalu unduh file .zip.
  • Ekstrak file tersebut, lalu jalankan redis-server.exe untuk menjalankan Redis di sistem lokal.

Linux (Ubuntu/Debian)

Instalasi Redis di Linux cukup sederhana, karena Redis tersedia di banyak repository resmi.

Buka terminal, lalu jalankan perintah berikut:

sudo apt update
sudo apt install redis-server

Setelah instalasi selesai, jalankan Redis dengan perintah berikut:

sudo systemctl start redis

Pastikan Redis berjalan dengan baik:

sudo systemctl status redis

macOS

Buat pengguna macOS, kamu bisa install Redis lewat Homebrew:

Pertama, pastikan kamu sudah menginstall Homebrew. Jika belum, kamu bisa install dengan menjalankan perintah berikut:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Setelah Homebrew terinstall, jalankan perintah berikut untuk install Redis:

brew install redis

Jalankan Redis dengan perintah ini:

brew services start redis

Kamu bisa cek apakah Redis sudah jalan dengan command:

redis-cli ping

Jika output yang kamu dapatkan adalah PONG, itu berarti Redis sudah berjalan dengan baik!

Install RedisInsight (GUI Resmi dari Redis)

RedisInsight adalah GUI resmi yang disediakan oleh Redis Labs. Ini adalah salah satu tool terbaik untuk mengelola Redis dengan antarmuka yang user-friendly. Berikut langkah Instalasi RedisInsight

Windows

Download RedisInsight: Kunjungi situs resmi RedisInsight di https://redis.com/redis-enterprise/redis-insight/, dan pilih RedisInsight untuk Windows.

Jalankan Installer: Setelah selesai download, jalankan file installer .exe dan ikuti proses instalasinya.

Buka RedisInsight: Setelah instalasi selesai, buka RedisInsight, dan tambahkan instance Redis dengan memasukkan alamat localhost:6379 (jika Redis berjalan di komputer lokal).

macOS

Download RedisInsight untuk macOS: Kunjungi halaman RedisInsight di RedisInsight macOS, lalu pilih versi untuk macOS.

Install RedisInsight: Setelah file .dmg ter-download, buka dan drag RedisInsight ke folder Applications.

Buka RedisInsight: Setelah instalasi, buka aplikasi RedisInsight, lalu tambahkan instance Redis yang ingin kamu kelola dengan memasukkan alamat Redis, misalnya localhost:6379.

Linux

Download RedisInsight untuk Linux: Download RedisInsight versi Linux dari RedisInsight Linux. Pilih format yang sesuai dengan sistem kamu (misalnya .deb atau .rpm).

Install RedisInsight: Jika menggunakan Ubuntu/Debian, kamu bisa install dengan perintah berikut:

sudo dpkg -i redisinsight-<version>.deb

Untuk CentOS/Fedora/RHEL, gunakan perintah:

sudo rpm -i redisinsight-<version>.rpm

Jalankan RedisInsight: Setelah instalasi selesai, kamu bisa membuka RedisInsight melalui terminal atau dari menu aplikasi.

Menghubungkan Redis dengan Fastify

Sekarang kita siap menghubungkan Redis ke aplikasi Fastify kita. Supaya lebih praktis, kita pakai plugin @fastify/redis. Dengan plugin ini, nggak perlu repot setup Redis client manual, karena udah diurus sama Fastify.

Install Plugin Ketik perintah ini di terminal buat install plugin-nya:

npm install @fastify/redis

atau dengan yarn

yarn add @fastify/redis

Setup Koneksi Redis di .env Tambahkan konfigurasi di file .env.development atau .env.production:

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Tambahkan di Config Buat memastikan koneksi Redis diatur dengan benar, tambahkan setting di config.ts:

const schema = {
  required: [
    ...
+   "REDIS_HOST",
+   "REDIS_PORT",
  ],
  properties: {
    ...
+   REDIS_HOST: {
+     type: "string",
+     default: "127.0.0.1",
+   },
+   REDIS_PORT: {
+     type: "string",
+     default: 6379,
+   },
  }
}

Register Redis di index.ts Setelah itu, tambahkan konfigurasi Redis di file index.ts:

import fastifyRedis from '@fastify/redis';
import config from "./utils/config";

// Register Redis plugin
app.register(fastifyRedis, {
  host: '127.0.0.1',  // ganti ini kalau pakai Docker atau Redis di server lain
  port: 6379,
});

Dengan beberapa baris kode di atas, Redis udah terhubung ke Fastify. Sekarang, kita bisa cek apakah koneksi ini berjalan dengan baik atau tidak.

jangan lupa tambahakn type di index.d.ts:

declare module "fastify" {
  interface FastifyInstance  {
    ...
    redis: fastifyRedis.FastifyRedis
  }
} 

Di Fastify, kita bisa menggunakan event ready untuk ngecek koneksi. Jadi, kita tambahkan kode ini:

server.ready(async () => {
  try {
    const pong = await server.redis.ping();
    server.log.info('Redis connected! Ping response:'+ pong);
  } catch (err) {
    console.error('Redis connection failed:', err);
  }
});

Jika semuanya berjalan mulus, kamu bakal melihat pesan “Redis connected! Ping response: PONG” di console. Itu artinya Redis kita sudah siap digunakan!

Jadi, itu dia cara cepat dan mudah untuk menghubungkan Redis dengan Fastify. Sekarang, kita siap untuk mulai nyimpen dan ngambil refresh token, serta melakukan berbagai operasi lain dengan Redis.

Menyimpan Refresh Token di Redis saat User Login

Ketika user berhasil login, kita bakal generate refresh token dan simpan di Redis. Tujuannya? Biar refresh token tetap aman dan cuma dipakai kalau user mau minta access token baru.

Buat Function untuk Simpan Token

Kita bikin dulu function storeToken di file /src/services/authservice.ts yang bakal dipakai buat nyimpen refresh token ke Redis. Berikut kodenya:

 export const storeToken = async (
  fastify: FastifyInstance,
  refreshToken: string,
  accessToken: string,
  user: PublicUserType
) => {
  await fastify.redis.set(
    `refreshToken:${user.id}-${refreshToken}`,
    accessToken,
    "EX",
    to_number_of_seconds(config.REFRESH_TOKEN_LONG_DURATION)
  );
};

Function ini bakal menyimpan refresh token di Redis dengan key unik (kombinasi user ID dan refresh token) dan ngasih batas waktu (TTL) biar refresh token otomatis kadaluarsa.

Tambahkan storeToken di loginHandler

Sekarang, kita masukin function storeToken ke dalam proses login di file /src/controllers/authController.ts, supaya refresh token langsung disimpan di Redis begitu user login. Berikut caranya:

export async function loginHandler(
  request: FastifyRequest,
  reply: FastifyReply
) {
    ...
    const token = await generateToken(request, user, remember);
+   const duration = to_number_of_seconds(
+     remember
+       ? config.REFRESH_TOKEN_LONG_DURATION
+       : config.REFRESH_TOKEN_SHORT_DURATION
+   );
+   await storeToken(request.server,token.refreshToken,token.accessToken,jwtRequest.user);    
    reply
      .setCookie(config.REFRESH_TOKEN_COOKIE_NAME, token.refreshToken, {
        secure: true, // send cookie over HTTPS only
        httpOnly: true,
        sameSite: true, // alternative CSRF protection
-       maxAge: to_number_of_seconds(
-         remember
-           ? config.REFRESH_TOKEN_LONG_DURATION
-           : config.REFRESH_TOKEN_SHORT_DURATION
-       ),
+       maxAge: duration,
      })
      .code(200)
      .send({ message: "Login successful", user, token: token.accessToken });    
    ...
}
  • Generate Token: Pertama kita generate access dan refresh token.
  • Hitung Durasi: TTL (time-to-live) refresh token dihitung berdasarkan apakah user pilih “remember me” atau tidak.
  • Simpan di Redis: storeToken dipanggil buat nyimpen refresh token ke Redis dengan TTL yang udah kita hitung.
  • Simpan di Cookie: Refresh token juga disimpan di cookie dengan waktu expired yang sama

Tambahkan storeToken di refreshHandler

Di sini kita akan memasukkan fungsi storeToken ke dalam proses refresh token di file /src/controllers/authController.ts. Tujuannya adalah agar setiap kali user minta refresh token baru, kita langsung simpan refresh token tersebut di Redis.

export async function refreshAccessTokenHandler(
  request: FastifyRequest,
  reply: FastifyReply
) {
  try {
    ...
    const token = await generateToken(
      request,
      jwtRequest.user,
      jwtRequest.remember
    );
+   const duration = to_number_of_seconds(
+     jwtRequest.remember
+       ? config.REFRESH_TOKEN_LONG_DURATION
+       : config.REFRESH_TOKEN_SHORT_DURATION
+   );
+   await storeToken(
+     request.server,
+     token.refreshToken,
+     token.accessToken,
+     jwtRequest.user,
+     duration
+   );
    reply
      .setCookie(config.REFRESH_TOKEN_COOKIE_NAME, token.refreshToken, {
        secure: true, // send cookie over HTTPS only
        httpOnly: true,
        sameSite: true, // alternative CSRF protection
-       maxAge: to_number_of_seconds(
-         remember
-           ? config.REFRESH_TOKEN_LONG_DURATION
-           : config.REFRESH_TOKEN_SHORT_DURATION
-       ),
+       maxAge: duration,
      })
      .code(200)
      .send({ token: token.accessToken });
  } catch (error) {
    reply.status(401).send({ error: "Invalid refresh token" });
  }
}
  • Kita generate access token dan refresh token baru menggunakan data user yang sedang login, berdasarkan apakah user memilih opsi “remember me” atau tidak.
  • Kita hitung durasi (TTL) refresh token berdasarkan apakah user memilih untuk login dalam jangka waktu panjang (remember) atau hanya sementara.
  • Setelah token baru berhasil dibuat, kita simpan refresh token ke Redis dengan menggunakan fungsi storeToken. Ini memastikan refresh token tersimpan aman di Redis dan siap digunakan nanti kalau user butuh akses token baru.
  • Kita set refresh token ke dalam cookie HTTP-Only supaya lebih aman dan nggak bisa diakses oleh JavaScript. Kita juga sesuaikan durasi cookie dengan durasi refresh token yang sudah dihitung sebelumnya.

Mengecek Token di Redis

Di kode ini, kita bikin function authenticate buat ngecek apakah access token dan refresh token yang dikirim user valid atau nggak. Ini penting biar kita tahu kalau user yang lagi akses aplikasi benar-benar punya access token yang sesuai dengan yang kita simpan di Redis waktu mereka login.

Nah, untuk implementasinya, kita akan mengubah sedikit plugin yang udah kita buat di /src/plugins/jwt.ts. Jadi, fungsi authenticate di sini bakal ngecek validitas token dengan langkah-langkah berikut:

...
function jwtPlugin(
  fastify: FastifyInstance,
  _options: FastifyPluginOptions,
  done: any
) {
  ...
    fastify.decorate(
    "authenticate",
    async (request: FastifyRequest, reply: FastifyReply) => {
      try {
        const jwtRequest = await request.jwtVerify<{ user: PublicUserType }>({
          onlyCookie: true,
        });
        const jwtAccess = await request.jwtVerify<{ user: PublicUserType }>();
        if (jwtAccess.user.id === jwtRequest.user.id) {
          const refreshToken = request.server.jwt.lookupToken(request, {
            onlyCookie: true,
          });
          const accessToken = request.server.jwt.lookupToken(request);
          const accessTokenRedis = await fastify.redis.get(
            `refreshToken:${jwtAccess.user.id}-${refreshToken}`
          );
          if (accessToken === accessTokenRedis) {
            request.server.user = jwtAccess.user;
          }else{
            throw new Error("Your token has been expired");

          }
        } else {
          throw new Error("Access token and request token didnt match");
        }
      } catch (err) {
        reply.send(err);
      }
    }
  );
  ...
}

Ambil Token dari Redis:

  • Di sini kita ambil refresh token dari cookie dan access token dari request user.
  • Setelah itu, kita cek Redis buat ngambil access token yang sudah kita simpan saat user login sebelumnya.
  • Redis menyimpan token dengan format kunci refreshToken:{user.id}-{refreshToken}, jadi kita pakai kombinasi user ID dan refresh token buat mendapatkan access token yang sesuai dari Redis.

Cocokkan Access Token:

  • Setelah dapat access token dari Redis, kita bandingkan dengan access token yang dikirim user.
  • Kalau access token dari Redis cocok dengan yang dikirim oleh user, berarti token masih valid dan aman. User bisa lanjut menggunakan aplikasi.
  • Tapi kalau access token tidak cocok (misalnya karena token di Redis sudah kadaluarsa atau dihapus), kita kasih pesan error “Your token has been expired” yang berarti token sudah habis masa berlakunya.

Membuat Endpoint Logout

Untuk bikin user benar-benar keluar dari aplikasi dan hapus semua sesi yang aktif, kita bisa buat endpoint logout. Di sini, kita akan hapus refresh token dari Redis dan juga clear cookie refresh token dari browser user. Berikut langkah-langkahnya:

Buat logoutHandler

Di file /src/controllers/authController.ts, kita tambahin function logoutHandler yang bakal menangani proses logout.

export async function logoutHandler(
  request: FastifyRequest,
  reply: FastifyReply
) {
  try {
    // Mengambil refresh token dari cookie
    const refreshToken = request.server.jwt.lookupToken(request, {
      onlyCookie: true,
    });

    // Ambil user dari request (diasumsikan user sudah terautentikasi)
    const user = request.server.user;

    // Hapus refresh token dari Redis
    await request.server.redis.del(`refreshToken:${user.id}-${refreshToken}`);

    // Hapus cookie refresh token
    reply.clearCookie(config.REFRESH_TOKEN_COOKIE_NAME, {
      secure: true, // sesuai konfigurasi cookie sebelumnya
      httpOnly: true,
      sameSite: true,
    });

    reply.code(200).send({ message: "Logout successful" });
  } catch (error) {
    reply.status(500).send({ error: "Failed to logout" });
  }
}

Ambil refresh token dari cookie:

  • Pada login, refresh token disimpan di dalam cookie. Di sini kita ambil kembali token itu dari request.cookies menggunakan nama cookie yang tersimpan di config.REFRESH_TOKEN_COOKIE_NAME.
  • Jika tidak ada token, berarti user tidak sedang login atau cookie sudah dihapus, maka error “No refresh token provided” akan muncul.

Ambil user yang sudah terautentikasi:

  • Dengan asumsi middleware authenticate sudah dijalankan sebelumnya, objek request.server.user akan berisi informasi user yang sedang login. Kita gunakan ini untuk mencocokkan token yang ingin dihapus di Redis.

Hapus refresh token dari Redis:

  • Redis menyimpan refresh token menggunakan format kunci seperti refreshToken:userId-refreshToken. Kunci ini digunakan untuk menyimpan refresh token yang sesuai dengan user tersebut.
  • Perintah await request.server.redis.del() akan menghapus refresh token tersebut dari Redis, sehingga token tidak bisa digunakan lagi.

Hapus cookie refresh token dari browser:

  • Setelah refresh token di Redis dihapus, kita juga perlu menghapus cookie dari browser. Ini dilakukan menggunakan reply.clearCookie().
  • Opsi seperti secure, httpOnly, dan sameSite harus disesuaikan dengan konfigurasi yang sama seperti ketika kita menyimpan cookie saat login, untuk memastikan konsistensi.

Kirim respon sukses:

  • Jika semua proses berjalan lancar, kita kirimkan respon sukses ke client dengan pesan “Logout successful”.
  • Jika ada error dalam proses logout (misalnya Redis gagal diakses), kita kirimkan status 500 dan pesan error.

Daftarin route logout

Supaya endpoint logout ini bisa diakses, kita daftarin route-nya di /src/routes/authRoutes.ts.

export async function authRoutes(server: FastifyInstance) {
  ...
  server.post("/logout", {
    preHandler: [server.authenticate],
    handler: logoutHandler,
  });
}

Ambil Refresh Token: Di langkah pertama, kita ambil refresh token dari cookie yang disimpan di browser user. Ini token yang nanti bakal dihapus.

Hapus Token dari Redis: Setelah refresh token diambil, kita hapus token tersebut dari Redis, supaya nggak bisa digunakan lagi buat minta access token baru.

Clear Cookie: Refresh token yang ada di browser user juga dihapus, biar user benar-benar keluar dari sesi login mereka.

Respons Logout Berhasil: Kalau semuanya sukses, kita kirim pesan Logout successful ke user.

Dengan begitu, proses logout selesai, dan semua sesi yang terkait dengan user tersebut (token di Redis dan cookie di browser) sudah dihapus.

Terima kasih

Terima kasih telah mengikuti tutorial ini hingga selesai! 🎉 Dalam tutorial ini, kita telah membahas berbagai aspek penting seperti menghubungkan Redis dengan Fastify, menyimpan refresh token di Redis saat user login, memvalidasi token yang tersimpan, hingga membuat endpoint logout yang berfungsi untuk menghapus token dari Redis dan mengelola cookie. Semua langkah ini sangat berguna untuk memastikan keamanan autentikasi pada aplikasi.

Jika Anda ingin melihat keseluruhan source code dari tutorial ini, Anda bisa mengaksesnya melalui link berikut:

👉 Source Code di GitHub

Jangan lupa untuk mengikuti tutorial selanjutnya yang akan membahas topik penting lainnya: Testing di Fastify. Ini akan membantu Anda memastikan bahwa aplikasi berjalan dengan benar dan aman. Sampai jumpa di tutorial berikutnya! 🚀

Tutorial Selanjutnya — Multilanguage di Fastify

Dalam tutorial berikutnya Fastify Restful API — Bagian 12: Buat API Fastify Multilingual dalam Sekejap! , kita akan belajar bagaimana menambahkan dukungan multi-language pada API di Fastify. Berikut ini poin-poin yang akan kita bahas:

  1. Mengapa Multi-Language Penting? Mengupas pentingnya mendukung berbagai bahasa dalam aplikasi untuk meningkatkan pengalaman pengguna dan memperluas jangkauan.
  2. Menggunakan Plugin i18next-http-middleware Memanfaatkan plugin i18next-http-middleware untuk menangani penerjemahan dan manajemen bahasa di API Fastify.
  3. Menyesuaikan Respons API Berdasarkan Bahasa Membuat respons API dinamis yang berubah sesuai dengan bahasa yang dipilih oleh pengguna.
  4. Menerjemahkan Pesan Error Validasi dengan ajv-i18n Cara menggunakan ajv-i18n untuk menerjemahkan pesan error validasi otomatis sesuai bahasa yang diinginkan.

Dengan menambahkan dukungan multi-language pada API, kita tidak hanya meningkatkan aksesibilitas dan inklusivitas aplikasi, tetapi juga memperkaya pengalaman pengguna. Jangan lewatkan tutorial ini!


메타데이터
post_id
03b671a35cf7
slug
fastify-restful-api-bagian-11-menyimpan-refresh-token-dengan-redis-03b671a35cf7
url
https://medium.com/@hadish19/fastify-restful-api-bagian-11-menyimpan-refresh-token-dengan-redis-03b671a35cf7
canonical_url
https://medium.com/@hadish19/fastify-restful-api-bagian-11-menyimpan-refresh-token-dengan-redis-03b671a35cf7
author_url
https://medium.com/@hadish19
status
ok
fetched_at
2026-07-22 16:23:41