Claims & Policies

Define claim-based authorization rules and enforce them per route. A policy maps to required claim type(s) + allowed value(s). A route declares an -AuthorizationPolicy and Kestrun validates the authenticated principal’s claims before executing the script block.

Full source

File: pwsh/tutorial/examples/8.7-Claims-Policies.ps1

<#
    Sample: Claims & Policies Authorization
    Purpose: Build claim-based policies and enforce them on routes after Basic auth.
    File:    8.7-Claims-Policies.ps1
    Notes:   Demonstrates issuing claims during authentication.
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)
# 1. Logging
New-KrLogger | Add-KrSinkConsole | Register-KrLogger -Name 'console' -SetAsDefault | Out-Null

# 2. Server
New-KrServer -Name 'Auth Claims'

# 3. Listener
Add-KrEndpoint -Port $Port -IPAddress $IPAddress -SelfSignedCert


# 5. Build policy set
$claimConfig = New-KrClaimPolicy |
    Add-KrClaimPolicy -PolicyName 'CanRead' -ClaimType 'can_read' -AllowedValues 'true' |
    Add-KrClaimPolicy -PolicyName 'CanWrite' -ClaimType 'can_write' -AllowedValues 'true' |
    Add-KrClaimPolicy -PolicyName 'CanDelete' -ClaimType 'can_delete' -AllowedValues 'true' |
    Build-KrClaimPolicy

# 6. Basic auth + issue claims for admin
Add-KrBasicAuthentication -Name 'PolicyBasic' -Realm 'Claims' -AllowInsecureHttp -ScriptBlock {
    param($Username, $Password)
    Write-KrLog -Level Information -Message 'Basic Authentication: User {user} is trying to authenticate.' -Values $Username
    if ($Username -eq 'admin' -and $Password -eq 'password') {
        $true
    } else {
        $false
    }
} -IssueClaimsScriptBlock {
    param($Identity)
    if ($Identity -eq 'admin') {
        return (
            Add-KrUserClaim -ClaimType 'can_read' -Value 'true' |
                Add-KrUserClaim -ClaimType 'can_write' -Value 'true'
        )
    }
} -ClaimPolicyConfig $claimConfig

# 7. Finalize configuration
Enable-KrConfiguration

# 8. Map policy-protected routes
Add-KrRouteGroup -Prefix '/policy' -AuthorizationSchema 'PolicyBasic' {
    Add-KrMapRoute -Verbs Get -Pattern '/read' -AuthorizationPolicy 'CanRead' -ScriptBlock { Write-KrTextResponse 'Read OK' }
    Add-KrMapRoute -Verbs Get -Pattern '/write' -AuthorizationPolicy 'CanWrite' -ScriptBlock { Write-KrTextResponse 'Write OK' }
    Add-KrMapRoute -Verbs Get -Pattern '/delete' -AuthorizationPolicy 'CanDelete' -ScriptBlock { Write-KrTextResponse 'Delete OK' }
}

# 9. Start server
Start-KrServer -CloseLogsOnExit


Step-by-step

  1. Logger: (Sample defines a console logger as default; omitted here for brevity.)
  2. Server: Create server named “Auth Claims”.
  3. Listener: Add HTTPS loopback listener with self-signed cert (port 5000).
  4. Runtime: Add PowerShell runtime.
  5. Policies: Build claim policy config using New-KrClaimPolicy piped through multiple Add-KrClaimPolicy calls (CanRead, CanWrite, CanDelete) then Build-KrClaimPolicy.
  6. Authentication: Configure Basic auth (Add-KrBasicAuthentication) with validation script. If credentials succeed return $true; otherwise $false.
  7. Issue claims: Use -IssueClaimsScriptBlock to emit user claims (admin gets read/write only).
  8. Finalize: Apply pipeline via Enable-KrConfiguration.
  9. Group: Secure routes with Add-KrRouteGroup -AuthorizationSchema 'PolicyBasic' -Prefix '/policy'.
  10. Routes: Add route handlers each specifying an -AuthorizationPolicy.
  11. Start: Run the server with Start-KrServer.

Policy structure

Each Add-KrClaimPolicy defines:

  • PolicyName: symbolic name used by routes (e.g., CanRead).
  • ClaimType: the claim key to check (e.g., can_read).
  • AllowedValues: one or more acceptable values (here just 'true').

All claim requirements for a single policy must pass (logical AND if future enhancements add multiple claims per policy).

Claim issuance

The -IssueClaimsScriptBlock receives the identity after successful authentication. It returns one or more Add-KrUserClaim objects piped together. In the sample only admin receives can_read and can_write; no claim is issued for can_delete, so the Delete policy will fail for this user.

Auth vs authorization codes

  • 401 Unauthorized: Authentication failed or missing (e.g., wrong Basic credentials).
  • 403 Forbidden: Authenticated but lacks required claim/policy.

Try it

# Helper: build Basic header for admin
$b = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:password'))

# 1) Read (should succeed)
Invoke-WebRequest https://127.0.0.1:5000/policy/read -Headers @{ Authorization = $b } -SkipCertificateCheck | Select -Expand Content

# 2) Write (should succeed; claim can_write=true)
Invoke-WebRequest https://127.0.0.1:5000/policy/write -Headers @{ Authorization = $b } -SkipCertificateCheck | Select -Expand Content

# 3) Delete (should 403; no can_delete claim)
try { Invoke-WebRequest https://127.0.0.1:5000/policy/delete -Headers @{ Authorization = $b } -SkipCertificateCheck -ErrorAction Stop }
catch { $_.Exception.Response.StatusCode }

# 4) Missing/invalid credentials (401 then 403 scenario)
$bad = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('user:wrong'))
try { Invoke-WebRequest https://127.0.0.1:5000/policy/read -Headers @{ Authorization = $bad } -SkipCertificateCheck -ErrorAction Stop }
catch { $_.Exception.Response.StatusCode }

References

Troubleshooting

Symptom Cause Fix / Action
403 Forbidden Required claim missing Check issue script; confirm claim type/value
401 then 403 sequence First request unauthenticated Add / correct Basic header
Policy always failing Policy name mismatch in route Match -AuthorizationPolicy with PolicyName
All routes 401 Basic auth script returns $false Verify credentials logic
Delete unexpectedly OK Claim accidentally issued Inspect -IssueClaimsScriptBlock output
Write unexpectedly 403 Claim type spelling mismatch Align claim type in policy vs issued claims

Security notes

  • Basic auth transmits credentials base64-encoded — always use HTTPS (self-signed is fine locally).
  • Avoid hard-coded credentials in production; integrate with a user store.
  • Claims represent authorization decisions; avoid over-granting (principle of least privilege).
  • Log authentication attempts (sample uses Write-KrLog). Consider separating audit sink.
  • Do not leak claim names or sensitive values in error details returned to clients.

Previous / Next