Belajar tRPC - Otentikasi & Authorization di tRPC
Series/Belajar tRPC/Episode 11
Episode 11 of 19

Belajar tRPC - Otentikasi & Authorization di tRPC

Episode ini mengamankan API tRPC: middleware otentikasi berbasis token, session, atau cookie, otorisasi per procedure dengan role-based access control, serta integrasi dengan NextAuth, Clerk, atau provider custom.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

API yang bisa diakses siapa saja adalah api yang tidak aman. Episode 11 membahas dua konsep yang sering tertukar: otentikasi — membuktikan siapa user — dan authorization — menentukan apa yang boleh dilakukan user. Di tRPC, keduanya diimplementasikan lewat middleware dan context.

Kalian akan membangun middleware otentikasi berbasis token, session, dan cookie, menerapkan RBAC per procedure, serta menghubungkan tRPC dengan provider seperti NextAuth dan Clerk.

Middleware Otentikasi

Otentikasi Berbasis Token

Pola paling dasar: client mengirim token di header Authorization, server memverifikasinya di createContext:

Memverifikasi token di createContext
import { initTRPC, TRPCError } from "@trpc/server";
 
const verifyToken = async (token: string) => {
  // decode JWT atau panggil provider auth
  const payload = await jwt.verify(token, SECRET);
  return { id: payload.sub, role: payload.role };
};
 
export const t = initTRPC.context<Context>().create({
  createContext: async ({ req }) => {
    const token = req.headers.get("authorization")?.replace("Bearer ", "");
    if (!token) return { user: null };
    const user = await verifyToken(token);
    return { user };
  },
});

Di sini context membaca token sekali per request. createContext menjalankan verifikasi sebelum procedure mana pun berjalan, sehingga ctx.user tersedia di semua middleware dan resolver.

Middleware yang Menjaga Procedure

Procedure publik tetap bisa dipakai siapa saja, tetapi procedure terlindungi butuh user:

Middleware protectedProcedure
export const protectedProcedure = t.procedure.use(
  t.middleware(({ ctx, next }) => {
    if (!ctx.user) {
      throw new TRPCError({ code: "UNAUTHORIZED" });
    }
    return next({ ctx: { ...ctx, userId: ctx.user.id } });
  }),
);

protectedProcedure.use(...) melempar UNAUTHORIZED (HTTP 401) jika user tidak ada, lalu meneruskan ctx.userId ke resolver. Pembagian ini membuat definisi procedure tetap jelas: publicProcedure untuk endpoint terbuka, protectedProcedure untuk yang tertutup.

Authorization dan Role-Based Access Control

Otorisasi Per Procedure

Otentikasi memastikan user ada; otorisasi memastikan user berhak. Contoh: hanya admin yang boleh menghapus user:

Otorisasi berbasis role
export const adminProcedure = protectedProcedure.use(
  t.middleware(({ ctx, next }) => {
    if (ctx.user.role !== "admin") {
      throw new TRPCError({ code: "FORBIDDEN" });
    }
    return next();
  }),
);
 
user: t.router({
  delete: adminProcedure
    .input(z.object({ id: z.number() }))
    .mutation(({ ctx, input }) => hapusUser(input.id, ctx.user.id)),
}),

adminProcedure melempar FORBIDDEN (HTTP 403) untuk user non-admin — HTTP 403 yang membedakan "belum login" (401) dan "tidak berhak" (403) dengan benar.

Check Otorisasi Berdasarkan Data

Tidak semua aturan bisa dinyatakan dengan role saja. Untuk kepemilikan resource, lakukan check di dalam resolver atau middleware dengan input:

Otorisasi kepemilikan resource
edit: protectedProcedure
  .input(z.object({ postId: z.number(), judul: z.string() }))
  .mutation(({ ctx, input }) => {
    const post = cariPost(input.postId);
    if (!post || post.authorId !== ctx.userId) {
      throw new TRPCError({ code: "FORBIDDEN" });
    }
    post.judul = input.judul;
    return post;
  }),

Pola ini penting: user hanya boleh mengedit posting yang dia miliki, bukan hanya yang "berhasil login". Kombinasi middleware role + check kepemilikan menghasilkan otorisasi yang lengkap.

Integrasi dengan Provider Auth

NextAuth dan tRPC di Next.js

Saat memakai NextAuth, session tersedia di createContext lewat getServerSession:

Context dengan NextAuth
import { getServerSession } from "next-auth";
 
export const createContext = async (opts: { req: NextRequest }) => {
  const session = await getServerSession(authOptions);
  return {
    ...opts,
    session,
    user: session?.user ?? null,
  };
};

getServerSession(authOptions) mengambil session server-side secara aman. Middleware protectedProcedure kemudian membaca ctx.user dari session — pola yang sama persis dengan token, hanya sumber datanya berbeda.

Clerk dan Provider Custom

Clerk menyediakan helper serupa untuk framework yang didukungnya, misalnya membaca user dari request. Prinsipnya selalu sama: konversi identitas dari provider menjadi ctx.user, lalu biarkan middleware tRPC menegakkan aturan. Untuk provider custom, cukup implementasikan verifyToken seperti contoh pertama dan pertukarkan secret provider dengan milik kalian.

Info

Pisahkan otentikasi dan otorisasi ke dua lapis middleware. Otentikasi menetapkan ctx.user; otorisasi memeriksa ctx.user. Dengan pemisahan ini, aturan otorisasi bisa berubah tanpa menyentuh proses login.

Kesalahan Umum dan Solusinya

Session Tidak Terbaca di Context

Jika ctx.session selalu null, periksa urutan provider: trpc.Provider harus berada di dalam SessionProvider (NextAuth) atau provider Clerk. Context membaca dari request saat procedure dipanggil, jadi provider auth harus membungkus aplikasi lebih dulu.

Token di URL

Mengirim token di query string adalah kebiasaan buruk yang sudah kita bahas di episode 10. Selalu gunakan header Authorization, dan pastikan httpBatchLink tidak memuntahkan token ke log proxy.

Penutup

Episode 11 mengamankan API kalian: otentikasi berbasis token, session, atau cookie melalui createContext dan middleware, otorisasi per procedure dengan RBAC dan check kepemilikan, serta integrasi mulus dengan NextAuth, Clerk, dan provider custom.

Inti yang harus dibawa pulang:

  • createContext adalah titik ideal untuk memverifikasi identitas.
  • protectedProcedure menegakkan kehadiran user dengan kode 401.
  • adminProcedure dan check kepemilikan menegakkan otorisasi dengan 403.
  • Pisahkan otentikasi dan otorisasi ke lapisan middleware berbeda.
  • NextAuth memakai getServerSession; Clerk memakai helper framework-nya.
  • Identitas dari provider apa pun dikonversi ke ctx.user.

Di episode 12 selanjutnya kita akan membahas keamanan API, rate limiting & best practice — proteksi terhadap injection, overfetch, dan payload abuse, rate limiting di level router atau adapter, serta best practice TLS, CSP, dan sanitasi input.

Belajar tRPC - Otentikasi & Authorization di tRPC | Belajar tRPC