Cookies
Maintain user sessions with a login form + cookie session authentication.
Full source
File: pwsh/tutorial/examples/8.5-Cookies.ps1
<#
Sample: Cookies Authentication
Purpose: Demonstrates form login + cookie session for subsequent authorized requests.
File: 8.5-Cookies.ps1
Notes: Uses HTTP (not HTTPS) for simplicity; secure cookie flags recommended with TLS.
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# 1. Logging
New-KrLogger |
Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -Name 'console' -SetAsDefault | Out-Null
# 2. Server
New-KrServer -Name 'Auth Cookies'
# 3. Listener
Add-KrEndpoint -Port $Port -IPAddress $IPAddress -SelfSignedCert
# 5. Define cookie builder
# 6. Register cookie auth scheme
New-KrCookieBuilder -Name 'KestrunAuth' -HttpOnly -SecurePolicy Always -SameSite Strict |
Add-KrCookiesAuthentication -Name 'Cookies' -LoginPath '/cookies/login' -LogoutPath '/cookies/logout' -AccessDeniedPath '/cookies/denied' `
-SlidingExpiration -ExpireTimeSpan (New-TimeSpan -Minutes 30)
# 7. Finalize configuration
Enable-KrConfiguration
# 8. Login form route
Add-KrMapRoute -Verbs Get -Pattern '/cookies/login' -ScriptBlock {
Write-KrTextResponse -InputObject @'
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form method='post' action='/cookies/login'>
<label>
Username:
<input type='text' name='username' required />
</label><br/>
<label>
Password:
<input type='password' name='password' required />
</label><br/>
<button type='submit'>Log In</button>
</form>
</body>
</html>
'@ -ContentType 'text/html'
}
# 9. Login route (issues cookie)
Add-KrMapRoute -Verbs Post -Pattern '/cookies/login' -ScriptBlock {
$form = $Context.Request.Form
if ($form['username'] -eq 'admin' -and $form['password'] -eq 'secret') {
$principal = Invoke-KrCookieSignIn -Scheme 'Cookies' -Name $form['username'] -PassThru
Write-KrLog -Level Information -Message 'User {user} signed {principal} in with Cookies authentication.' -Values $form['username'], $principal
Write-KrJsonResponse @{ success = $true }
} else {
Write-KrJsonResponse @{ success = $false } -StatusCode 401
}
}
# 10. Protected route group requiring cookie auth
Add-KrRouteGroup -Prefix '/cookies' -AuthorizationSchema 'Cookies' {
Add-KrMapRoute -Verbs Get -Pattern '/logout' -ScriptBlock {
Invoke-KrCookieSignOut -Scheme 'Cookies' -Redirect
}
# 9. Protected route requiring cookie
Add-KrMapRoute -Verbs Get -Pattern '/hello' -ScriptBlock {
$user = $Context.User.Identity.Name
Write-KrTextResponse -InputObject "Welcome, $user! You are authenticated by Cookies Authentication." -ContentType 'text/plain'
}
}
# 11. Start server
Start-KrServer -CloseLogsOnExit
Step-by-step
- Logger: Build and register a console logger at Debug (sets as default so framework/server logs are visible).
- Server: Create a named server (“Auth Cookies”).
- Listener: Add HTTPS loopback listener on 5000 with a self-signed certificate (
-SelfSignedCert). - Runtime: Add PowerShell runtime so your script routes can execute.
- Cookie builder: Configure cookie flags via
New-KrCookieBuilder(-HttpOnly,-SecurePolicy Always,-SameSite Strict). - Auth scheme: Pipe builder into
Add-KrCookiesAuthenticationgiving scheme name, login/logout/denied paths, sliding expiration & 30‑minute lifespan. - Enable: Call
Enable-KrConfigurationto finalize the server pipeline. - Login form (GET): Serve an HTML form at
/cookies/login. - Login (POST): Validate credentials, sign in with
Invoke-KrCookieSignIn(returns principal when-PassThru), write audit log, respond JSON. - Protected group: Use
Add-KrRouteGroup -AuthorizationSchema 'Cookies'to secure/cookies/logoutand/cookies/hello. - Start: Run the server with
Start-KrServer.
Sign-in mechanics
Invoke-KrCookieSignIn issues the auth cookie under the configured scheme. When using -PassThru you receive the principal (claims identity) allowing you to enrich logs or respond with user info.
Sliding expiration
The -SlidingExpiration flag renews the cookie lifetime on active use (bounded by any absolute expiry you set). Here -ExpireTimeSpan (New-TimeSpan -Minutes 30) establishes a 30‑minute idle timeout.
Route group enforcement
Any route inside the group inherits the authorization scheme. Unauthenticated requests get redirected (for login path) or a 401/403 depending on the scheme’s configuration.
Try it
# 1) Fetch login form (optional)
Invoke-WebRequest -Uri https://127.0.0.1:5000/cookies/login -SkipCertificateCheck | Select -Expand RawContent | Select-String 'Login'
# 2) Post credentials (stores session in variable)
Invoke-WebRequest -Uri https://127.0.0.1:5000/cookies/login -Method Post -SkipCertificateCheck -Body @{username='admin';password='secret'} -SessionVariable authSession
# (Optional) Inspect cookie
$authSession.Cookies | Format-Table -Auto
# 3) Access protected route
Invoke-WebRequest -Uri https://127.0.0.1:5000/cookies/hello -SkipCertificateCheck -WebSession $authSession | Select -Expand Content
# 4) Logout
Invoke-WebRequest -Uri https://127.0.0.1:5000/cookies/logout -SkipCertificateCheck -WebSession $authSession | Select -Expand RawContent
References
- Register-KrLogger
- Write-KrLog
- New-KrCookieBuilder
- Add-KrCookiesAuthentication
- Invoke-KrCookieSignIn
- Invoke-KrCookieSignOut
- Add-KrMapRoute
- Add-KrRouteGroup
- New-KrServer
- Add-KrEndpoint
- Enable-KrConfiguration
- Start-KrServer
- Set-KrLoggerLevel
- Add-KrSinkConsole
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 401 after login | Session not stored | Reuse same WebRequestSession |
| Cookie missing | Secure / HTTPS mismatch | Use HTTPS or adjust SecurePolicy |
| Logout not working | Using new session | Reuse session for logout route |
| Login always fails | Credential check logic | Inspect form parsing |
Security notes
- Demo credentials (
admin/secret) are hard-coded for illustration — use a proper user store & password hashing (e.g., PBKDF2, bcrypt, Argon2). - Always deploy behind TLS; secure cookies (
-SecurePolicy Always) should not be downgraded in production. - Consider
SameSite=Laxif third-party login flows or cross-site POST callbacks are required. - Audit events: treat
Write-KrLogentries at sign-in/sign-out as security logs; restrict file and console exposure as appropriate. - Avoid leaking sensitive claims to the client; only return minimal JSON.
Previous / Next
- Previous: JWT Tokens
- Next: Windows Authentication