KembaliBelajar Full-stack Dengan React Router v7
Pembahasan

Autentikasi

Enaknya di React Router, dia udah nyediain session storage yang bisa lo pake buat bikin sistem autentikasi. Jadi, lo nggak perlu ribet-ribet mikirin solusi sendiri. Ini cukup kalo lo pengen kontrol penuh atas sistem autentikasi lo.

Lo bisa pake createSessionStorage dari React Router buat bikin session storage. Contohnya:

app/libs/session.ts
import { createCookieSessionStorage } from "react-router";

type SessionData = {
  userId: number;
};

type FlashData = {
  message: string;
};

export const { getSession, commitSession, destroySession } =
  createCookieSessionStorage<SessionData, FlashData>({
    cookie: {
      // ini yang wajib
      name: "session-gue",

      // sisanya ini opsional
      httpOnly: true,
      path: "/",
      sameSite: "lax",
      secure: process.env.NODE_ENV === "production",
    },
  });

Session di atas lo bisa pake buat nyimpen data user yang lagi login, misalnya userId (dalam contoh ini) atau data flash (pesan sekali tampil) buat ngasih feedback ke user setelah mereka melakukan aksi tertentu.

Kalo lo liat lagi, di atas ada tiga fungsi yang di-export:

  • getSession: buat ambil session dari cookie.
  • commitSession: buat simpen session ke cookie.
  • destroySession: buat hapus session dari cookie.

Sistem session di React Router ini nggak di-spread lewat middleware, jadi lo harus panggil fungsi getSession setiap kali lo butuh akses session di dalam route module lo.

Sebagai contoh, bikin halaman login:

app/routes/login.tsx
import { data, redirect } from "react-router";
import type { Route } from "./+types/login";
import { db } from "~/libs/db";
import { getSession, commitSession } from "~/libs/session";

export async function loader({
  request,
}: Route.LoaderArgs) {
  // pass cookie dari request ke getSession buat ngambil session
  const session = await getSession(
    request.headers.get("Cookie")
  );

  // ngecek apakah user udah login
  // kalo udah, redirect ke halaman utama
  if (session.has("userId")) {
    return redirect("/");
  }

  return data(
    // ngambil flash data `error` dari session
    { error: session.get("error") },
    {
      headers: {
        // simpen session ke cookie
        "Set-Cookie": await commitSession(session),
      },
    }
  );
}

export async function action({
  request,
}: Route.ActionArgs) {
  const session = await getSession(
    request.headers.get("Cookie")
  );
  const form = await request.formData();
  const username = form.get("username");
  const password = form.get("password");

  const userId = await db.login(
    username,
    password
  );

  // kalo login gagal
  if (userId == null) {
    // set flash message ke session
    session.flash("error", "Invalid username/password");

    // redirect ke halaman login dengan session yang udah di-update
    return redirect("/login", {
      headers: {
        "Set-Cookie": await commitSession(session),
      },
    });
  }

  // kalo login sukses, simpen userId ke session
  session.set("userId", userId);

  // redirect ke halaman utama
  return redirect("/", {
    headers: {
      // simpen session ke cookie
      "Set-Cookie": await commitSession(session),
    },
  });
}

export default function Login({
  loaderData,
}: Route.ComponentProps) {
  const { error } = loaderData;

  return (
    <div>
      {error ? <div className="error">{error}</div> : null}
      <form method="POST">
        <h1>Please sign in</h1>
        <label>
          Username: <input type="text" name="username" />
        </label>
        <label>
          Password:
          <input type="password" name="password" />
        </label>
        <button type="submit">Sign in</button>
      </form>
    </div>
  );
}

Penjelasannya gini:

  • Di loader, kita ngecek apakah user udah login dengan ngecek apakah session punya userId:
    • Kalo udah, kita redirect ke halaman utama.
    • Kalo belum, kita ambil data flash error dari session dan return ke component buat ditampilin.
  • Ketika user submit form, action bakal dipanggil dan ini yang terjadi:
    • Di sini, kita ambil session dari cookie dan ngecek apakah username dan password yang dimasukin valid.
    • Kalo valid, simpen userId ke session dan redirect ke halaman utama.
    • Kalo nggak valid, set flash message error ke session dan redirect kembali ke halaman login.

Hal yang perlu diinget, ketika lo nge-set sesuatu di session, lo harus nge-commit session tersebut dengan commitSession(session) supaya perubahan yang lo buat ke session bisa disimpen ke cookie.

Dan kalo lo mau ngehapus session, lo bisa pake destroySession(session):

app/routes/logout.tsx
import { redirect, useFetcher } from "react-router";
import type { Route } from "./+types/logout";
import { getSession, destroySession } from "~/libs/session";

export async function action({
  request,
}: Route.ActionArgs) {
  const session = await getSession(
    request.headers.get("Cookie")
  );

  // hapus session
  return redirect("/", {
    headers: {
      "Set-Cookie": await destroySession(session),
    },
  });
}

export default function Logout() {
  const fetcher = useFetcher();

  return (
    <fetcher.Form method="POST">
      <p>Mau logout?</p>
      <button type="submit">Logout</button>
    </fetcher.Form>
  );
}

Cara ini cukup buat bikin sistem autentikasi yang simpel. Kalo lo butuh solusi yang lebih kompleks, lo bisa pake library kayak better-auth atau OpenAUTH.

Progres kursus

Sudah paham pelajaran ini?

Tandai selesai untuk menyimpan progresmu di kursus ini.

Diskusi

0 komentar

Belum ada komentar.

Mulai diskusi