ASP.NET Core API #8: Mengamankan Endpoint API Menggunakan Token JWT

ASP.NET Core API #8: Mengamankan Endpoint API Menggunakan Token JWT

Halo lagi, para penjaga pintu API! 👮‍♂️

Di artikel sebelumnya kita sudah berhasil membuat sistem login yang mengeluarkan token JWT. Sekarang kita akan pasang palang pintu di setiap endpoint yang perlu diamankan. Kita akan gunakan atribut ajaib bernama [Authorize]. Ibaratnya, kita kasih tanda "Hanya yang punya stempel (token) yang boleh lewat".

🤣 Kenapa endpoint API suka pakai atribut [Authorize]? Karena dia nggak mau authorize (otorisasi) sembarangan! (Canggih dikit bahasanya)

Mengingat Kembali JWT

Setelah login, server memberikan token JWT. Token ini mirip tiket masuk bioskop. Di dalam tiket ada data penonton (claims) dan tanda tangan digital. Setiap kali mau masuk ke studio (endpoint), petugas (server) akan memeriksa tiket. Kalau valid, dipersilakan masuk.

Token dikirim melalui header Authorization dengan format: Bearer <token>.

Langkah 1: Menambahkan Atribut [Authorize] pada Controller

Buka KategoriController.cs. Tambahkan using Microsoft.AspNetCore.Authorization; di bagian atas. Kemudian tempelkan atribut [Authorize] di atas class. Jadinya seperti ini:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WisataAPI.Data;
using WisataAPI.Models;

namespace WisataAPI.Controllers
{
    [Authorize]  // 👈 Semua method di controller ini butuh autentikasi
    [Route("api/[controller]")]
    [ApiController]
    public class KategoriController : ControllerBase
    {
        // ... isi controller tetap sama ...
    }
}

Dengan tambahan itu, semua method di KategoriController (GET, POST, PUT, DELETE) hanya bisa diakses jika request menyertakan token yang valid. Tapi biasanya kita ingin GET bisa diakses publik. Makanya kita akan atur lebih spesifik.

Langkah 2: Mengizinkan Akses Publik untuk GET

Kita ingin siapa saja bisa melihat daftar kategori dan detail kategori (GET), tapi hanya yang login yang bisa menambah, mengubah, menghapus. Caranya: pindahkan [Authorize] dari atas class ke method-method yang perlu diamankan.

Contoh di KategoriController.cs:

public class KategoriController : ControllerBase
{
    // GET: api/kategori (publik)
    [HttpGet]
    [AllowAnonymous]  // 👈 Boleh diakses tanpa token
    public async Task<ActionResult<IEnumerable<KategoriWisata>>> GetKategori()
    {
        return await _context.KategoriWisata.ToListAsync();
    }

    // GET: api/kategori/5 (publik)
    [HttpGet("{id}")]
    [AllowAnonymous]
    public async Task<ActionResult<KategoriWisata>> GetKategori(int id)
    {
        // ...
    }

    // POST: api/kategori (hanya yang login)
    [HttpPost]
    [Authorize]
    public async Task<ActionResult<KategoriWisata>> PostKategori(KategoriWisata kategori)
    {
        // ...
    }

    // PUT: api/kategori/5 (hanya yang login)
    [HttpPut("{id}")]
    [Authorize]
    public async Task<IActionResult> PutKategori(int id, KategoriWisata kategori)
    {
        // ...
    }

    // DELETE: api/kategori/5 (hanya yang login)
    [HttpDelete("{id}")]
    [Authorize]
    public async Task<IActionResult> DeleteKategori(int id)
    {
        // ...
    }
}

Perhatikan: [AllowAnonymous] membuka akses meskipun secara default controller tidak memiliki atribut. Kita letakkan di method yang ingin dibuka.

Lakukan hal yang sama di WisataController.cs: GET publik, sisanya [Authorize].

Langkah 3: Menambahkan Role (Peran) – Opsional tapi Keren

Kadang kita ingin membedakan akses berdasarkan peran, misalnya hanya admin yang boleh menghapus, sementara user biasa hanya boleh menambah. Kita bisa menambahkan kolom Role di tabel User.

Tambahkan properti public string Role { get; set; } di model User.cs. Set default misalnya "User". Kemudian update database dengan migration.

Di method register, kita bisa set role sesuai kebutuhan (misal admin ditentukan khusus). Untuk sederhananya, kita tambahkan role "Admin" secara manual di database.

Saat login, kita masukkan role ke dalam claims:

new Claim(ClaimTypes.Role, user.Role)

Kemudian di controller, kita bisa menggunakan [Authorize(Roles = "Admin")] untuk method tertentu.

Contoh di KategoriController:

[HttpDelete("{id}")]
[Authorize(Roles = "Admin")]  // 👈 Hanya admin yang bisa hapus
public async Task<IActionResult> DeleteKategori(int id)
{
    // ...
}

Untuk method lain (POST, PUT) bisa diakses oleh semua role (cukup [Authorize]).

Kita tidak akan praktekkan semua di sini, tapi ini konsep yang berguna nanti.

Langkah 4: Mengambil Informasi User dari Token di Dalam Method

Ketika method dipanggil dengan token, kita bisa tahu siapa pengguna yang melakukan request. Misalnya, kita ingin mencatat siapa yang menambah kategori. Caranya, kita bisa membaca claims dari HttpContext.User.

Contoh di method PostKategori:

[HttpPost]
[Authorize]
public async Task<ActionResult<KategoriWisata>> PostKategori(KategoriWisata kategori)
{
    // Ambil username dari token
    var username = User.Identity.Name; // atau dari claim
    var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // butuh using System.Security.Claims

    // Lakukan sesuatu dengan informasi user, misal simpan ke log
    Console.WriteLine($"User {username} menambah kategori {kategori.Nama}");

    _context.KategoriWisata.Add(kategori);
    await _context.SaveChangesAsync();

    return CreatedAtAction(nameof(GetKategori), new { id = kategori.Id }, kategori);
}

Penjelasan:

  • User.Identity.Name berisi username (dari claim name).
  • User.FindFirstValue(ClaimTypes.NameIdentifier) mengambil ID user.
  • Untuk bisa pakai FindFirstValue, tambahkan using System.Security.Claims; di bagian atas file.

Ini sangat berguna untuk audit log atau membatasi akses berdasarkan kepemilikan data (misal user hanya bisa ubah data miliknya sendiri).

Langkah 5: Uji Coba di Postman

Jalankan API (F5). Buka Postman.

1. Akses GET tanpa token

  • GET https://localhost:7000/api/kategori
  • Seharusnya tetap berhasil karena publik.

2. Akses POST tanpa token

  • POST ke URL yang sama dengan data kategori baru.
  • Response: 401 Unauthorized. Bagus, tertolak.

3. Login untuk mendapatkan token

  • POST https://localhost:7000/api/auth/login dengan username dan password yang sudah didaftarkan.
  • Copy token dari response.

4. Akses POST dengan token

  • Set header Authorization: Bearer <token>.
  • Kirim POST dengan data kategori baru.
  • Seharusnya berhasil (201 Created).

5. Cek apakah token yang salah ditolak

  • Ubah sedikit token (misal hapus satu karakter).
  • Kirim POST, akan dapat 401 Unauthorized.
💡 Di Postman, kamu bisa menyimpan token di variable environment. Caranya: setelah login, di tab Tests, tulis pm.environment.set("token", pm.response.json().token);. Lalu di header Authorization, isi Bearer {{token}}. Praktis!

Langkah 6: Uji Coba Role (Jika Diterapkan)

Jika sudah menambahkan role, coba login sebagai user biasa (role "User") dan coba akses DELETE kategori. Harusnya ditolak dengan 403 Forbidden (bukan 401). Login sebagai admin, baru bisa hapus.

⚠️ Perbedaan 401 dan 403

  • 401 Unauthorized: Kamu belum login atau token tidak valid. (Silakan login dulu)
  • 403 Forbidden: Kamu sudah login, tapi tidak punya izin (misal role tidak cukup). (Maaf, bukan wewenangmu)

Mengatasi Masalah CORS (Opsional)

Kalau nanti API dipanggil dari aplikasi web (React, Vue, dll), mungkin perlu mengatur CORS. Tapi untuk sekarang, kita lewati dulu.

Selamat! API Kamu Sekarang Lebih Aman

Sekarang endpoint sensitif sudah dilindungi. Hanya pengguna terautentikasi yang bisa menambah, mengubah, atau menghapus data. Publik masih bisa melihat data. Ini adalah praktik keamanan yang baik.

Di artikel berikutnya (ASP.NET Core API #9 – Bonus), kita akan menambahkan fitur upload gambar untuk tempat wisata. Siap-siap belajar ngirim file via Postman!

😎 Jangan sampai lupa bawa token ya, kalau nggak, nanti diusir sama petugas API. Mirip lupa bawa tiket bioskop, gabisa masuk deh.

Ditulis oleh Kakak programmer yang dulu juga suka lupa bawa kunci rumah. Kalau ada pertanyaan, tulis di komentar ya!

Lebih baru Lebih lama

نموذج الاتصال