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
<PackageReference Include="Kemenkeu.IAM.Plugin" Version="1.0.13" />
<PackageVersion Include="Kemenkeu.IAM.Plugin" Version="1.0.13" />
<PackageReference Include="Kemenkeu.IAM.Plugin" />
paket add Kemenkeu.IAM.Plugin --version 1.0.13
#r "nuget: Kemenkeu.IAM.Plugin, 1.0.13"
#:package Kemenkeu.IAM.Plugin@1.0.13
#addin nuget:?package=Kemenkeu.IAM.Plugin&version=1.0.13
#tool nuget:?package=Kemenkeu.IAM.Plugin&version=1.0.13
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 AndaBaseUri: URL IAM serverAuthorization: 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
nulldi 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:
- Intersection dari root ke leaf dalam satu rantai user group (parent → child)
- 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
Authorizationtidak 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:AuthorizationdanIAM: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/scopestidak 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 SQLWHERE 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.WhenWritingNulluntuk 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 | Versions 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. |
-
net10.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.0)
- Microsoft.AspNetCore.HeaderPropagation (>= 10.0.0)
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 |