Multiple Schemes

Combine several authentication mechanisms (Basic, API Key, JWT Bearer) in a single server and allow routes to accept one of several schemes. This pattern lets you:

  • Gradually migrate between auth types (e.g., Basic -> JWT) without breaking clients.
  • Support automation (API key) and interactive users (Basic) simultaneously.
  • Offer short-lived bearer tokens while retaining a fallback credential.

When multiple schemes are specified in -AuthorizationSchema, the request is authorized if ANY scheme succeeds.

Full source

File: pwsh/tutorial/examples/8.8-Multiple-Schemes.ps1

<#
        Sample: Multiple Authentication Schemes
        Purpose: Combine Basic, API Key, and JWT Bearer schemes in one server.
        File:    8.8-Multiple-Schemes.ps1
        Notes:   Shows grouping routes and applying distinct schemes per endpoint.
#>
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 Multi'

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


# 5. Basic auth scheme
Add-KrBasicAuthentication -Name 'BasicPS' -Realm 'Multi' -AllowInsecureHttp -ScriptBlock {
    param($Username, $Password) # Plain text for tutorial simplicity
    $Username -eq 'admin' -and $Password -eq 'password'
}

# 6. API key scheme
Add-KrApiKeyAuthentication -Name 'KeySimple' -AllowInsecureHttp -HeaderName 'X-Api-Key' -ExpectedKey 'my-secret-api-key'

# 7. JWT bearer scheme setup
$builder = New-KrJWTBuilder |
    Add-KrJWTIssuer -Issuer 'KestrunApi' |
    Add-KrJWTAudience -Audience 'KestrunClients' |
    Protect-KrJWT -HexadecimalKey '6f1a1ce2e8cc4a5685ad0e1d1f0b8c092b6dce4f7a08b1c2d3e4f5a6b7c8d9e0' -Algorithm HS256
$res = Build-KrJWT -Builder $builder
$validation = $res | Get-KrJWTValidationParameter
Add-KrJWTBearerAuthentication -Name 'Bearer' -ValidationParameter $validation

# 8. Finalize configuration
Enable-KrConfiguration

# 9. Map routes with different schemes
Add-KrRouteGroup -Prefix '/secure' {
    # Pure Basic route
    Add-KrMapRoute -Verbs Get -Pattern '/basic' -AuthorizationSchema 'BasicPS' -ScriptBlock {
        Write-KrTextResponse 'Basic OK'
    }

    # API Key OR Basic (either accepted: order matters only for challenge response)
    Add-KrMapRoute -Verbs Get -Pattern '/key' -AuthorizationSchema 'KeySimple', 'BasicPS' -ScriptBlock {
        Write-KrTextResponse 'Key OK'
    }

    # JWT OR Basic
    Add-KrMapRoute -Verbs Get -Pattern '/jwt' -AuthorizationSchema 'Bearer', 'BasicPS' -ScriptBlock {
        Write-KrTextResponse 'JWT OK'
    }

    # Issue a JWT using Basic (to demonstrate acquiring a token for /secure/jwt)
    Add-KrMapRoute -Verbs Get -Pattern '/token/new' -AuthorizationSchema 'BasicPS' -ScriptBlock {
        $user = $Context.User.Identity.Name
        $build = Copy-KrJWTTokenBuilder -Builder $builder |
            Add-KrJWTSubject -Subject $user |
            Add-KrJWTClaim -UserClaimType Name -Value $user |
            Add-KrJWTClaim -UserClaimType Role -Value 'admin' |
            Build-KrJWT
        $token = $build | Get-KrJWTToken
        Write-KrJsonResponse @{ access_token = $token; expires = $build.Expires }
    }
}

# 10. Start server
Start-KrServer -CloseLogsOnExit


Step-by-step

  1. Logging & server: configure a console logger and create the server.
  2. Listener: bind loopback HTTPS (self-signed cert for demo). Use -SkipCertificateCheck in test clients.
  3. Runtime: enable PowerShell runtime.
  4. Basic scheme: password-based initial authentication (BasicPS).
  5. API Key scheme: header-based key (X-Api-Key) for automation (KeySimple).
  6. JWT builder: configure issuer, audience, protection (HMAC), and build once for validation parameters.
  7. JWT bearer scheme: register bearer validation under name Bearer.
  8. Enable configuration: finalize server/auth registration.
  9. Secure group: under /secure define routes with different accepted schemes:

    • /secure/basic (Basic only)
    • /secure/key (API Key OR Basic)
    • /secure/jwt (JWT OR Basic)
    • /secure/token/new (Basic only) issues a JWT for subsequent bearer access.
  10. Start server.

Multiple scheme semantics

Add-KrMapRoute -AuthorizationSchema 'KeySimple','BasicPS' means:

  1. Try API Key validation.
  2. If it fails (no key or invalid), attempt Basic.
  3. If all listed schemes fail, the route returns 401 with the challenge header(s) for the first failing scheme.

Order matters for which challenge header the client sees first; place your preferred or most common scheme first.

Try it

Assumptions: server listening on https://127.0.0.1:5000 with self-signed cert.

# Basic auth header
$basic = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:password'))

# 1. Basic-only route
Invoke-RestMethod https://127.0.0.1:5000/secure/basic -SkipCertificateCheck -Headers @{ Authorization = $basic }

# 2. API Key OR Basic route (use API key)
Invoke-RestMethod https://127.0.0.1:5000/secure/key -SkipCertificateCheck -Headers @{ 'X-Api-Key' = 'my-secret-api-key' }

# 3. API Key OR Basic route (fallback to Basic)
Invoke-RestMethod https://127.0.0.1:5000/secure/key -SkipCertificateCheck -Headers @{ Authorization = $basic }

# 4. JWT OR Basic route (initially with Basic)
Invoke-RestMethod https://127.0.0.1:5000/secure/jwt -SkipCertificateCheck -Headers @{ Authorization = $basic }

# 5. Obtain JWT via token issuance (Basic required)
$token = (Invoke-RestMethod https://127.0.0.1:5000/secure/token/new -SkipCertificateCheck -Headers @{ Authorization = $basic }).access_token

# 6. Call JWT route with Bearer token
Invoke-RestMethod https://127.0.0.1:5000/secure/jwt -SkipCertificateCheck -Headers @{ Authorization = "Bearer $token" }

# 7. (Optional) Inspect token claims
Get-KrJWTInfo -Token $token | Format-List Issuer, Audience, Expires, Claims

If you supply both a valid API Key and Basic header, the first listed scheme that succeeds authorizes the request.

References

Troubleshooting

Symptom Cause / Details Fix / Guidance
401 (Basic) Wrong password / header missing Recreate Authorization: Basic ... header
401 (API Key) Missing/incorrect X-Api-Key Provide correct key value
401 (Bearer) Missing / expired / invalid JWT Obtain fresh token via /secure/token/new
Wrong challenge header shown Less preferred scheme listed first Reorder names in -AuthorizationSchema
Claims missing in JWT Builder reused without copy Use Copy-KrJWTTokenBuilder before adding per-request claims
Token not expiring No validity limiter configured Add Limit-KrJWTValidity -Minutes <n> to builder
API Key exposed in logs Verbose logging includes headers Redact or lower log verbosity for headers

Previous / Next

See also: Basic · API Key · JWT · JWT Guide