Kemenkeu.IAM.Plugin 1.0.13

dotnet add package Kemenkeu.IAM.Plugin --version 1.0.13
                    
NuGet\Install-Package Kemenkeu.IAM.Plugin -Version 1.0.13
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Kemenkeu.IAM.Plugin" Version="1.0.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Kemenkeu.IAM.Plugin" Version="1.0.13" />
                    
Directory.Packages.props
<PackageReference Include="Kemenkeu.IAM.Plugin" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Kemenkeu.IAM.Plugin --version 1.0.13
                    
#r "nuget: Kemenkeu.IAM.Plugin, 1.0.13"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Kemenkeu.IAM.Plugin@1.0.13
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Kemenkeu.IAM.Plugin&version=1.0.13
                    
Install as a Cake Addin
#tool nuget:?package=Kemenkeu.IAM.Plugin&version=1.0.13
                    
Install as a Cake Tool

Kemenkeu IAM Plugin

Plugin .NET untuk integrasi aplikasi ASP.NET Core dengan sistem Kemenkeu IAM (Identity and Access Management).

📋 Fitur

  • Authorization: Validasi akses endpoint berdasarkan permission pengguna
  • Sieve/Data Redaction: Penyembunyian field sensitif berdasarkan hak akses pengguna
  • Scope-Based Filtering: Filter data di level query (SQL WHERE) berdasarkan data scope pengguna (satker, unit, dll)
  • Route Provisioning: Registrasi otomatis endpoint aplikasi ke IAM server
  • Header Propagation: Propagasi authorization token antar service

🔧 Requirements

  • .NET 10.0 atau lebih tinggi
  • ASP.NET Core Application
  • Akses ke Kemenkeu IAM Server

📦 Instalasi

1. Tambahkan Package Reference

Tambahkan plugin ke project Anda:

<ItemGroup>
    <PackageReference Include="Kemenkeu.IAM.Plugin" Version="1.0.3" />
</ItemGroup>

2. Konfigurasi IAM

Tambahkan konfigurasi IAM di appsettings.json:

{
  "IAM": {
    "BackendName": "NamaAplikasi",
    "BaseUri": "https://iam-server.kemenkeu.go.id",
    "Authorization": "Bearer <token-untuk-provisioning>",
    "AppId": "00000000-0000-0000-0000-000000000000"
  }
}

Keterangan:

  • BackendName: Nama aplikasi/service Anda
  • BaseUri: URL IAM server
  • Authorization: Token bearer untuk provisioning (biasanya service token)
  • AppId: GUID aplikasi yang terdaftar di IAM

🚀 Penggunaan

Setup di Program.cs

using Iam.Plugin.Services;

var builder = WebApplication.CreateBuilder(args);

// Tambahkan Kemenkeu IAM
builder.AddKemenkeuIam();

// Services lainnya...
builder.Services.AddControllers();

var app = builder.Build();

// Aktifkan middleware IAM
app.UseKemenkeuIam();

// Middleware lainnya...
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

// (Optional) Provision routes ke IAM server saat startup
await app.ProvisionKemenkeuIam();

app.Run();

1. Authorization

Gunakan attribute [KemenkeuAuthorize] untuk mengamankan endpoint:

using Iam.Plugin.Attributes;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class EmployeeController : ControllerBase
{
    // Endpoint ini memerlukan authorization dari IAM
    [HttpGet]
    [KemenkeuAuthorize]
    public async Task<IActionResult> GetEmployees()
    {
        // Business logic...
        return Ok(employees);
    }

    // Tanpa attribute = tidak di-check IAM
    [HttpGet("public")]
    public IActionResult GetPublicInfo()
    {
        return Ok("Public data");
    }
}

Cara kerja:

  • Saat request masuk, filter akan memanggil IAM server endpoint /iam-agent/authorize
  • IAM server akan memvalidasi apakah user memiliki akses ke endpoint tersebut
  • Jika tidak authorized, akan return HTTP 403 Forbidden

2. Data Sieve (Field Redaction)

Gunakan attribute [KemenkeuSieve] untuk menyembunyikan field sensitif:

using Iam.Plugin.Attributes;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
[KemenkeuSieve] // Attribute di level controller
public class SalaryController : ControllerBase
{
    public class SalaryDto
    {
        public string EmployeeId { get; set; }
        public string Name { get; set; }
        public decimal BasicSalary { get; set; }
        public decimal Allowance { get; set; }
        public string BankAccount { get; set; }
    }

    [HttpGet("{id}")]
    [KemenkeuAuthorize]
    public async Task<IActionResult> GetSalary(string id)
    {
        var salary = await _service.GetSalaryAsync(id);
        
        // Response akan di-filter sesuai permission user
        // Field seperti BasicSalary, BankAccount bisa di-redact
        return Ok(salary);
    }
}

Cara kerja:

  • Setelah action execute, filter akan memanggil /iam-agent/sieve
  • IAM server return list field yang harus di-redact untuk user tersebut
  • Plugin akan set field tersebut menjadi null di response JSON
  • Field yang di-redact tidak akan muncul di response (karena JsonIgnoreCondition.WhenWritingNull)

3. Scope-Based Data Filtering

IAM menyediakan sistem data scope untuk membatasi data yang dapat diakses pengguna berdasarkan area tanggung jawab (satker, unit organisasi, dll). Plugin menyediakan extension method .ApplyScope() untuk memfilter data di level query (database) sehingga hanya data yang berhak yang di-fetch.

Konsep Scope

Scope type didefinisikan secara hierarkis dan database-driven — menambah scope type baru tidak perlu deploy ulang:

Scope Type Label Level Parent
KdDept Kode Departemen 10 — (root)
KdUnit Kode Unit 20 KdDept
KdSatker Kode Satker 30 KdUnit
KdLokasi Kode Lokasi 40 KdSatker

Setiap pengguna memiliki scope melalui User Group membership. Scope user dihitung dengan:

  1. Intersection dari root ke leaf dalam satu rantai user group (parent → child)
  2. Union antar semua user group yang dimiliki user

Hasilnya berupa Dictionary<string, List<string>> — setiap scope type memetakan ke daftar nilai yang diizinkan. Nilai ["*"] berarti akses ke semua nilai.

Menggunakan [KemenkeuScope] + .ApplyScope()

Step 1: Dekorasi property DTO/entity dengan [KemenkeuScope]:

using Iam.Plugin.Attributes;

public class AnggaranDto
{
    [KemenkeuScope(ScopeType = "KdSatker")]
    public string KdSatker { get; set; }

    [KemenkeuScope(ScopeType = "KdUnit")]
    public string KdUnit { get; set; }

    public string Uraian { get; set; }
    public decimal Pagu { get; set; }
}

Step 2: Inject IKemenkeuScopeContext dan panggil .ApplyScope() di service/repository:

public class AnggaranService
{
    private readonly AppDbContext _db;
    private readonly IKemenkeuScopeContext _scopeContext;

    public AnggaranService(AppDbContext db, IKemenkeuScopeContext scopeContext)
    {
        _db = db;
        _scopeContext = scopeContext;
    }

    public async Task<List<AnggaranDto>> GetAllAsync(CancellationToken ct)
    {
        var scopes = await _scopeContext.GetScopesAsync(ct);

        return await _db.Anggaran
            .Where(a => !a.IsDeleted)
            .Select(a => new AnggaranDto { ... })
            .ApplyScope(scopes)   // → SQL WHERE kd_satker IN ('010001', '010002')
            .ToListAsync(ct);
    }
}

.ApplyScope() menghasilkan SQL WHERE IN (...) sehingga database hanya mengembalikan data yang berhak. Ini jauh lebih efisien daripada fetch semua lalu filter di memory, dan pagination tetap akurat.

Scope Rules
User Scope Hasil
["*"] Tidak ada filter (semua data)
["010001", "010002"] WHERE kd_satker IN ('010001', '010002')
Scope type tidak ada / kosong WHERE 1=0 (deny semua)
Mengambil Scope Secara Manual

Gunakan IKemenkeuIamService jika perlu mengambil scope user di business logic:

public class CustomService
{
    private readonly IKemenkeuIamService _iamService;

    public CustomService(IKemenkeuIamService iamService)
    {
        _iamService = iamService;
    }

    public async Task<List<Anggaran>> GetFilteredData()
    {
        // Ambil scope user
        var scopes = await _iamService.GetUserScopes();
        // scopes = { "KdSatker": ["010001", "010002"], "KdDept": ["*"] }

        // Gunakan untuk query manual
        if (scopes.TryGetValue("KdSatker", out var satkerValues) && !satkerValues.Contains("*"))
        {
            return await _db.Anggaran
                .Where(a => satkerValues.Contains(a.KdSatker))
                .ToListAsync();
        }

        return await _db.Anggaran.ToListAsync(); // wildcard = semua data
    }
}

4. Manual Authorization Check

Gunakan IKemenkeuIamService untuk validasi manual:

using Iam.Plugin.Services;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class CustomController : ControllerBase
{
    private readonly IKemenkeuIamService _iamService;

    public CustomController(IKemenkeuIamService iamService)
    {
        _iamService = iamService;
    }

    [HttpPost("complex-operation")]
    public async Task<IActionResult> ComplexOperation()
    {
        // Custom authorization check
        var metadata = new KemenkeuIamMetadata(
            ControllerName: "CustomController",
            ActionName: "ComplexOperation",
            HttpMethod: "POST"
        );

        bool isAuthorized = await _iamService.Authorize(metadata);
        if (!isAuthorized)
        {
            return Forbid();
        }

        // Get redacted fields for current user
        string[] redactedFields = await _iamService.GetRedactedFields(metadata);
        
        // Business logic...
        return Ok();
    }
}

5. Route Provisioning

Provisioning otomatis mendaftarkan semua endpoint aplikasi ke IAM server:

// Di Program.cs, setelah app.Run() atau di startup
await app.ProvisionKemenkeuIam();

Kapan digunakan:

  • Saat pertama kali deploy aplikasi
  • Setelah menambah/mengubah endpoint
  • Untuk sinkronisasi dengan IAM server

Atau gunakan endpoint provisioning manual:

[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
    private readonly KemenkeuIamProvisioner _provisioner;
    private readonly IConfiguration _configuration;

    public AdminController(
        KemenkeuIamProvisioner provisioner,
        IConfiguration configuration)
    {
        _provisioner = provisioner;
        _configuration = configuration;
    }

    [HttpPost("provision-routes")]
    public async Task<IActionResult> ProvisionRoutes()
    {
        var config = _configuration.GetSection("IAM")
            .Get<KemenekeuIamConfiguration>();
        
        await _provisioner.RegisterRoutes(
            config.AppId, 
            config.Authorization
        );
        
        return Ok("Routes provisioned successfully");
    }
}

🔍 Troubleshooting

Authorization Selalu Gagal

Penyebab:

  • Header Authorization tidak ter-propagate
  • IAM server tidak bisa diakses
  • User tidak memiliki permission

Solusi:

// Pastikan UseKemenkeuIam() dipanggil sebelum UseAuthentication()
app.UseKemenkeuIam();
app.UseAuthentication();
app.UseAuthorization();

Provisioning Gagal

Penyebab:

  • Token authorization tidak valid
  • AppId tidak terdaftar di IAM

Solusi:

  • Verifikasi konfigurasi IAM:Authorization dan IAM:AppId
  • Check koneksi ke IAM:BaseUri

Field Tidak Ter-redact

Penyebab:

  • [KemenkeuSieve] attribute tidak diterapkan
  • JSON serializer custom tidak kompatibel

Solusi:

  • Pastikan controller memiliki attribute [KemenkeuSieve]
  • Plugin otomatis set JsonIgnoreCondition.WhenWritingNull

Scope Filter Tidak Bekerja

Penyebab:

  • Property DTO tidak memiliki attribute [KemenkeuScope]
  • User belum memiliki scope assignment di user group manapun
  • IAM server /iam-agent/scopes tidak bisa diakses (fail-open: data tidak difilter)

Solusi:

  • Verifikasi attribute [KemenkeuScope(ScopeType = "...")] pada property DTO
  • Pastikan scope type key cocok dengan yang terdaftar di ScopeTypeDefinition (case-sensitive)
  • Check user group membership dan scope assignment via IAM admin
  • Verify IAM server berjalan dan Authorization header ter-propagate

Data Kosong Setelah Scope Filter

Penyebab:

  • User tidak memiliki scope type yang dibutuhkan (absent = deny all)
  • Scope value tidak cocok dengan data di response

Solusi:

  • Periksa scope user: GET /iam-agent/scopes — pastikan scope type ada di response
  • Tambahkan scope assignment di user group jika diperlukan
  • Gunakan ["*"] untuk memberikan akses ke semua nilai

📚 API Reference

Attributes

[KemenkeuAuthorize]

Mengamankan endpoint dengan IAM authorization.

  • Dapat digunakan di controller atau method level
  • Inherit dari AuthorizeAttribute
[KemenkeuSieve]

Mengaktifkan field redaction untuk response.

  • Hanya dapat digunakan di controller level
[KemenkeuScope]

Menandai property DTO/entity untuk query-level data scope filtering.

  • Digunakan di property level pada DTO/entity class
  • Parameter: ScopeType — nama scope type yang terdaftar di IAM (e.g. "KdSatker", "KdUnit")
  • Gunakan bersama .ApplyScope(scopes) di service/repository untuk generate SQL WHERE IN (...)
[KemenkeuScope(ScopeType = "KdSatker")]
public string KdSatker { get; set; }

Services

IKemenkeuIamService
public interface IKemenkeuIamService
{
    // Validasi authorization untuk endpoint tertentu
    Task<bool> Authorize(
        KemenkeuIamMetadata metadata, 
        CancellationToken ct = default
    );
    
    // Dapatkan daftar field yang harus di-redact
    Task<string[]> GetRedactedFields(
        KemenkeuIamMetadata metadata, 
        CancellationToken ct = default
    );

    // Dapatkan aggregated data scopes untuk user saat ini
    // Returns: { "KdSatker": ["010001", "010002"], "KdDept": ["*"] }
    // Fail-open: return empty dictionary jika error
    Task<Dictionary<string, List<string>>> GetUserScopes(
        CancellationToken ct = default
    );
}

Filters

KemenkeuAuthorizationFilter

Pre-request filter yang memvalidasi akses endpoint via IAM /iam-agent/authorize.

  • Otomatis terdaftar saat builder.AddKemenkeuIam()
  • Skip endpoint tanpa [KemenkeuAuthorize]
KemenkeuSieveFilter

Post-response filter yang me-redact field sensitif berdasarkan policy.

  • Otomatis terdaftar saat builder.AddKemenkeuIam()
  • Skip endpoint tanpa [KemenkeuSieve]
  • Field yang di-redact di-set menjadi null (bukan "***" atau string lain)
  • Bekerja dengan JsonIgnoreCondition.WhenWritingNull untuk menyembunyikan field dari response

Note: KemenkeuScopeFilter (post-processing scope filter) telah dihapus. Gunakan .ApplyScope() di level query untuk scope-based filtering — lebih efisien dan pagination tetap akurat.

Extension Methods

builder.AddKemenkeuIam()

Mendaftarkan semua services IAM ke DI container.

app.UseKemenkeuIam()

Mengaktifkan header propagation middleware.

app.ProvisionKemenkeuIam()

Mendaftarkan semua routes ke IAM server.

🏗️ Arsitektur

┌─────────────────┐
│  Client App     │
└────────┬────────┘
         │ HTTP Request + Bearer Token
         │
┌────────▼─────────────────────────────┐
│  Your ASP.NET Web API                │
│                                      │
│  [KemenkeuAuthorize]  ◄── Plugin     │
│  [KemenkeuSieve]      ◄── Plugin     │
│  [KemenkeuScope]      ◄── Plugin     │
│                                      │
│  ┌─ Request Phase ────────────────┐  │
│  │ KemenkeuAuthorizationFilter    │  │
│  │ → POST /iam-agent/authorize    │  │
│  └────────────────────────────────┘  │
│                                      │
│  ┌─ Query Phase ──────────────────┐  │
│  │ IKemenkeuScopeContext          │  │
│  │ → GET  /iam-agent/scopes       │  │
│  │ → .ApplyScope() on IQueryable  │  │
│  │ → SQL WHERE IN (...)           │  │
│  └────────────────────────────────┘  │
│                                      │
│  ┌─ Response Phase ───────────────┐  │
│  │ KemenkeuSieveFilter            │  │
│  │ → POST /iam-agent/sieve        │  │
│  └────────────────────────────────┘  │
└────────┬─────────────────────────────┘
         │
┌────────▼────────┐
│   IAM Server    │
│                 │
│ /iam-agent/     │
│   authorize     │
│   sieve         │
│   scopes        │
│                 │
│ /scopetype      │
│ /usergroup      │
└─────────────────┘

Scope Inheritance Model

User Group: "Direktorat A"          (root)
├── Scope: KdDept = "015"
├── Scope: KdUnit = ["01", "02"]
│
└── User Group: "Satker Jakarta"    (child)
    ├── Scope: KdSatker = ["010001"]
    │
    └── Effective scopes (intersection dari parent chain):
        KdDept   = ["015"]          ← inherited dari parent
        KdUnit   = ["01", "02"]     ← inherited dari parent
        KdSatker = ["010001"]       ← defined langsung

User juga di "Tim Khusus":
├── Scope: KdDept = ["*"]           ← wildcard = semua departemen
├── Scope: KdSatker = ["020001"]

Final scopes (union antar group):
  KdDept   = ["*"]                  ← wildcard menang
  KdUnit   = ["01", "02"]           ← dari Satker Jakarta
  KdSatker = ["010001", "020001"]   ← union kedua group

📄 License

Kemenkeu Internal Use Only

🤝 Support

Untuk pertanyaan atau issue, hubungi tim IAM Kemenkeu.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.13 240 9/7/2026
1.0.12 140 9/3/2026
1.0.11 284 7/12/2026
1.0.10 165 6/30/2026
1.0.9 223 6/26/2026
1.0.8 201 6/12/2026
1.0.7 219 5/12/2026
1.0.6 120 5/12/2026
1.0.5 117 5/12/2026
1.0.4 130 5/12/2026
1.0.3 176 4/7/2026
1.0.2 151 3/3/2026
1.0.1 163 2/13/2026
1.0.0 137 2/13/2026