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
- Logger: (Sample defines a console logger as default; omitted here for brevity.)
- Server: Create server named “Auth Claims”.
- Listener: Add HTTPS loopback listener with self-signed cert (port 5000).
- Runtime: Add PowerShell runtime.
- Policies: Build claim policy config using
New-KrClaimPolicypiped through multipleAdd-KrClaimPolicycalls (CanRead, CanWrite, CanDelete) thenBuild-KrClaimPolicy. - Authentication: Configure Basic auth (
Add-KrBasicAuthentication) with validation script. If credentials succeed return$true; otherwise$false. - Issue claims: Use
-IssueClaimsScriptBlockto emit user claims (admin gets read/write only). - Finalize: Apply pipeline via
Enable-KrConfiguration. - Group: Secure routes with
Add-KrRouteGroup -AuthorizationSchema 'PolicyBasic' -Prefix '/policy'. - Routes: Add route handlers each specifying an
-AuthorizationPolicy. - 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
- New-KrClaimPolicy
- Add-KrClaimPolicy
- Build-KrClaimPolicy
- Add-KrBasicAuthentication
- Add-KrUserClaim
- Add-KrMapRoute
- Add-KrRouteGroup
- New-KrServer
- Add-KrEndpoint
- Enable-KrConfiguration
- Start-KrServer
- Write-KrLog
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
- Previous: Windows Authentication
- Next: Multiple Schemes