Skip to main content

Security Architecture: Identity, SSO, Authorization, Audit & Secure Deployment

A simulated corporate sign-in that hands the application a set of claims, a ClaimsMapper that turns them into the application's own roles, a PermissionService.Demand that checks the tenant guard first and the role store second, an audit log screen where every Demand lands — granted or denied — and the failure path: an enabled Export button that the service refuses anyway, with Missing permission: ExportData written to the trail.

Around that spine the lab adds tenant-role enforcement, export approval with separation of duties, safe HTML rendering of an untrusted customer note, and a security hardening checklist.

There is no real identity provider, no password field and no credential of any kind in this application. The sign-in gate picks which identity the provider should assert and shows the claim list that crosses the boundary. Nothing here is deployed anywhere.

Run it

cd "LearnWisej-Samples/Enterprise Wisej.NET Course\Module 10\EnterpriseOps"
dotnet run -f net10.0 --urls http://localhost:5210

Then open http://localhost:5210. (Visual Studio: open EnterpriseOps.slnx, press F5 — the port is in Properties/launchSettings.json.) Requirements: .NET 10 SDK and the Wisej-4 4.1.0 NuGet package.

The screen

The sign-in gate (SignInGate) opens first — before any data is loaded. Pick an identity from the corporate directory (its claims are listed on the right) and press Sign in, or Stay signed out to leave the screen locked. To act as a second person at the same time, open the app in another browser or a private window (a new session gets its own gate).

AuditLogPage ("EnterpriseOps — Work queue + audit"):

AreaWhat it holds
Work queue (top left)dgvWorkOrders, ✔ Approve (btnApprove), Approve pending export (btnApproveExport), ⤓ Export data (btnExport), and the result banner lblBanner
Customer note (top right)the latest note on WO-1002, rendered by NoteRenderService with AllowHtml = false — the payload's tags show as characters
Audit log — sensitive commands (bottom)the three filters User / Permission / Result, dgvAudit (Time · User · Tenant · Permission demanded · Result · Detail) and the dark status bar lblStatusBar

Approve and Approve pending export appear only when Has says the caller holds the permission. Export data is shown to every signed-in user: ExportService demands ExportData itself.

The directory to sign in as

AccountTenantRoleUse it for
m.weberfabrikamManagerapprovals, and requesting an export
l.romerofabrikamTechnicianthe denied export
j.kimfabrikamAuditorreading the whole trail; releasing someone else's export
d.singhfabrikamAdminthe separation-of-duties refusal
svc.importfabrikamServiceAccounta non-human caller with edit rights only
ana.opscontosoManageranother tenant — sees contoso rows only
t.novakfabrikam(none)authenticated, entitled to nothing

What to click

#Signed in asActionWhat you should see
1l.romero⤓ Export datafailure (the walkthrough): red banner "You don't have permission to export data."; status bar UnauthorizedAccessException — Missing permission: ExportData · audited · nothing exported; the denial is the first row of the audit log
2anyResult: DENIED filteronly refusals, including the seeded j.kim · ExportData · DENIED
3m.weber✔ ApproveWO-… approved; two audit rows — ApproveWorkOrders · OK (the decision) and ApproveWorkOrder · OK (the change), same correlation id
4m.weber⤓ Export data36 rows is above the 25-row threshold: amber banner, ExportData · PENDING · awaiting a second approver
5d.singh, with no other export pending⤓ Export data, then Approve pending export"An export cannot be approved by the person who requested it."ApproveExport · DENIED · separation of duties
6j.kim, in a second browser while an export is pendingApprove pending exportreleased: ApproveExport · OK and ExportData · OK · completed after approval
7t.novaksign inamber banner naming the unmapped group CorpVPN-Users, an empty queue because Demand(ViewWorkOrders) refused

Pending exports are shared by every session of the tenant (like the audit log), so the requester and the approver can be two people in two browsers.

Where things live

Module 10/
└─ EnterpriseOps/
├─ Domain/ WorkOrder · WorkOrderStatus · Priority · Tenant · WorkQueueRow · WorkOrderNote
├─ Security/ SecurityArchitecturePatterns.cs · PermissionService · RolePermissionStore · TenantGuard
│ ClaimsMapper · SsoIdentityProvider · SessionContext · CommandContext · Role
│ AuditLog · HtmlText · HardeningChecklist
├─ Services/ SignInService · WorkOrderService · ExportService · AuditQueryService
│ NoteRenderService · ServiceRegistry · CommandResult
├─ Data/ SeedData · InMemoryWorkOrderRepository · InMemoryNoteStore
├─ Diagnostics/ ActivityTrace — server-side log (System.Diagnostics.Trace)
├─ UI/ AuditLogPage.cs / .Designer.cs · SignInGate.cs / .Designer.cs
├─ docs/ the five deliverables + identity-flow.svg
├─ Program.cs session composition root — creates the graph, opens the page, signs nobody in
└─ Startup.cs Kestrel host (app.UseWisej(); *.json is never served)

Data: 40 in-memory work orders (36 fabrikam, 4 contoso), 3 customer notes on WO-1002 (the third carries an injection payload), and 12 seeded audit entries including the five.