Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Digest Authentication, JWT Authentication, Certificate Management & RFC Compliance #1470

Open
wants to merge 72 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
062cc2d
first drop
mdaneri Jan 29, 2025
ed3a0f5
fixes
mdaneri Jan 30, 2025
71d4747
Adding auth-int
mdaneri Jan 30, 2025
9090b07
add rawdata
mdaneri Jan 30, 2025
3088db2
Fixed tests
mdaneri Jan 30, 2025
b53f07d
Update Cryptography.ps1
mdaneri Jan 30, 2025
4690eff
update documentation
mdaneri Jan 31, 2025
23889e8
Fix markdown
mdaneri Jan 31, 2025
2af345c
Merge branch 'develop' into RFC-7616-Compliance
mdaneri Feb 5, 2025
815ca10
Enhance Authentication: RFC Compliance, JWT Algorithms, and Bearer Qu…
mdaneri Feb 8, 2025
615d4ab
fix tests and
mdaneri Feb 8, 2025
7a8f61a
Merge remote-tracking branch 'upstream/develop' into RFC-7616-Compliance
mdaneri Feb 9, 2025
93fb8ea
fixes
mdaneri Feb 10, 2025
d5fface
added missing header and language entries
mdaneri Feb 10, 2025
71315d5
Merge remote-tracking branch 'upstream/develop' into RFC-7616-Compliance
mdaneri Feb 10, 2025
a148489
work in progress
mdaneri Feb 11, 2025
119d479
working version
mdaneri Feb 12, 2025
e98748a
Merge branch 'develop' into RFC-7616-Compliance
mdaneri Feb 12, 2025
2bc4d84
fix not windows issue
mdaneri Feb 12, 2025
5a2f77f
Update pode.build.ps1
mdaneri Feb 13, 2025
0e3ae54
fix Desktop tests
mdaneri Feb 13, 2025
881b72c
doc update
mdaneri Feb 13, 2025
61072c2
Update Cryptography.Tests.ps1
mdaneri Feb 13, 2025
84bda14
Update Cryptography.Tests.ps1
mdaneri Feb 13, 2025
363168b
Change from PEM cert to PFX and adding 5.1 support
mdaneri Feb 16, 2025
feb7a2d
Merge remote-tracking branch 'upstream/develop' into RFC-7616-Compliance
mdaneri Feb 16, 2025
7368548
updated to adhere the Pode standard for the certificate
mdaneri Feb 16, 2025
0f0c252
updated comments and documentation
mdaneri Feb 16, 2025
852d8c0
fixed tests
mdaneri Feb 16, 2025
6f54b3f
macos fixes
mdaneri Feb 16, 2025
cf107b2
added workaround for .Net Linux issue
mdaneri Feb 17, 2025
74584e9
added header to Get-PodeJwtSigningAlgorithm
mdaneri Feb 17, 2025
fc4ed64
Update Cryptography.ps1
mdaneri Feb 17, 2025
3939983
moved Authentication example inside Authentication folder
mdaneri Feb 17, 2025
9a35551
Added Update-PodeJWT and support for bearer Body token
mdaneri Feb 18, 2025
9fd8589
update build for 5.1
mdaneri Feb 19, 2025
601c1cb
update JWT lifecycle
mdaneri Feb 19, 2025
6add82d
added JWT lifecycle
mdaneri Feb 19, 2025
5bbd552
added certificate management functions
mdaneri Feb 21, 2025
7f06e50
revert pode.build
mdaneri Feb 21, 2025
4b0d09c
fix issue with ephemeral
mdaneri Feb 21, 2025
5fc7a00
fixex
mdaneri Feb 22, 2025
b40d4e8
Merge branch 'develop' into RFC-7616-Compliance
mdaneri Feb 22, 2025
908cdaf
Fix MacOS EphemeralKeySet issue
mdaneri Feb 22, 2025
ecc2729
Merge branch 'RFC-7616-Compliance' of https://github.com/mdaneri/Pode…
mdaneri Feb 22, 2025
7db8ff7
added certificate documentation
mdaneri Feb 22, 2025
04d76a6
Merge remote-tracking branch 'upstream/develop' into RFC-7616-Compliance
mdaneri Feb 22, 2025
6dd871d
added certificate test
mdaneri Feb 23, 2025
c42e497
Merge branch 'develop' into RFC-7616-Compliance
mdaneri Feb 23, 2025
c3c24d8
update Test-PodeCertificate and SSL documentation
mdaneri Feb 23, 2025
2f4e5ce
fix linux mac SSL detection issue
mdaneri Feb 24, 2025
72674fc
update macOS
mdaneri Feb 24, 2025
2df6dfe
fix mac os issue with ssl
mdaneri Feb 24, 2025
f0ca68e
Update JWTAuthentication.Tests.ps1
mdaneri Feb 25, 2025
5758820
update tests
mdaneri Feb 25, 2025
76bab58
Fix Test and build
mdaneri Feb 25, 2025
6116794
test update and fixes
mdaneri Feb 26, 2025
ec6fb18
added digest tests
mdaneri Feb 26, 2025
9f83ecc
Update DigestAuthentication.Tests.ps1
mdaneri Feb 26, 2025
6a42833
added digest check for $QualityOfProtection 'auth-int'
mdaneri Feb 27, 2025
1ef78ac
change parameter Import-PodeCertificate -FilePath to -Path
mdaneri Feb 27, 2025
3789b33
Update Cryptography.ps1
mdaneri Feb 27, 2025
ea89876
fix Export-Certificate
mdaneri Feb 27, 2025
11a574c
new Invoke-WebRequestDigest
mdaneri Feb 28, 2025
044070d
update
mdaneri Feb 28, 2025
c54d0a6
new Digest client module with documentation
mdaneri Feb 28, 2025
e1f4e24
fix tests for 5.1
mdaneri Mar 1, 2025
5d7db37
fix tests
mdaneri Mar 1, 2025
d91b7a5
fix powershell support message
mdaneri Mar 1, 2025
f057eff
Folder reorganization
mdaneri Mar 2, 2025
c030ac2
Update Helpers.ps1
mdaneri Mar 9, 2025
05f73db
Merge remote-tracking branch 'upstream/develop' into RFC-7616-Compliance
mdaneri Mar 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,8 @@ docs/Getting-Started/Samples.md

# Dump Folder
Dump
examples/certs/*-public.pem
examples/certs/*-private.pem
tests/certs/*
/examples/certs
examples/Authentication/certs/*
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,13 @@
"javascript.format.insertSpaceBeforeFunctionParenthesis": false,
"[yaml]": {
"editor.tabSize": 2
},
"markdownlint.config": {
"default": true,
"MD045": false,
"MD033": false,
"MD026": {
"punctuation": ".,;:"
}
}
}
65 changes: 33 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center">
<h1 align="center">
<img src="https://github.com/Badgerati/Pode/raw/develop/images/icon-new.svg?raw=true" width="250" />
</p>
</h1>

[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Badgerati/Pode/master/LICENSE.txt)
[![Documentation](https://img.shields.io/github/v/release/badgerati/pode?label=docs&logo=readthedocs&logoColor=white)](https://badgerati.github.io/Pode)
Expand Down Expand Up @@ -53,36 +53,37 @@ Then navigate to `http://127.0.0.1:8000` in your browser.

## 🚀 Features

* Cross-platform using PowerShell Core (with support for PS5)
* Docker support, including images for ARM/Raspberry Pi
* Azure Functions, AWS Lambda, and IIS support
* OpenAPI specification version 3.0.x and 3.1.0
* OpenAPI documentation with Swagger, Redoc, RapidDoc, StopLight, OpenAPI-Explorer and RapiPdf
* Listen on a single or multiple IP(v4/v6) address/hostnames
* Cross-platform support for HTTP(S), WS(S), SSE, SMTP(S), and TCP(S)
* Host REST APIs, Web Pages, and Static Content (with caching)
* Support for custom error pages
* Request and Response compression using GZip/Deflate
* Multi-thread support for incoming requests
* Inbuilt template engine, with support for third-parties
* Async timers for short-running repeatable processes
* Async scheduled tasks using cron expressions for short/long-running processes
* Supports logging to CLI, Files, and custom logic for other services like LogStash
* Cross-state variable access across multiple runspaces
* Restart the server via file monitoring, or defined periods/times
* Ability to allow/deny requests from certain IP addresses and subnets
* Basic rate limiting for IP addresses and subnets
* Middleware and Sessions on web servers, with Flash message and CSRF support
* Authentication on requests, such as Basic, Windows and Azure AD
* Authorisation support on requests, using Roles, Groups, Scopes, etc.
* Support for dynamically building Routes from Functions and Modules
* Generate/bind self-signed certificates
* Secret management support to load secrets from vaults
* Support for File Watchers
* In-memory caching, with optional support for external providers (such as Redis)
* (Windows) Open the hosted server as a desktop application
* FileBrowsing support
* Localization (i18n) in Arabic, German, Spanish, France, Italian, Japanese, Korean, Polish, Portuguese, and Chinese
- ✅ Cross-platform using PowerShell Core (with support for PS5)
- ✅ Docker support, including images for ARM/Raspberry Pi
- ✅ Azure Functions, AWS Lambda, and IIS support
- ✅ OpenAPI specification version 3.0.x and 3.1.0
- ✅ OpenAPI documentation with Swagger, Redoc, RapidDoc, StopLight, OpenAPI-Explorer and RapiPdf
- ✅ Listen on a single or multiple IP(v4/v6) addresses/hostnames
- ✅ Cross-platform support for HTTP(S), WS(S), SSE, SMTP(S), and TCP(S)
- ✅ Host REST APIs, Web Pages, and Static Content (with caching)
- ✅ Support for custom error pages
- ✅ Request and Response compression using GZip/Deflate
- ✅ Multi-thread support for incoming requests
- ✅ Inbuilt template engine, with support for third-parties
- ✅ Async timers for short-running repeatable processes
- ✅ Async scheduled tasks using cron expressions for short/long-running processes
- ✅ Supports logging to CLI, Files, and custom logic for other services like LogStash
- ✅ Cross-state variable access across multiple runspaces
- ✅ Restart the server via file monitoring, or defined periods/times
- ✅ Ability to allow/deny requests from certain IP addresses and subnets
- ✅ Basic rate limiting for IP addresses and subnets
- ✅ Middleware and Sessions on web servers, with Flash message and CSRF support
- ✅ Authentication on requests, such as Basic, Windows and Azure AD
- ✅ Authorisation support on requests, using Roles, Groups, Scopes, etc.
- ✅ Enhanced authentication support, including Basic, Bearer (with JWT), Certificate, Digest, Form, OAuth2, and ApiKey (with JWT).
- ✅ Support for dynamically building Routes from Functions and Modules
- ✅ Generate/bind self-signed certificates
- ✅ Secret management support to load secrets from vaults
- ✅ Support for File Watchers
- ✅ In-memory caching, with optional support for external providers (such as Redis)
- ✅ (Windows) Open the hosted server as a desktop application
- ✅ FileBrowsing support
- ✅ Localization (i18n) in Arabic, German, Spanish, France, Italian, Japanese, Korean, Polish, Portuguese,Dutch and Chinese

## 📦 Install

Expand Down
4 changes: 2 additions & 2 deletions docs/Tutorials/Authentication/Methods/ApiKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ Start-PodeServer {
}
```

By default, Pode will look for an `X-API-KEY` header in the request. You can change this to Cookie or Query by using the `-Location` parameter. To change the name of what Pode looks for, you can use `-LocationName`.
By default, Pode will look for an `X-API-KEY` header in the request. You can change this to Cookie or Query by using the `-ApiKeyLocation` parameter. To change the name of what Pode looks for, you can use `-LocationName`.

For example, to look for an `appId` query value:

```powershell
Start-PodeServer {
New-PodeAuthScheme -ApiKey -Location Query -LocationName 'appId' | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
New-PodeAuthScheme -ApiKey -ApiKeyLocation Query -LocationName 'appId' | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($key)

# check if the key is valid, and get user
Expand Down
195 changes: 163 additions & 32 deletions docs/Tutorials/Authentication/Methods/Bearer.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,30 @@ Bearer authentication lets you authenticate a user based on a token, with option
Authorization: Bearer <token>
```

!!! note
**`New-PodeAuthScheme -Bearer` is deprecated.** Please use **`New-PodeAuthBearerScheme`**.

## Setup

To start using Bearer authentication in Pode you can use `New-PodeAuthScheme -Bearer`, and then pipe the returned object into [`Add-PodeAuth`](../../../../Functions/Authentication/Add-PodeAuth). The parameter supplied to the [`Add-PodeAuth`](../../../../Functions/Authentication/Add-PodeAuth) function's ScriptBlock is the `$token` from the Authorization token:
To start using Bearer authentication in Pode, call **`New-PodeAuthBearerScheme`**, and then pipe the returned object into [`Add-PodeAuth`](../../../../Functions/Authentication/Add-PodeAuth). The parameter supplied to the [`Add-PodeAuth`](../../../../Functions/Authentication/Add-PodeAuth) function's **ScriptBlock** is the `$token` from the Authorization header.

```powershell
Start-PodeServer {
New-PodeAuthBearerScheme | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# check if the token is valid, and get user

return @{ User = $user }
}
}
```

By default, Pode will look for a token in the **`Authorization`** header, verifying that it starts with the **`Bearer`** tag. You can customize this tag via **`-HeaderTag`**. You can also change the token extraction location to the **query string** using **`-Location Query`**. For the **`-Location query`** the standard tag is **`access_token`**:

```powershell
Start-PodeServer {
New-PodeAuthScheme -Bearer | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
New-PodeAuthBearerScheme -Location Query | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# check if the token is valid, and get user
Expand All @@ -22,13 +39,129 @@ Start-PodeServer {
}
```

By default, Pode will check if the request's header contains an `Authorization` key, and whether the value of that key starts with `Bearer` tag. The `New-PodeAuthScheme -Bearer` function can be supplied parameters to customise the tag using `-HeaderTag`.
**Note:** Per [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750), using the Authorization header is recommended for sending bearer tokens. Query parameters should only be used when headers are not feasible, as query strings may be logged in URLs, potentially exposing sensitive information.

## JWT Support

`New-PodeAuthBearerScheme` supports **JWT authentication** with various security levels and algorithms. Set **`-AsJWT`** to enable JWT validation. Depending on the chosen algorithm, you can specify:

- **HMAC**-based secret keys (`-Secret`)
- **Certificate**-based parameters (`-Certificate`, `-CertificateThumbprint`, `-CertificateName`, `-X509Certificate`, `-SelfSigned`)
- The **RSA padding scheme** (`-RsaPaddingScheme`)
- The **JWT verification mode** (`-JwtVerificationMode`)

### JwtVerificationMode

Defines how aggressively JWT claims should be checked:

- **Strict**: Requires all standard claims:
- `exp` (Expiration Time)
- `nbf` (Not Before)
- `iat` (Issued At)
- `iss` (Issuer)
- `aud` (Audience)
- `jti` (JWT ID)

- **Moderate**: Allows missing `iss` (Issuer) and `aud` (Audience) but still checks expiration (`exp`).
- **Lenient**: Ignores missing `iss` and `aud`, only verifies expiration (`exp`), not-before (`nbf`), and issued-at (`iat`).

### HMAC Example

Here’s an example using **HMAC** (HS256) JWT validation:

```powershell
Start-PodeServer {
New-PodeAuthBearerScheme `
-AsJWT `
-Algorithm 'HS256' `
-Secret (ConvertTo-SecureString "MySecretKey" -AsPlainText -Force) `
-JwtVerificationMode 'Strict' |
Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# validate and decode JWT, then extract user details

return @{ User = $user }
}
}
```

### Certificate-Based Example

For **RSA/ECDSA** JWT validation, you can specify a **certificate** or **thumbprint** instead of a secret key. Pode will infer the appropriate signing algorithms (e.g., RS256, ES256) from the certificate. For instance, using a local **PFX** certificate file:

```powershell
Start-PodeServer {
New-PodeAuthBearerScheme `
-AsJWT `
-Algorithm 'RS256' `
-Certificate "C:\path\to\cert.pfx" `
-CertificatePassword (ConvertTo-SecureString "CertPass" -AsPlainText -Force) `
-JwtVerificationMode 'Moderate' |
Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# validate JWT and extract user

return @{ User = $user }
}
}
```

### Self-Signed Certificate Example

For testing purposes or internal deployments, you can use the **`-SelfSigned`** parameter, which automatically generates an **ephemeral self-signed ECDSA certificate** (ES384) for JWT signing. This avoids the need to manually create and manage certificate files.

#### Example:

```powershell
Start-PodeServer {
New-PodeAuthBearerScheme `
-AsJWT `
-SelfSigned |
Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# validate JWT and extract user

return @{ User = $user }
}
}
```

This is equivalent to manually generating a self-signed ECDSA certificate and passing it via `-X509Certificate`:

```powershell
Start-PodeServer {
$x509Certificate = New-PodeSelfSignedCertificate `
-CommonName 'JWT Signing Certificate' `
-KeyType ECDSA `
-KeyLength 384 `
-CertificatePurpose CodeSigning `
-Ephemeral

New-PodeAuthBearerScheme `
-AsJWT `
-X509Certificate $x509Certificate |
Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# validate JWT and extract user

return @{ User = $user }
}
}
```

Using `-SelfSigned` simplifies setup by automatically handling certificate creation and disposal, making it a convenient choice for local development and testing scenarios.

## Scope Validation

You can also optionally return a `Scope` property alongside the `User`. If you specify any scopes with [`New-PodeAuthScheme`](../../../../Functions/Authentication/New-PodeAuthScheme) then it will be validated in the Bearer's post validator - a 403 will be returned if the scope is invalid.
You can optionally include `-Scope` when creating the scheme. Pode will validate any returned `Scope` from your auth **ScriptBlock** against the scheme’s required scopes. If the scope is invalid, Pode will return 403 (Forbidden).

```powershell
Start-PodeServer {
New-PodeAuthScheme -Bearer -Scope 'write' | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
New-PodeAuthBearerScheme -Scope 'write' | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# check if the token is valid, and get user
Expand All @@ -38,19 +171,21 @@ Start-PodeServer {
}
```



## Middleware

Once configured you can start using Bearer authentication to validate incoming requests. You can either configure the validation to happen on every Route as global Middleware, or as custom Route Middleware.
Once configured, you can instruct Pode to validate every request with Bearer authentication by using **Global Middleware**, or you can require it on individual Routes.

The following will use Bearer authentication to validate every request on every Route:
**Global Middleware Example** – Validate **every** incoming request:

```powershell
Start-PodeServer {
Add-PodeAuthMiddleware -Name 'GlobalAuthValidation' -Authentication 'Authenticate'
}
```

Whereas the following example will use Bearer authentication to only validate requests on specific a Route:
**Route-Specific Example** – Validate only on a certain Route:

```powershell
Start-PodeServer {
Expand All @@ -60,46 +195,42 @@ Start-PodeServer {
}
```

## JWT

You can supply a JWT using Bearer authentication, for more details [see here](../JWT).

## Full Example

The following full example of Bearer authentication will setup and configure authentication, validate the token, and then validate on a specific Route:
Below is a complete example demonstrating Bearer authentication with JWT. It configures a server, sets up JWT validation with a shared secret, and validates requests on one route (`/cpu`) while leaving another (`/memory`) open:

```powershell
Start-PodeServer {
Add-PodeEndpoint -Address * -Port 8080 -Protocol Http

# setup bearer authentication to validate a user
New-PodeAuthScheme -Bearer | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# here you'd check a real storage, this is just for example
if ($token -eq 'test-token') {
return @{
User = @{
'ID' ='M0R7Y302'
'Name' = 'Morty'
'Type' = 'Human'
# Setup Bearer authentication to validate a user via JWT
New-PodeAuthBearerScheme -AsJWT -Algorithm 'HS256' -Secret (ConvertTo-SecureString "MySecretKey" -AsPlainText -Force) -JwtVerificationMode 'Lenient' |
Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock {
param($token)

# Example: in real usage, you would decode/verify the JWT fully
if ($token -eq 'test-token') {
return @{
User = @{
'ID' = 'M0R7Y302'
'Name' = 'Morty'
'Type' = 'Human'
}
# Scope = 'read'
}
# Scope = 'read'
}
}

# authentication failed
return $null
}
# authentication failed
return $null
}

# check the request on this route against the authentication
# Validate against the authentication on this route
Add-PodeRoute -Method Get -Path '/cpu' -Authentication 'Authenticate' -ScriptBlock {
Write-PodeJsonResponse -Value @{ 'cpu' = 82 }
}

# this route will not be validated against the authentication
# Open route, no auth required
Add-PodeRoute -Method Get -Path '/memory' -ScriptBlock {
Write-PodeJsonResponse -Value @{ 'memory' = 14 }
}
}
```
Loading