JSON Web Tokens (JWT)
End-to-end guide for issuing and validating JWT bearer tokens with Kestrun’s PowerShell cmdlets.
Focus areas:
- Token builder pipeline (
New-KrJWTBuilderetc.) - Adding identity & custom claims
- Protecting payload & headers (signing / integrity)
- Validating tokens on incoming requests
- Renew / refresh patterns
- Security, key management, and common pitfalls
The PowerShell cmdlets wrap a lightweight internal builder abstraction, not a heavy framework w/ opaque defaults.
1. Concepts
| Concept | Description |
|---|---|
| Builder | Fluent pipeline object that collects header/payload/meta until Build-KrJWT |
| Protection | HMAC (HS256/384/512) or other algorithms configured via Protect-KrJWT |
| Claims | Standard (sub, name, role) or custom application claims |
| Validation | Parameters (issuer, audience, signing key, clock skew) |
| Renewal | Creating a new token from current context / existing claims |
| Lifetime | Controlled via Limit-KrJWTValidity or claim-specific additions |
| Not Before | Start time gating via Limit-KrJWTNotBefore |
2. Typical workflow
flowchart LR
A[New-KrJWTBuilder] --> B(Add-KrJWTIssuer)
B --> C(Add-KrJWTAudience)
C --> D(Protect-KrJWT)
D --> E(Limit-KrJWTValidity / NotBefore?)
E --> F(Add-KrJWTSubject / Claims)
F --> G(Build-KrJWT)
G --> H(Get-KrJWTToken)
H --> I(Get-KrJWTValidationParameter) --> J(Add-KrJWTBearerAuthentication)
J --> K(Protected Routes)
3. Minimal issue + validate example
# Build configuration
$builder = New-KrJWTBuilder |
Add-KrJWTIssuer -Issuer 'DemoIssuer' |
Add-KrJWTAudience -Audience 'DemoAudience' |
Protect-KrJWT -HexadecimalKey ( -join ((0..63) | ForEach-Object { '{0:x}' -f (Get-Random -Max 16) }) ) -Algorithm HS256 |
Limit-KrJWTValidity -Minutes 30
$built = $builder | Add-KrJWTSubject -Subject 'user1' | Add-KrJWTClaim -UserClaimType Name -Value 'user1' | Build-KrJWT
$token = $built | Get-KrJWTToken
# Prepare validation parameters (for bearer scheme)
$validation = $built | Get-KrJWTValidationParameter
Add-KrJWTBearerAuthentication -Name 'Bearer' -ValidationParameter $validation
4. Detailed cmdlet pipeline
4.1 Create builder
New-KrJWTBuilder / legacy alias New-KrJWTTokenBuilder returns stateful builder object.
4.2 Issuer & audiences
Add one issuer + one or more audiences:
$b = New-KrJWTBuilder |
Add-KrJWTIssuer -Issuer 'IssuerA' |
Add-KrJWTAudience -Audience 'ClientA' |
Add-KrJWTAudience -Audience 'ClientB'
4.3 Protect (sign) the token
Symmetric HMAC (HS256):
$b = $b | Protect-KrJWT -HexadecimalKey 'a0b1c2d3e4f5061728394a5b6c7d8e9fa1b2c3d4e5f60718293a4b5c6d7e8f90' -Algorithm HS256
Or protect only payload (header left default) via Protect-KrJWTPayload (rare; usually use full protection).
4.4 Lifetime & temporal claims
$b = $b |
Limit-KrJWTValidity -Minutes 15 |
Limit-KrJWTNotBefore -Seconds 5 # token not valid for first 5 seconds
4.5 Subject & claims
Standard helpers:
$b = $b |
Add-KrJWTSubject -Subject 'user42' |
Add-KrJWTClaim -UserClaimType Name -Value 'user42' |
Add-KrJWTClaim -UserClaimType Role -Value 'admin'
Custom claim type/value:
$b = $b | Add-KrJWTClaim -ClaimType 'region' -Value 'us-east-1'
4.6 Build & extract
$result = $b | Build-KrJWT
$jwtString = $result | Get-KrJWTToken
$result exposes: Issuer, Audience(s), Expires, NotBefore, Algorithm, Claims, Header, Payload.
4.7 Validation parameters
$validation = $result | Get-KrJWTValidationParameter
Add-KrJWTBearerAuthentication -Name 'Bearer' -ValidationParameter $validation
4.8 Copy & mutate per-request
Copy-KrJWTTokenBuilder -Builder $b |
Add-KrJWTSubject -Subject $Context.User.Identity.Name |
Add-KrJWTClaim -UserClaimType Role -Value 'admin' |
Build-KrJWT | Get-KrJWTToken
4.9 Updating from existing context
Update-KrJWT -FromContext re-creates a token using the existing principal (subject + claims) while applying the same signing key & lifetime rules.
$refresh = $b | Update-KrJWT -FromContext
4.10 Introspection
Decode without validating signature (dev diagnostics):
Get-KrJWTInfo -Token $jwtString | Format-List *
Validate (signature + lifetime):
Test-KrJWT -Token $jwtString -ValidationParameter $validation
5. Putting it together (issue & renew snippet)
Add-KrMapRoute -Verbs Get -Pattern '/token/new' -AuthorizationSchema 'BasicInit' -ScriptBlock {
$build = Copy-KrJWTTokenBuilder -Builder $jwtBuilder |
Add-KrJWTSubject -Subject $Context.User.Identity.Name |
Add-KrJWTClaim -UserClaimType Name -Value $Context.User.Identity.Name |
Add-KrJWTClaim -UserClaimType Role -Value 'admin' |
Build-KrJWT
Write-KrJsonResponse @{ access_token = ($build | Get-KrJWTToken); expires = $build.Expires }
}
Add-KrMapRoute -Verbs Get -Pattern '/token/renew' -AuthorizationSchema 'Bearer' -ScriptBlock {
$newToken = $jwtBuilder | Update-KrJWT -FromContext
Write-KrJsonResponse @{ access_token = $newToken }
}
6. Security & best practices
- Generate strong keys (32+ bytes for HS256). Store outside source: environment variable, secret manager, Key Vault.
- Rotate keys: maintain overlapping validation (old accept + new issue) windows when rotating.
- Short lifetimes reduce replay risk; pair with refresh mechanism if needed.
- Limit claim size & PII: tokens are often logged/forwarded by proxies. Avoid sensitive identifiers.
- Always validate: issuer, audience, signature, expiration, not-before.
- Use HTTPS: bearer tokens are bearer — possession = access.
- Avoid embedding authorization decisions (e.g., granular permissions) directly; prefer simple roles + server-side policy.
- Clock skew: if clients show premature expiry, add server skew tolerance (future cmdlet may expose this).
7. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
401 Bearer | Missing / invalid token | Provide Authorization: Bearer <token> |
| Token expired early | Clock skew / short lifetime | Increase validity or adjust skew |
| Wrong audience error | Audience mismatch | Add correct audience when building / validating |
| Signature invalid | Key mismatch / algorithm mismatch | Ensure same key & algorithm; check hex length |
| Claims missing | Builder reused (claims overwritten) | Use Copy-KrJWTTokenBuilder for per-request mutation |
| Renew returns old data | Not using context-based update | Add Update-KrJWT -FromContext or new claim pipeline |
8. Cmdlet reference (grouped)
| Category | Cmdlets |
|---|---|
| Builder | New-KrJWTBuilder (New-KrJWTTokenBuilder), Copy-KrJWTTokenBuilder |
| Identity | Add-KrJWTSubject, Add-KrJWTClaim |
| Protection | Protect-KrJWT, Protect-KrJWTPayload |
| Lifetime | Limit-KrJWTValidity, Limit-KrJWTNotBefore |
| Build/Extract | Build-KrJWT, Get-KrJWTToken, Get-KrJWTInfo |
| Validate | Get-KrJWTValidationParameter, Test-KrJWT, Add-KrJWTBearerAuthentication |
| Update | Update-KrJWT |
Return to the Guides index.