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

  1. Logger: Build and register a console logger at Debug (sets as default so framework/server logs are visible).
  2. Server: Create a named server (“Auth Cookies”).
  3. Listener: Add HTTPS loopback listener on 5000 with a self-signed certificate (-SelfSignedCert).
  4. Runtime: Add PowerShell runtime so your script routes can execute.
  5. Cookie builder: Configure cookie flags via New-KrCookieBuilder (-HttpOnly, -SecurePolicy Always, -SameSite Strict).
  6. Auth scheme: Pipe builder into Add-KrCookiesAuthentication giving scheme name, login/logout/denied paths, sliding expiration & 30‑minute lifespan.
  7. Enable: Call Enable-KrConfiguration to finalize the server pipeline.
  8. Login form (GET): Serve an HTML form at /cookies/login.
  9. Login (POST): Validate credentials, sign in with Invoke-KrCookieSignIn (returns principal when -PassThru), write audit log, respond JSON.
  10. Protected group: Use Add-KrRouteGroup -AuthorizationSchema 'Cookies' to secure /cookies/logout and /cookies/hello.
  11. 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

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=Lax if third-party login flows or cross-site POST callbacks are required.
  • Audit events: treat Write-KrLog entries 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