JWT (JSON Web Token) adalah suatu standar industri (RFC 7519) untuk pertukaran informasi dalam bentuk token, token tersebut di dalamnya merepresentasikan Claim yang dipertukarkan antara dua pihak dan diencode dalam struktur JSON. JWT merupakan string berisi JSON Object dari Claim yang dipertukarkan, dan memungkinkan untuk dilakukan enkripsi atau dibubuhkan tanda tangan digital. Definisi Claim itu sendiri adalah informasi dalam bentuk key-value yang dipertukarkan dari satu pihak ke pihak lain. Contoh sederhana dari claim, seseorang login dan berhasil, lalu sistem mengembalikan informasi yang berisi : username nya admin, role nya admin, nama lengkap nya Administrator. Maka dari contoh tersebut terdapat 3 claim yang dapat dituliskan dalam notasi JSON sebagai berikut

{
       "preferred_username": "Admin",
       "name": "Administrator",
       "role": "Admin"
}

Dalam JWT sendiri, terdapat claim dengan nama claim yang sudah teregistrasi/standar untuk digunakan pada JWT, mengacu pada IANA berikut claim terdaftar yang penting penggunaannya pada JWT

  • iss : issuer dari JWT
  • sub : subject dari JWT (User)
  • aud : audience, pihak yang berhak menjadi penerima token JWT
  • exp : expiration time, waktu kapan token JWT kadaluarsa / tidak berlaku
  • nbf : not before time, waktu kapan token JWT bisa mulai berlaku
  • iat : issued at time, waktu kapan token JWT ini diterbitkan
  • jti : JWT ID, unique identifier dari token JWT

Kegunaan JWT adalah untuk otentikasi, otorisasi, dan untuk pertukaran data informasi. Otentikasi adalah proses memverifikasi identitas pengguna yang masuk ke sistem. Otorisasi adalah proses memberikan ijin akses ke suatu resource tertentu.

Bagaimana JWT Bekerja untuk Otentikasi?

  • Pengguna mengirimkan informasi credential ke server berupa identitas username dan password
  • Jika informasi credential username dan password tersebut valid, maka user berhasil diotentikasi dan server akan mengirimkan token JWT ke user
  • Untuk dapat mengakses resource, pengguna harus mengirinkan token JWT melalui HTTP Header dari request dalam bentuk bearer.
  • Server akan mengecek tanda tangan digital pada JWT, jika pada request terdapat JWT dan tanda tangannya valid, maka request bisa diproses lebih lanjut untuk otorisasi.

Bagaimana JWT Bekerja untuk Otorisasi?

  • Pengguna terlebih dahulu melakukan otentikasi dengan mengirimkan informasi credential seperti username dan password, atau secret key.
  • Server akan mengirimkan balik token JWT ke pengguna, yang di dalamnya berisi informasi user, peran, audience, claim, dan ditandatangani oleh server.
  • Setiap kali pengguna akan mengakses resource tertentu, misal suatu API endpoint, pengguna juga harus mengirimkan token JWT tersebut ke server melalui bearer atau header request.
  • Server yang menerima token JWT akan melakukan serangkaian mekanisme pengecekan
    • server akan mengecek tanda tangan digital pada JWT apakah valid
    • server akan mengecek issuer JWT dan audience JWT apakah sesuai
    • server akan mengecek expiration time dari JWT
    • server akan mengecek serangkaian role di dalam claim yang tersimpan pada JWT. Biasanya suatu resource hanya bisa diakses oleh role tertentu saja. Misal endpoint API hanya bisa diakses oleh role Admin, jika di dalam claim JWT informasinya menyatakan hanya berisi role User, maka pengguna tersebut tidak akan diijinkan.

Apa yang sebaiknya diperhatikan ketika menggunakan JWT

  • Gunakan waktu kadaluarsa JWT untuk membatasi penyalahgunaan JWT, sehingga token bisa tidak berlaku setelah melampaui tanggal kadaluarsanya
  • JWT dapat di decode dengan mudah, jangan menyimpan informasi sensitif seperti password di dalam claim JWT. Pengguna dapat mendecode JWT dengan mudah misalkan melalui web https://www.jwt.io
  • Hindari menyimpan token JWT di dalam browser localStorage karena rawan dicuri melalui XSS
  • Gunakan HTTPS untuk pertukaran JWT , karena sama seperti cookies, token JWT juga bisa terkena sadap melalui mekanisme Man-In-The-Middle
  • Jaga secret key untuk tanda tangan JWT dan enkripsi JWT supaya tidak tersebar, karena jika bocor maka pihak lain dapat menerbitkan token JWT yang valid

Gambar 1: decode token JWT melalui web jwt.io

Kenapa Penggunaan JWT Tidak Dianjurkan Untuk Otorisasi

  • JWT Sebenarnya hanyalah sebuah standar format token , yang digunakan oleh developer untuk pertukaran data/ tiket untuk otorisasi dan otentikasi pada sistem mereka
  • Berbeda dengan OAuth2 dan OpenID, OAuth2 dan OpenID merupakan standar protokol yang memiliki standar alur untuk otorisasi dan otentikasi di dalamnya. OAuth2 dan OpenID memiliki kerangka kerja dan aturan bagaimana alur otorisasi dan otentikasi bekerja, suatu hal yang jelas tidak dimiliki oleh JWT Token.
  • OAuth2 dan OpenID mengatur alur , sementara JWT Token menjadi format pertukaran tiket akses yang diterbitkan oleh identity provider / server otorisasi OAuth2 / OpenID
  • JWT tidak memiliki mekanisme untuk revokasi token dan tidak mendefinisikan standar bagaimana merevokasi token supaya token diblacklist / tidak dapat digunakan meski masih dalam masa berlaku token.
  • JWT tidak mendefinisikan alur standar untuk otorisasi, karena hanya berupa standar format token
  • OAuth2 dan OpenID sebagai salah satu protokol otorisasi sudah mendefinisikan alur kerja standar dalam proses otorisasi.
    • OAuth2/OpenID memiliki mekanisme pendelegasian ijin, memungkinkan pengguna mengakses aplikasi pihak ketiga tanpa perlu user harus membagikan kredential login mereka ke aplikasi pihak ketiga.
    • OAuth/OpenID memiliki mekanisme standar untuk revokasi token, sehingga pengguna dapat meminta pencabutan ijin terhadap suatu token ke server otorisasi, memudahkan pengguna untuk mencabut token.
    • OAuth2/OpenID memiliki standar alur untuk berbagai case-case penggunaan token dengan panduan yang jelas, misalkan untuk jenis penggunaan aplikasi web, untuk aplikasi mobile, dll.

Dalam tulisan saya sebelumnya sudah pernah dibahas mengenai bagaimana membuat Openid/OAuth2 di server azure ( https://bayuprn.com/2025/05/membuat-openid-oauth2-akses-di-server-azure/ ) , Serta bagaimana membuat website ASP.NET Core dengan OpenID (https://bayuprn.com/2025/05/membuat-asp-net-core-website-dengan-openid-authentication/ ).

MEMBUAT PROJECT WEB API ASP.NET CORE DENGAN OTENTIKASI DAN OTORISASI JWT

Untuk membuat web api dengan dukungan otorisasi JWT, kita bisa membuatnya dengan mudah menggunakan template project ASP.NET Core Web API. Untuk dukungan JWT, sudah tersedia nuget package resmi untuk JWT. KIta bisa menggunakan IDE Microsoft Visual Studio 2022 Community Edition untuk membuat project.

Pada Visual Studio 2022, pilih project ASP.NET Core Web API, lalu gunakan project name JWTWebApi. Pada target framework, pilih .NET 9.0. Centang pada Configure for HTTPS, dan Enable Open API Support seperti pada gambar, lalu Create Project.

Gambar 2: Dialog Create new project dengan template ASP.NET Core Web API pada Visual Studio 2022

Setelah project dibuat, klik kanan pada bagian Dependencies, lalu pilih menu Manage Nuget Package. Berikut ini adalah package yang harus diinstal

  • Microsoft.AspNetCore.Authentication.JwtBearer
  • Microsoft.AspNetCore.OpenApi
  • Swashbuckle.AspNetCore.Swagger
  • Swashbuckle.AspNetCore.SwaggerUI
  • Swashbuckle.AspNetCore.Annotations
  • Swashbuckle.AspNetCore.Filters
Gambar 3: Nuget Package dependencies pada project Solution Explorer
Gambar 4: Nuget Package Manager untuk menginstall package Microsoft.AspNetCore.Authentication.JwtBearer pada project web api

Apabila package Nuget berhasil terinstall dan semua class berhasil dibuat, hasil akhir nantinya akan mirip seperti pada gambar berikut ini:

Gambar 5: Struktur project JWTWebApi pada solution explorer

Perhatikan Gambar 6, gambar tersebut menggambarkan mekanisme bagaimana otentikasi dengan Bearer JWT. Kita perlu membuat sebuat controller khusus dengan endpoint token untuk validasi user password da mendapatkan token. Kita juga perlu membuat class implementasi IUserService yang menangani validasi user. Dan sebuah class implementasi dari IJWTTokenService yang bertukas generate token JWT menggunakan kunci enkripsi yang ada.

Gambar 6: Sequence diagram otentikasi user dan generate token JWT

Mengacu pada Gambar 5, Pertama – tama, pada project buatlah folder Models. Di dalam folder Models, tambahkan class UserModel, TokenModel, dan ResponseResult berikut ini. Class ini digunakan untuk model return value dari method.

 public class UserModel
 {
     public string UserId { get; set; }
     public string Password { get; set; }
     public string Role { get; set; }

 }
  public class TokenModel
  {
      public bool IsSuccess { get; set; }
      public string Message { get; set; }

      public string Token { get; set; }
  }
   public class ResponseResult
   {
       public bool IsSuccess { get; set; }
       public string Message { get; set; }
   }

Selanjutnya, seperti pada Gambar 5, pada project JWTWebApi, buatlah folder Interfaces yang akan berisi interface-interface service yang diperlukan untuk Dependency Injection. Kita perlu membuat interface IUserService dan IJWTTokenService. Berikut adalah listing kodenya.

  public interface IJWTTokenService
  {
      string GenerateJWTTokenString(string userId, string role, DateTime expired);
  }
  public interface IUserService
  {
      string GenerateUserToken(string userId, string password);
      UserModel ValidateUser(string username, string password);
  }

Kemudian, sesuai dari Gambar 5, kita juga perlu membuat folder Services di dalam project. Folder ini berisi class class implementasi interface untuk Dependency Injection.

Berikut adalah listing code untuk class JWTTokenService yang merupakan implementasi dari interface IJWTTokenService. Kita menggunakan algoritma HmacSha256 untuk signing token JWT. Perhatikan bahwa Secret Key, Issuer, dan Audience diambil dari setting di appsettings.json !

 public class JWTTokenService : IJWTTokenService
 {
     private IConfiguration cfg;

     public JWTTokenService(IConfiguration configuration)
     {
         cfg = configuration;
     }

     public string GenerateJWTTokenString(string userId, string role, DateTime expired)
     {
         string secretKey = cfg.GetValue<string>("JwtToken:SecretKey");
         string issuer = cfg.GetValue<string>("JwtToken:Issuer");
         string audience = cfg.GetValue<string>("JwtToken:Audience");

         var claims = new List<Claim>()
         {
             new Claim(ClaimTypes.NameIdentifier, userId),
             new Claim(ClaimTypes.Name, userId),
             new Claim(ClaimTypes.Role, role),
             new Claim("UserID", userId)
         };

         var symKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(secretKey));
         var signingCredential = new SigningCredentials(symKey, SecurityAlgorithms.HmacSha256);

         var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: expired,
            signingCredentials: signingCredential
        );

         var tokenHandler = new JwtSecurityTokenHandler();
         return tokenHandler.WriteToken(token);
     }
 }

Berikut adalah class UserService yang merupakan implementasi dari interface IUserService. Class ini memvalidasi parameter user password yang dilewatkan ke method ValidateUser. Dalam implementasi real, pengecekan user password bisa terhubung ke table User yang ada di Database. Untuk menyederhanakan coding , kita cukup mengecek user “admin” dengan role Administrator, dan user “user” dengan role User. Class UserService juga terdapat method GenerateUserToken untuk memvalidasi user password dengan memanggil method ValidateUser di dalamnya, lalu jika sukses maka akan memanggil method GenerateJWTTokenString dari IJWTTokenService.

    public class UserService : IUserService
    {
        private IJWTTokenService jwtSvc;

        public UserService(
            IJWTTokenService jwtTokenService
            )
        {
            jwtSvc = jwtTokenService;
        }

        public UserModel ValidateUser(string username, string password)
        {
            if (string.Equals(username, "admin", StringComparison.Ordinal)
                && string.Equals(password, "admin", StringComparison.Ordinal)
                )
            {
                return new UserModel() { UserId = username, Password = password, Role = "Administrator" };
            }
            else if (string.Equals(username, "user", StringComparison.Ordinal)
                && string.Equals(password, "user", StringComparison.Ordinal)
                )
            {
                return new UserModel() { UserId = username, Password = password, Role = "User" };
            }
            else
            {
                return null;
            }
        }

        public string GenerateUserToken(string userId, string password)
        {
            ArgumentException.ThrowIfNullOrWhiteSpace(userId, "userId");
            ArgumentException.ThrowIfNullOrWhiteSpace(password, "password");

            var user = ValidateUser(userId, password);
            if (user == null)
            {
                throw new Exception("Unauthorize");
            }

            var expired = DateTime.Now.AddMinutes(30);
            var token = jwtSvc.GenerateJWTTokenString(user.UserId, user.Role, expired);

            return token;
        }
    }

Setup Service, Authentication, dan Dependency Injection

Buka file Program.cs yang ada pada project. Di sini, kita perlu mengkonfigurasi project ketika initialisasi untuk mendukung otentikasi JWT Bearer serta menambahkan service penting dan beberapa service yang telah kita buat ke dependency injection. Selain itu kita setting agar secara default tidak ada perubahan Name Case dari response JSON yang dihasilkan.

var builder = WebApplication.CreateBuilder(args);
var appsetting = builder.Configuration.AddJsonFile("appsettings.json").AddEnvironmentVariables().Build();

// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
               .AddJwtBearer(options =>
               {
                   options.TokenValidationParameters = new TokenValidationParameters
                   {
                       ValidateIssuer = true,
                       ValidateAudience = true,
                       ValidateLifetime = true,
                       ValidateIssuerSigningKey = true,
                       ValidIssuer = builder.Configuration["JwtToken:Issuer"],
                       ValidAudience = builder.Configuration["JwtToken:Audience"],
                       IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtToken:SecretKey"]))
                   };
               });
builder.Services.AddAuthorization();

builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IConfiguration>(appsetting);
builder.Services.AddScoped<IJWTTokenService, JWTTokenService>();
builder.Services.AddScoped<IUserService, UserService>();

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
    });

Berikut ini adalah listing lengkap kode dari Program.cs. Perhatikan bahwa di listing kode juga ditambahkan dukungan Swagger.

 public class Program
 {
     public static void Main(string[] args)
     {
         var builder = WebApplication.CreateBuilder(args);
         var appsetting = builder.Configuration.AddJsonFile("appsettings.json").AddEnvironmentVariables().Build();


         // Add services to the container.
         builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                        .AddJwtBearer(options =>
                        {
                            options.TokenValidationParameters = new TokenValidationParameters
                            {
                                ValidateIssuer = true,
                                ValidateAudience = true,
                                ValidateLifetime = true,
                                ValidateIssuerSigningKey = true,
                                ValidIssuer = builder.Configuration["JwtToken:Issuer"],
                                ValidAudience = builder.Configuration["JwtToken:Audience"],
                                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtToken:SecretKey"]))
                            };
                        });
         builder.Services.AddAuthorization();

         builder.Services.AddHttpContextAccessor();
         builder.Services.AddSingleton<IConfiguration>(appsetting);
         builder.Services.AddScoped<IJWTTokenService, JWTTokenService>();
         builder.Services.AddScoped<IUserService, UserService>();

         builder.Services.AddControllers()
             .AddJsonOptions(options =>
             {
                 options.JsonSerializerOptions.PropertyNamingPolicy = null;
                 options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
                 options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
             });

         // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
         builder.Services.AddOpenApi("v1");

         builder.Services.Configure<GzipCompressionProviderOptions>(options =>
         {
             options.Level = System.IO.Compression.CompressionLevel.SmallestSize;
         });

         builder.Services.AddCors(o => o.AddPolicy("AllowAllOrigins", builder =>
         {
             builder.AllowAnyOrigin()
                     .AllowAnyMethod()
                     .AllowAnyHeader();
         }));


         builder.Services.AddSwaggerGen(c =>
         {
             c.SwaggerDoc("v1", new OpenApiInfo { Title = "Example Web API", Version = "v1", Description = "Access API by Role Claim." });
             c.EnableAnnotations();
             c.ExampleFilters();

             c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
             {
                 Name = "Authorization",
                 Description = "Please provide JWT Token as Bearer",
                 In = ParameterLocation.Header,
                 Type = SecuritySchemeType.Http,
                 Scheme = JwtBearerDefaults.AuthenticationScheme
             });

             c.AddSecurityRequirement(new OpenApiSecurityRequirement
             {
                 {
                       new OpenApiSecurityScheme
                       {
                           Reference = new OpenApiReference
                           {
                               Type = ReferenceType.SecurityScheme,
                               Id = "Bearer"
                           }
                       },
                      new string[] {}
                 }
             });
         });
         builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly());



         var app = builder.Build();

         // Configure the HTTP request pipeline.
         if (app.Environment.IsDevelopment())
         {
             app.MapOpenApi();
         }

         app.UseHttpsRedirection();            
         app.UseCors();
         app.UseAuthentication();
         app.UseAuthorization();


         app.UseSwagger();
         app.UseSwaggerUI(c =>
         {
             c.SwaggerEndpoint("v1/swagger.json", "Example Web API");
         });

         app.MapControllers();
         app.MapSwagger();

         app.Run();
     }
 }

Selanjutnya, cek file konfigurasi appsettings.json. Di awal kita membuat kode untuk mengambil Secret Key, Issuer, dan Audience dari setting appsettings.json. Edit file appsettings.json dan pastikan bagian JwtToken ada di file konfigurasi.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "JwtToken": {
    "SecretKey": "MyRandomString1234567890As$ECRETK3y",
    "Issuer": "https://localhost:7003/",
    "Audience": "https://localhost:7003/"
  }
}

Membuat Controller API

Buatlah file controller baru AuthController di dalam folder Controllers. Controller ini memiliki endpoint action Token yang gunanya menerima user password yang disubmit pengguna , dan mengembalikan response berupa token JWT jika berhasil. Kita perlu mengambil service IUserService melalui dependency injection pada parameter contructor dari controller.

    [SwaggerTag("Authorize")]
    [ApiController] 
    [Route("api/auth")]
    [Microsoft.AspNetCore.Cors.EnableCors("AllowAllOrigins")]
    public class AuthController:Controller
    {
        private IUserService _userService;

        public AuthController(IUserService userService)
        {
            _userService = userService;
        }


        [SwaggerOperation("Get Bearer Token")]
        [SwaggerResponse(200, "Bearer Token", typeof(TokenModel))]
        [HttpGet("token")]
        public IActionResult Token(string userId, string password)
        {
            try
            {
                var token = _userService.GenerateUserToken(userId, password);

                return Json(new TokenModel()
                {
                    Token = token,
                    IsSuccess = true,
                    Message = token
                } );
            }
            catch (Exception ex)
            {
                return Json(new ResponseResult()
                {
                    IsSuccess = false,
                    Message = $"Generate token failed ! {ex.Message}"
                });
            }
        }
    }

Mengecek ada tidaknya JWT Token yang dikirimkan saja tidak cukup untuk membatasi akses ke endpoint API. Ada endpoint Action yang hanya bisa diakses oleh Administrator , dan ada endpoint Action yang bisa diakses baik Administrator maupun User. Untuk menerapkan validasi tersebut, kita perlu membuat sebuat Action Filter.

Action Filter merupakan sebuah class yang mengandung kode yang akan di jalankan pada tahapan tertentu dari pemrosesan request, Action Filter dieksekusi ketika sebelum Action dari Controller di proses, atau setelah Action dari Controller selesai diproses. (Kunjungi https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-9.0#action-filters untuk informasi lebih lengkap)

Untuk membuat action filter, sesuai dengan gambar 5, buatlah folder Modules pada project. Selanjutnya pada folder Modules, buatlah class baru bernama RequiredRoleAttribute. Class ini memiliki parameter array dari nama Role yang dibutuhkan. Cara kerjanya, sebelum Action dari controller diproses, action filter RequiredRoleAttribute akan mengecek claim Role dari token JWT apakah memiliki salah satu nama role yang dibutuhkan. Jika ya, maka action dari controller akan lanjut diproses, jika tidak maka akan return Unauthorize. Berikut adalah kode lengkap dari RequiredRoleAttribute.

 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
 public class RequiredRoleAttribute : ActionFilterAttribute
 {

     private List<string> roles;
     public RequiredRoleAttribute(params string[] roles)
     {
         this.roles = roles?.ToList();
     }

     public override void OnActionExecuting(ActionExecutingContext context)
     {            
         try
         {
             if (this.roles == null || this.roles.Count == 0)
             {
                 base.OnActionExecuting(context);
             }
             else
             {
                 bool isAuthorized = false;
                 if (context.HttpContext.User?.Identity?.IsAuthenticated == true)
                 {
                     var claims = context.HttpContext.User?.Claims?.ToList() ?? new List<System.Security.Claims.Claim>();
                     var role = claims.FirstOrDefault(c => c.Type == ClaimTypes.Role);
                     var roleName = role?.Value;
                     if (this.roles.Contains(roleName))
                     {
                         isAuthorized = true;
                     }
                 }

                 if (isAuthorized)
                 {
                     base.OnActionExecuting(context);
                 }
                 else
                 {
                     context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
                 }
             }
         }
         catch (Exception)
         {
             context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError);
         }                            
     }
 }

Setelah berhasil membuat action filter RequiredRoleAttribute, kita lanjut membuat controller SecureApiController yang menjadi contoh sederhana penerapan endpoint web api. Buatlah file SecureApiController di dalam folder Controllers pada proyek sesuai dengan contoh pada Gambar 5.

Skenario nya, untuk mengakses controller SecureApiController harus menggunakan JWT Token yang valid, lalu action GetAdminInfo hanya bisa diakses admin, dan action GetUserInfo bisa diakses baik user maupun admin. Kita perlu menambahkan attribute [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] pada controller agar supaya hanya request dengan JWT Token yang valid yang bisa mengakses. Lalu kita perlu menambahkan [RequiredRole(“Administrator”)] pada GetAdminInfo agar supaya hanya Administrator yang bisa mengakses. Berikut ini adalah listing lengkap dari kode SecureApiController.

 [SwaggerTag("Example Secure API v1")]
 [ApiController]
 [Route("/api/secure-api")]
 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
 [Microsoft.AspNetCore.Cors.EnableCors("AllowAllOrigins")]
 public class SecureApiController: Controller
 {
     public SecureApiController()
     {
         
     }



     [RequiredRole("Administrator")]
     [SwaggerOperation("Get Admin Info")]
     [SwaggerResponse(200, "Information", typeof(ResponseResult))]
     [HttpGet("get-admin-info")]
     public IActionResult GetAdminInfo()
     {
         var claims = HttpContext.User.Claims.ToList();
         var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
         var role = claims.FirstOrDefault(c => c.Type == ClaimTypes.Role);

         return Json(new ResponseResult()
         {
             IsSuccess = true,
             Message = $"Howdy Admin, Your username is {name?.Value??"N/A"}, and your Role is {role?.Value??"N/A"}"
         });
     }




     [RequiredRole("Administrator","User")]
     [SwaggerOperation("Get User Info")]
     [SwaggerResponse(200, "Information", typeof(ResponseResult))]
     [HttpGet("get-user-info")]
     public IActionResult GetUserInfo()
     {
         var claims = HttpContext.User.Claims.ToList();
         var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier); 

         return Json(new ResponseResult()
         {
             IsSuccess = true,
             Message = $"Hello User, Your username is {name?.Value ?? "N/A"}"
         });
     }
 }

Testing JWT Token dan Endpoint API

Jalankan aplikasi dengan F5 atau debug https di visual studio. Ekspektasinya adalah running di alamat URL https://localhost:7003/ sesuai dengan alamat issuer. Tidak perlu installasi tools tambahan, kita cukup menggunakan Swagger yang ada. Buka alamat https://localhost:7003/swagger/index.html untuk membuka halaman Swagger.

Gambar 7: Tampilan halaman Swagger menampilkan list endpoint API yang telah dibuat pada proyek

Mari kita coba hit endpoint get-user-info melalui Swagger. Hasil responsenya Unauthorize 401 meminta challenge Bearer. Ini artinya kita diminta menyertakan token JWT sebagai bearer.

Gambar 8: Hit endpoint get-user-info tanpa menyertakan token JWT

Oleh karena itu, kita hit endpoint /api/auth/token untuk mendapatkan token JWT terlebih dahulu. Kita gunakan user id : user dan password: user sebagai parameter yang kita input di Swagger. Selanjutnya kita klik Execute. Jika sukses maka response api tersebut hasilnya seharusnya token JWT.

Gambar 9: execute endpoint token untuk mendapatkan JWT menggunakan user

Cek bagian response dari api, dan ambil value dari property Token. Berikut contoh Token JWT yang dihasilkan.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6InVzZXIiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidXNlciIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IlVzZXIiLCJVc2VySUQiOiJ1c2VyIiwiZXhwIjoxNzU3MzMzMDE4LCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo3MDAzLyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMDMvIn0.Rad_zVza9aMn_VXorZNoucI4RIYRble2iNRtrqww6v8

Selanjutnya pada bagian kanan atas, klik Tombol Authorize dari Swagger. Pada dialog available Authorization, Masukkan token JWT pada textbox value yang tersedia. Lalu click tombol Authorize dan Close.

Gambar 10: Tombol Authorize pada Swagger
Gambar 11: Dialog popup untuk menginput authorization pada Swagger

Kita coba kembali untuk melakukan hit ke endpoint get-user-info. Kali ini akan berhasil dengan response IsSuccess = true.

Gambar 12: Hit endpoint get-user-info berhasil dan mendapatkan response sukses

Namun ketika hit endpoint get-admin-info, kita mendapat response dengan status code 401 Unauthorize dikarenakan token JWT yang digenerate menggunakan user dengan role User, bukan Administrator.

Gambar 13: hit endpoint get-admin-info gagal unauthrorize jika user nya tidak memiliki role Administrator

Kita coba lagi untuk generate ulang token JWT di endpoint /api/auth/token , namun kali ini kita menggunakan user id admin dan password admin.

Gambar 14: execute endpoint token untuk mendapatkan JWT menggunakan admin

Berikut adalah hasil token JWT baru yang dihasilkan. Jika di cek pada jwt.io, dia akan memiliki claim role yang berbeda dengan sebelumnya, karena memiliki role Administrator.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6ImFkbWluIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6ImFkbWluIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5pc3RyYXRvciIsIlVzZXJJRCI6ImFkbWluIiwiZXhwIjoxNzU3MzMzNjYxLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo3MDAzLyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMDMvIn0.dFg-wKeW4mGDMEzYosJguqnw2_Nj6uovw3p3vhJkxsI

Lewat tombol Authorize Swagger, kita input ulang token baru yang kita peroleh di dialog popup autorization swagger.

Gambar 15: Dialog popup untuk menginput authorization pada Swagger, token baru diinput untuk menggantikan token lama

Selanjutnya kita coba untuk melakukan hit ulang di endpoint get-admin-info. Kali ini seharusnya berhasil dan memberikan response sukses.

Gambar 16: hit endpoint get-admin-info sukses setelah menggunakan token JWT yang digenerate menggunakan user dengan role Administrator

KESIMPULAN

  • JWT Token dapat digunakan untuk otorisasi dan otentikasi sederhana yang dapat diterapkan pada endpoint web api
  • Penerapan akses otorisasi berdasarkan claim peran membantu meningkatkan keamanan endpoint API karena bisa memfilter berdasarkan Role yang dimiliki user
  • Action filter perlu dibuat untuk otorisasi berdasarkan peran. Action filter RequiredRoleAttribute yang dibuat berhasil memfilter akses ke endpoint berdasarkan claim Role.
  • Untuk dapat menggunakan JWT Token sebagai otorisasi dan otentikasi, kita perlu membangun sendiri token endpoint dari awal, berbeda dengan OAuth2/OpenID yang tersedia server otorisasi milik identity provider di internet.

Download listing kode Source Code lengkap di sini

,


Leave a Reply

Your email address will not be published. Required fields are marked *