diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1
index a9c4685..3be29b1 100644
--- a/PiHoleShell/PiHoleShell.psm1
+++ b/PiHoleShell/PiHoleShell.psm1
@@ -18,7 +18,7 @@ Export-ModuleMember -Function @(
#Actions
'Update-PiHoleActionsGravity', 'Invoke-PiHoleFlushNetwork', 'Invoke-PiHoleFlushLogs', 'Restart-PiHoleDnsService' `
#Authentication
- 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', `
+ 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', 'Get-PiHoleAuthStatus', 'Get-PiHoleAuthTotp', `
#GroupManagement
'Get-PiHoleGroup', 'New-PiHoleGroup', 'Update-PiHoleGroup', 'Remove-PiHoleGroup', `
#DnsControl
diff --git a/PiHoleShell/Public/Authentication/Get-PiHoleAuthStatus.ps1 b/PiHoleShell/Public/Authentication/Get-PiHoleAuthStatus.ps1
new file mode 100644
index 0000000..3666bb7
--- /dev/null
+++ b/PiHoleShell/Public/Authentication/Get-PiHoleAuthStatus.ps1
@@ -0,0 +1,82 @@
+function Get-PiHoleAuthStatus {
+ <#
+.SYNOPSIS
+Check if authentication is required
+
+.DESCRIPTION
+Checks whether your Pi-hole requires a login for the calling client. Some Pi-hole
+configurations skip authentication entirely for trusted local clients, in which case this
+reports a valid session without needing a password at all. Pass -Password to instead check
+the status of a real login with that password.
+
+.PARAMETER PiHoleServer
+The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"
+
+.PARAMETER Password
+The API Password you generated from your PiHole server. Optional - omit it to check whether
+your Pi-hole requires a login at all for this client, without authenticating.
+
+.PARAMETER IgnoreSsl
+Set to $true to skip SSL certificate validation
+
+.PARAMETER RawOutput
+This will dump the response instead of the formatted object
+
+.EXAMPLE
+Get-PiHoleAuthStatus -PiHoleServer "http://pihole.domain.com:8080"
+
+.EXAMPLE
+Get-PiHoleAuthStatus -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"
+ #>
+ [CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/auth')]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
+ param (
+ [Parameter(Mandatory = $true)]
+ [System.URI]$PiHoleServer,
+ [string]$Password,
+ [bool]$IgnoreSsl = $false,
+ [bool]$RawOutput = $false
+ )
+ try {
+ if ($PSBoundParameters.ContainsKey('Password')) {
+ $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl
+ }
+
+ $Params = @{
+ Uri = "$($PiHoleServer.OriginalString)/api/auth"
+ Method = "Get"
+ SkipCertificateCheck = $IgnoreSsl
+ ContentType = "application/json"
+ }
+ if ($Sid) {
+ $Params.Headers = @{sid = $($Sid) }
+ }
+
+ $Response = Invoke-RestMethod @Params
+
+ if ($RawOutput) {
+ Write-Output $Response
+ }
+ else {
+ $Object = [PSCustomObject]@{
+ Valid = $Response.session.valid
+ Totp = $Response.session.totp
+ Sid = $Response.session.sid
+ Csrf = $Response.session.csrf
+ Validity = $Response.session.validity
+ Message = $Response.session.message
+ }
+ Write-Output $Object
+ }
+ }
+
+ catch {
+ Write-Error -Message $_.Exception.Message
+ }
+
+ finally {
+ if ($Sid) {
+ Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
+ }
+ }
+}
diff --git a/PiHoleShell/Public/Authentication/Get-PiHoleAuthTotp.ps1 b/PiHoleShell/Public/Authentication/Get-PiHoleAuthTotp.ps1
new file mode 100644
index 0000000..427248c
--- /dev/null
+++ b/PiHoleShell/Public/Authentication/Get-PiHoleAuthTotp.ps1
@@ -0,0 +1,76 @@
+function Get-PiHoleAuthTotp {
+ <#
+.SYNOPSIS
+Suggest new TOTP credentials
+
+.DESCRIPTION
+Suggests new TOTP credentials for setting up two-factor authentication (2FA) on your Pi-hole.
+This only suggests a secret; it does not enable 2FA by itself.
+
+.PARAMETER PiHoleServer
+The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"
+
+.PARAMETER Password
+The API Password you generated from your PiHole server
+
+.PARAMETER IgnoreSsl
+Set to $true to skip SSL certificate validation
+
+.PARAMETER RawOutput
+This will dump the response instead of the formatted object
+
+.EXAMPLE
+Get-PiHoleAuthTotp -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"
+ #>
+ [CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/auth/totp')]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
+ param (
+ [Parameter(Mandatory = $true)]
+ [System.URI]$PiHoleServer,
+ [Parameter(Mandatory = $true)]
+ [string]$Password,
+ [bool]$IgnoreSsl = $false,
+ [bool]$RawOutput = $false
+ )
+ try {
+ $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl
+
+ $Params = @{
+ Headers = @{sid = $($Sid) }
+ Uri = "$($PiHoleServer.OriginalString)/api/auth/totp"
+ Method = "Get"
+ SkipCertificateCheck = $IgnoreSsl
+ ContentType = "application/json"
+ }
+
+ $Response = Invoke-RestMethod @Params
+
+ if ($RawOutput) {
+ Write-Output $Response
+ }
+ else {
+ $Object = [PSCustomObject]@{
+ Type = $Response.totp.type
+ Account = $Response.totp.account
+ Issuer = $Response.totp.issuer
+ Algorithm = $Response.totp.algorithm
+ Digits = $Response.totp.digits
+ Period = $Response.totp.period
+ Offset = $Response.totp.offset
+ Secret = $Response.totp.secret
+ Codes = $Response.totp.codes
+ }
+ Write-Output $Object
+ }
+ }
+
+ catch {
+ Write-Error -Message $_.Exception.Message
+ }
+
+ finally {
+ if ($Sid) {
+ Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
+ }
+ }
+}
diff --git a/README.md b/README.md
index 14a562d..ac3af28 100644
--- a/README.md
+++ b/README.md
@@ -202,6 +202,8 @@ Session handling is automatic for every command above, but these are available f
| Function | Description |
|---|---|
+| `Get-PiHoleAuthStatus` | Check if authentication is required |
+| `Get-PiHoleAuthTotp` | Suggest new TOTP credentials |
| `Get-PiHoleCurrentAuthSession` | List of all current sessions including their validity and further information about the client such as the IP address and user agent. |
| `Remove-PiHoleAuthSession` | Using this endpoint, a session can be deleted by its ID. |
diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md
index 3d35681..a701363 100644
--- a/docs/EXAMPLES.md
+++ b/docs/EXAMPLES.md
@@ -99,9 +99,9 @@ New-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -GroupName "PiHo
Name : PiHoleShellDocsExampleGroup
Comment : Example group
Enabled : True
-Id : 60
-DateAdded : 9/30/2026 4:22:23 PM
-DateModified : 9/30/2026 4:22:23 PM
+Id : 57
+DateAdded : 10/1/2026 3:13:46 PM
+DateModified : 10/1/2026 3:13:46 PM
```
### Get-PiHoleGroup
@@ -143,9 +143,9 @@ DateModified : 9/30/2026 8:48:26 AM
Name : PiHoleShellDocsExampleGroup
Comment : Example group
Enabled : True
-Id : 60
-DateAdded : 9/30/2026 4:22:23 PM
-DateModified : 9/30/2026 4:22:23 PM
+Id : 57
+DateAdded : 10/1/2026 3:13:46 PM
+DateModified : 10/1/2026 3:13:46 PM
```
### Update-PiHoleGroup
@@ -159,9 +159,9 @@ Update-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -GroupName "P
Name : PiHoleShellDocsExampleGroup
Comment : Example group
Enabled : False
-Id : 60
-DateAdded : 9/30/2026 4:22:23 PM
-DateModified : 9/30/2026 4:22:41 PM
+Id : 57
+DateAdded : 10/1/2026 3:13:46 PM
+DateModified : 10/1/2026 3:14:04 PM
```
### Remove-PiHoleGroup
@@ -191,8 +191,8 @@ Comment : Example list
Groups : {Default}
Enabled : True
Id : 71
-DateAdded : 9/30/2026 4:23:18 PM
-DateModified : 9/30/2026 4:23:18 PM
+DateAdded : 10/1/2026 3:14:41 PM
+DateModified : 10/1/2026 3:14:41 PM
Type : Block
DateUpdated :
Number : 0
@@ -295,8 +295,8 @@ Comment : Example list
Groups : {Default}
Enabled : False
Id : 71
-DateAdded : 9/30/2026 4:23:18 PM
-DateModified : 9/30/2026 4:23:51 PM
+DateAdded : 10/1/2026 3:14:41 PM
+DateModified : 10/1/2026 3:15:14 PM
Type : Block
DateUpdated :
Number : 0
@@ -422,8 +422,8 @@ Comment : Example domain
Groups : {Default}
Enabled : True
Id : 22
-DateAdded : 9/30/2026 4:24:29 PM
-DateModified : 9/30/2026 4:24:29 PM
+DateAdded : 10/1/2026 3:15:52 PM
+DateModified : 10/1/2026 3:15:52 PM
```
### Get-PiHoleDomain
@@ -442,8 +442,8 @@ Comment : Example domain
Groups : {Default}
Enabled : True
Id : 22
-DateAdded : 9/30/2026 4:24:29 PM
-DateModified : 9/30/2026 4:24:29 PM
+DateAdded : 10/1/2026 3:15:52 PM
+DateModified : 10/1/2026 3:15:52 PM
```
### Update-PiHoleDomain
@@ -462,8 +462,8 @@ Comment : Example domain
Groups : {Default}
Enabled : False
Id : 22
-DateAdded : 9/30/2026 4:24:29 PM
-DateModified : 9/30/2026 4:25:02 PM
+DateAdded : 10/1/2026 3:15:52 PM
+DateModified : 10/1/2026 3:16:25 PM
```
### Remove-PiHoleDomain
@@ -494,9 +494,9 @@ Client : 192.168.99.99
Name :
Comment : Example client
Groups : {Default}
-Id : 23
-DateAdded : 9/30/2026 4:25:33 PM
-DateModified : 9/30/2026 4:25:33 PM
+Id : 22
+DateAdded : 10/1/2026 3:16:56 PM
+DateModified : 10/1/2026 3:16:56 PM
```
### Get-PiHoleClient
@@ -511,9 +511,9 @@ Client : 192.168.99.99
Name :
Comment : Example client
Groups : {Default}
-Id : 23
-DateAdded : 9/30/2026 4:25:33 PM
-DateModified : 9/30/2026 4:25:33 PM
+Id : 22
+DateAdded : 10/1/2026 3:16:56 PM
+DateModified : 10/1/2026 3:16:56 PM
```
### Get-PiHoleClientSuggestion
@@ -526,13 +526,13 @@ Get-PiHoleClientSuggestion -PiHoleServer $PiHoleServer -Password $Password
HwAddr : ip-127.0.0.1
MacVendor :
-LastQuery : 9/30/2026 4:19:28 PM
+LastQuery : 10/1/2026 3:00:00 PM
Addresses : 127.0.0.1
Names : localhost
HwAddr : 74:56:3c:bb:f4:0b
MacVendor : Giga-Byte Technology Co.,Ltd.
-LastQuery : 9/30/2026 4:19:27 PM
+LastQuery : 10/1/2026 9:03:33 AM
Addresses : 192.168.1.162
Names :
```
@@ -549,9 +549,9 @@ Client : 192.168.99.99
Name :
Comment : Updated comment
Groups : {Default}
-Id : 23
-DateAdded : 9/30/2026 4:25:33 PM
-DateModified : 9/30/2026 4:26:12 PM
+Id : 22
+DateAdded : 10/1/2026 3:16:56 PM
+DateModified : 10/1/2026 3:17:35 PM
```
### Remove-PiHoleClient
@@ -576,18 +576,18 @@ Get-PiHoleStatsSummary -PiHoleServer $PiHoleServer -Password $Password
```
-Total : 42
-Blocked : 8
-PercentBlocked : 19.0476188659668
+Total : 52
+Blocked : 10
+PercentBlocked : 19.230770111084
UniqueDomains : 15
Forwarded : 27
-Cached : 7
+Cached : 15
Frequency : 0
-Types : @{A=19; AAAA=20; ANY=0; SRV=0; SOA=0; PTR=3; TXT=0; NAPTR=0; MX=0; DS=0; RRSIG=0; DNSKEY=0; NS=0; SVCB=0; HTTPS=0; OTHER=0}
-Status : @{Unknown=0; Gravity=8; Forwarded=27; Cache=7; Regex=0; DenyList=0; ExternalBlockedIp=0; ExternalBlockedNull=0; ExternalBlockedNxra=0; GravityCname=0; RegexCname=0; DenyListCname=0; Retired=0; RetiredDnssec=0; InProgress=0; Dbbusy=0; SpecialDomain=0; CacheStale=0; ExternalBlockedEde15=0}
-Replies : @{Unknown=10; Nodata=2; Nxdomain=1; Cname=0; Ip=27; Domain=2; Rrname=0; ServFail=0; Refused=0; Notimp=0; Other=0; Dnssec=0; None=0; Blob=0}
+Types : @{A=20; AAAA=19; ANY=0; SRV=0; SOA=0; PTR=13; TXT=0; NAPTR=0; MX=0; DS=0; RRSIG=0; DNSKEY=0; NS=0; SVCB=0; HTTPS=0; OTHER=0}
+Status : @{Unknown=0; Gravity=10; Forwarded=27; Cache=15; Regex=0; DenyList=0; ExternalBlockedIp=0; ExternalBlockedNull=0; ExternalBlockedNxra=0; GravityCname=0; RegexCname=0; DenyListCname=0; Retired=0; RetiredDnssec=0; InProgress=0; Dbbusy=0; SpecialDomain=0; CacheStale=0; ExternalBlockedEde15=0}
+Replies : @{Unknown=10; Nodata=3; Nxdomain=3; Cname=0; Ip=26; Domain=10; Rrname=0; ServFail=0; Refused=0; Notimp=0; Other=0; Dnssec=0; None=0; Blob=0}
Clients : @{Active=2; Total=2}
-Gravity : @{DomainsBeingBlocked=494952; LastUpdate=1790799702}
+Gravity : @{DomainsBeingBlocked=495564; LastUpdate=1790861541}
```
### Get-PiHoleStatsRecentBlocked
@@ -598,7 +598,7 @@ Get-PiHoleStatsRecentBlocked -PiHoleServer $PiHoleServer -Password $Password
```
-Blocked : adservice.google.com
+Blocked : googleadservices.com
```
### Get-PiHoleStatsQueryType
@@ -610,10 +610,10 @@ Get-PiHoleStatsQueryType -PiHoleServer $PiHoleServer -Password $Password
```
Type : A
-Count : 19
+Count : 20
Type : AAAA
-Count : 20
+Count : 19
Type : ANY
Count : 0
@@ -635,13 +635,10 @@ Get-PiHoleStatsTopDomain -PiHoleServer $PiHoleServer -Password $Password
```
-Domain : mozilla.org
-Count : 4
-
-Domain : bbc.com
-Count : 4
+Domain : 1.0.0.127.in-addr.arpa
+Count : 7
-Domain : stackoverflow.com
+Domain : example.com
Count : 4
Domain : cloudflare.com
@@ -650,6 +647,9 @@ Count : 4
Domain : reddit.com
Count : 4
+Domain : bbc.com
+Count : 4
+
_(showing 5 of 10 results)_
```
@@ -667,7 +667,7 @@ Count : 39
IP : 127.0.0.1
Name : localhost
-Count : 3
+Count : 13
```
### Get-PiHoleStatsUpstream
@@ -678,9 +678,9 @@ Get-PiHoleStatsUpstream -PiHoleServer $PiHoleServer -Password $Password
```
-TotalQueries : 42
+TotalQueries : 52
ForwardedQueries : 27
-Upstreams : {@{Ip=blocklist; Name=blocklist; Port=-1; Count=8; ResponseTime=0; Variance=0}, @{Ip=cache; Name=cache; Port=-1; Count=7; ResponseTime=0; Variance=0}, @{Ip=8.8.8.8; Name=dns.google; Port=53; Count=27; ResponseTime=0.0287302732467651; Variance=0.00306724246755919}}
+Upstreams : {@{Ip=blocklist; Name=blocklist; Port=-1; Count=10; ResponseTime=0; Variance=0}, @{Ip=cache; Name=cache; Port=-1; Count=15; ResponseTime=0; Variance=0}, @{Ip=8.8.8.8; Name=dns.google; Port=53; Count=27; ResponseTime=0.0202921777963638; Variance=0.00264818341859654}}
```
### Get-PiHoleStatsQuerySuggestions
@@ -691,7 +691,7 @@ Get-PiHoleStatsQuerySuggestions -PiHoleServer $PiHoleServer -Password $Password
```
-Domain : {mozilla.org, bbc.com, stackoverflow.com, cloudflare.com…}
+Domain : {1.0.0.127.in-addr.arpa, example.com, cloudflare.com, reddit.com…}
ClientIp : {192.168.1.162, 127.0.0.1}
ClientName : {localhost}
Upstream : {blocklist, cache, 8.8.8.8#53 (dns.google), permitted}
@@ -711,9 +711,9 @@ Get-PiHoleStatsDatabaseSummary -PiHoleServer $PiHoleServer -Password $Password
```
-SumQueries : 42
-SumBlocked : 8
-PercentBlocked : 19.0476188659668
+SumQueries : 52
+SumBlocked : 10
+PercentBlocked : 19.230770111084
TotalClients : 3
```
@@ -728,7 +728,7 @@ Get-PiHoleStatsDatabaseQueryType -PiHoleServer $PiHoleServer -Password $Password
```
Type : A
-Count : 20
+Count : 19
Type : AAAA
Count : 0
@@ -740,7 +740,7 @@ Type : SRV
Count : 0
Type : SOA
-Count : 3
+Count : 13
_(showing 5 of 16 results)_
```
@@ -755,13 +755,13 @@ Get-PiHoleStatsDatabaseTopDomain -PiHoleServer $PiHoleServer -Password $Password
```
-Domain : nytimes.com
-Count : 4
+Domain : 1.0.0.127.in-addr.arpa
+Count : 7
Domain : bbc.com
Count : 4
-Domain : mozilla.org
+Domain : wikipedia.org
Count : 4
Domain : example.com
@@ -785,15 +785,15 @@ Get-PiHoleStatsDatabaseTopClient -PiHoleServer $PiHoleServer -Password $Password
IP : 192.168.1.162
Name :
-Count : 31
+Count : 29
IP : 127.0.0.1
-Name :
-Count : 2
+Name : localhost
+Count : 11
IP : 127.0.0.1
-Name : localhost
-Count : 1
+Name :
+Count : 2
```
### Get-PiHoleStatsDatabaseUpstream
@@ -806,9 +806,9 @@ Get-PiHoleStatsDatabaseUpstream -PiHoleServer $PiHoleServer -Password $Password
```
-TotalQueries : 42
+TotalQueries : 52
ForwardedQueries : 27
-Upstreams : {@{Ip=2; Name=; Port=4294967295; Count=27; ResponseTime=0; Variance=0}, @{Ip=cache; Name=cache; Port=-1; Count=7; ResponseTime=0; Variance=0}, @{Ip=blocklist; Name=blocklist; Port=-1; Count=8; ResponseTime=0; Variance=0}}
+Upstreams : {@{Ip=2; Name=; Port=4294967295; Count=27; ResponseTime=0; Variance=0}, @{Ip=cache; Name=cache; Port=-1; Count=15; ResponseTime=0; Variance=0}, @{Ip=blocklist; Name=blocklist; Port=-1; Count=10; ResponseTime=0; Variance=0}}
```
### Get-PiHoleQuery
@@ -821,69 +821,69 @@ Get-PiHoleQuery -PiHoleServer $PiHoleServer -Password $Password -Length 5
```
-Id : 41
-Time : 9/30/2026 4:19:28 PM
+Id : 51
+Time : 10/1/2026 3:00:00 PM
Type : PTR
Domain : 1.0.0.127.in-addr.arpa
Cname :
Status : CACHE
Client : @{Ip=127.0.0.1; Name=localhost}
Dnssec : UNKNOWN
-Reply : @{Type=DOMAIN; Time=0.0016322135925293}
+Reply : @{Type=DOMAIN; Time=0.000154972076416016}
ListId :
Upstream :
Ede : @{Code=-1; Text=}
-Id : 40
-Time : 9/30/2026 4:19:28 PM
-Type : AAAA
-Domain : adservice.google.com
+Id : 50
+Time : 10/1/2026 2:00:01 PM
+Type : PTR
+Domain : 1.0.0.127.in-addr.arpa
Cname :
-Status : GRAVITY
-Client : @{Ip=192.168.1.162; Name=}
+Status : CACHE
+Client : @{Ip=127.0.0.1; Name=localhost}
Dnssec : UNKNOWN
-Reply : @{Type=IP; Time=0.00115633010864258}
-ListId : -53
+Reply : @{Type=DOMAIN; Time=0.000155925750732422}
+ListId :
Upstream :
Ede : @{Code=-1; Text=}
-Id : 39
-Time : 9/30/2026 4:19:28 PM
-Type : A
-Domain : adservice.google.com
+Id : 49
+Time : 10/1/2026 1:00:00 PM
+Type : PTR
+Domain : 1.0.0.127.in-addr.arpa
Cname :
-Status : GRAVITY
-Client : @{Ip=192.168.1.162; Name=}
+Status : CACHE
+Client : @{Ip=127.0.0.1; Name=localhost}
Dnssec : UNKNOWN
-Reply : @{Type=IP; Time=0.00106549263000488}
-ListId : -53
+Reply : @{Type=DOMAIN; Time=0.000154018402099609}
+ListId :
Upstream :
Ede : @{Code=-1; Text=}
-Id : 38
-Time : 9/30/2026 4:19:28 PM
-Type : AAAA
-Domain : microsoft.com
+Id : 48
+Time : 10/1/2026 12:00:00 PM
+Type : PTR
+Domain : 1.0.0.127.in-addr.arpa
Cname :
Status : CACHE
-Client : @{Ip=192.168.1.162; Name=}
+Client : @{Ip=127.0.0.1; Name=localhost}
Dnssec : UNKNOWN
-Reply : @{Type=IP; Time=0.000108957290649414}
+Reply : @{Type=DOMAIN; Time=0.000212192535400391}
ListId :
Upstream :
Ede : @{Code=-1; Text=}
-Id : 37
-Time : 9/30/2026 4:19:28 PM
-Type : AAAA
-Domain : microsoft.com
+Id : 47
+Time : 10/1/2026 11:00:01 AM
+Type : PTR
+Domain : 8.8.8.8.in-addr.arpa
Cname :
-Status : FORWARDED
-Client : @{Ip=192.168.1.162; Name=}
+Status : CACHE
+Client : @{Ip=127.0.0.1; Name=localhost}
Dnssec : UNKNOWN
-Reply : @{Type=IP; Time=0.0397381782531738}
+Reply : @{Type=DOMAIN; Time=0.00014805793762207}
ListId :
-Upstream : 8.8.8.8#53
+Upstream :
Ede : @{Code=-1; Text=}
```
@@ -978,20 +978,20 @@ CpuPercent : 0
MemoryPercent : 0
ActiveClients : 2
Blocking : enabled
-Cache : @{Size=10000; Inserted=38; Evicted=0}
+Cache : @{Size=10000; Inserted=35; Evicted=0}
Config : @{DhcpActive=False; DhcpStart=; DhcpEnd=; DhcpIpv6=False; DnsDnssec=False; DnsDomain=lan; DnsNumUpstreams=2; DnsPort=53; DnsrevServerAactive=False; PrivacyLevel=0}
-GravitySize : 494952
+GravitySize : 495564
HostModel : Raspberry Pi Zero W Rev 1.1
IFace : @{v4=; v6=}
NodeName : dns3.localdomain
Pid : 388
-Queries : @{Total=42; Blocked=8; PercentBlocked=19.0476188659668; QueryFrequency=0}
-RecentBlocked : adservice.google.com
-Sensors : @{CpuTemp=41.698; HotLimit=60; Unit=C}
-System : @{Uptime=153598; Memory=; Procs=73; Cpu=; Ftl=}
+Queries : @{Total=52; Blocked=10; PercentBlocked=19.230770111084; QueryFrequency=0}
+RecentBlocked : googleadservices.com
+Sensors : @{CpuTemp=39.546; HotLimit=60; Unit=C}
+System : @{Uptime=235882; Memory=; Procs=72; Cpu=; Ftl=}
TopBlocked : googleadservices.com
TopClient : 192.168.1.162
-TopDomain : nytimes.com
+TopDomain : 1.0.0.127.in-addr.arpa
Version : @{Core=; Web=; Ftl=; Docker=}
```
@@ -1028,11 +1028,11 @@ Get-PiHoleInfoSystem -PiHoleServer $PiHoleServer -Password $Password
```
-Uptime : 153609
+Uptime : 235892
Memory : @{Ram=; Swap=}
-Procs : 73
-Cpu : @{NumProcessors=1; PercentCpu=96.5; Load=}
-Ftl : @{PercentMemory=3.01092410087585; PercentCpu=96}
+Procs : 74
+Cpu : @{NumProcessors=1; PercentCpu=97.1999969482422; Load=}
+Ftl : @{PercentMemory=2.52266621589661; PercentCpu=96.4000015258789}
```
### Get-PiHoleInfoFtl
@@ -1043,16 +1043,16 @@ Get-PiHoleInfoFtl -PiHoleServer $PiHoleServer -Password $Password
```
-Database : @{Gravity=494952; Antigravity=0; Groups=4; Lists=14; Clients=0; Domains=; Regex=}
+Database : @{Gravity=495564; Antigravity=0; Groups=4; Lists=14; Clients=0; Domains=; Regex=}
PrivacyLevel : 0
QueryFrequency : 0
Clients : @{Total=2; Active=2}
Pid : 388
-Uptime : 3776419.413644
-PercentMemory : 3.01092410087585
+Uptime : 24254906.207769
+PercentMemory : 2.52266621589661
PercentCpu : 96.4000015258789
AllowDestructive : True
-Dnsmasq : @{DnsCacheInserted=38; DnsCacheLiveFreed=0; DnsQueriesForwarded=27; DnsAuthAnswered=0; DnsLocalAnswered=15; DnsStaleAnswered=0; DnsUnanswered=0; DnssecMaxCryptoUse=0; DnssecMaxSigFail=0; DnssecMaxWork=0; Bootp=0; Pxe=0; DhcpAck=0; DhcpDecline=0; DhcpDiscover=0; DhcpInform=0; DhcpNak=0; DhcpOffer=0; DhcpRelease=0; DhcpRequest=0; Noanswer=0; LeasesAllocated4=0; LeasesPruned4=0; LeasesAllocated6=0; LeasesPruned6=0; TcpConnections=0; DhcpLeasequery=0; DhcpLeaseUnassigned=0; DhcpLeaseActve=0; DhcpLeaseUnknown=0}
+Dnsmasq : @{DnsCacheInserted=35; DnsCacheLiveFreed=0; DnsQueriesForwarded=27; DnsAuthAnswered=0; DnsLocalAnswered=25; DnsStaleAnswered=0; DnsUnanswered=0; DnssecMaxCryptoUse=0; DnssecMaxSigFail=0; DnssecMaxWork=0; Bootp=0; Pxe=0; DhcpAck=0; DhcpDecline=0; DhcpDiscover=0; DhcpInform=0; DhcpNak=0; DhcpOffer=0; DhcpRelease=0; DhcpRequest=0; Noanswer=0; LeasesAllocated4=0; LeasesPruned4=0; LeasesAllocated6=0; LeasesPruned6=0; TcpConnections=0; DhcpLeasequery=0; DhcpLeaseUnassigned=0; DhcpLeaseActve=0; DhcpLeaseUnknown=0}
```
### Get-PiHoleInfoSensors
@@ -1064,7 +1064,7 @@ Get-PiHoleInfoSensors -PiHoleServer $PiHoleServer -Password $Password
```
List : {@{Name=cpu_thermal; Path=hwmon0; Source=devices/virtual/thermal/thermal_zone0; Temps=}, @{Name=rpi_volt; Path=hwmon1; Source=devices/platform/soc/soc:firmware/raspberrypi-hwmon; Temps=}}
-CpuTemp : 41.16
+CpuTemp : 40.084
HotLimit : 60
Unit : C
```
@@ -1081,12 +1081,12 @@ Size : 13983744
Type : Regular file
Mode : rw-r-----
AccessTime : 6/25/2025 12:32:44 AM
-ModifiedTime : 9/30/2026 3:18:14 PM
-ChangeTime : 9/30/2026 3:18:15 PM
+ModifiedTime : 10/1/2026 2:06:00 PM
+ChangeTime : 10/1/2026 2:06:00 PM
Owner : @{User=; Group=}
-Queries : 42
-EarliestTimestamp : 9/29/2026 4:20:00 PM
-QueriesDisk : 42
+Queries : 52
+EarliestTimestamp : 9/30/2026 3:10:00 PM
+QueriesDisk : 52
EarliestTimestampDisk :
SqliteVersion : 3.53.1
```
@@ -1155,17 +1155,17 @@ Get-PiHoleInfoMessage -PiHoleServer $PiHoleServer -Password $Password
```
-Id : 22
-Timestamp : 9/30/2026 4:16:55 PM
+Id : 1
+Timestamp : 10/1/2026 8:28:21 AM
Type : LIST
Plain : List with ID 63 (https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/domains/light.txt) was inaccessible during last gravity run
Html : List with ID 63 (https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/domains/light.txt) was inaccessible during last gravity run
-Id : 23
-Timestamp : 9/30/2026 4:18:16 PM
+Id : 2
+Timestamp : 10/1/2026 9:38:21 AM
Type : LOAD
-Plain : Long-term load (15min avg) larger than number of processors: 1.9 > 1
-Html : Long-term load (15min avg) larger than number of processors: 1.9 > 1
This may slow down DNS resolution and can cause bottlenecks.
+Plain : Long-term load (15min avg) larger than number of processors: 1.0 > 1
+Html : Long-term load (15min avg) larger than number of processors: 1.0 > 1
This may slow down DNS resolution and can cause bottlenecks.
```
### Get-PiHoleInfoMessageCount
@@ -1200,8 +1200,8 @@ Get-PiHoleLogWebserver -PiHoleServer $PiHoleServer -Password $Password
```
-Log : {@{Timestamp=9/30/2026 3:18:16 PM; Message=Initializing HTTP server on ports "8089,8489s"; Priority=}, @{Timestamp=9/30/2026 3:29:52 PM; Message=ACCESS: 192.168.1.162 - - [30/Sep/2026:20:29:51 +0000] "PATCH /api/config?restart=false HTTP/1.1" 200 4588 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}, @{Timestamp=9/30/2026 3:29:53 PM; Message=ACCESS: 192.168.1.162 - - [30/Sep/2026:20:29:52 +0000] "DELETE /api/auth HTTP/1.1" 204 558 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}, @{Timestamp=9/30/2026 3:29:54 PM; Message=ACCESS: 192.168.1.162 - - [30/Sep/2026:20:29:45 +0000] "POST /api/auth HTTP/1.1" 200 806 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}…}
-NextID : 33
+Log : {@{Timestamp=10/1/2026 8:28:22 AM; Message=Initializing HTTP server on ports "8089,8489s"; Priority=}, @{Timestamp=10/1/2026 8:31:16 AM; Message=ACCESS: 192.168.1.162 - - [01/Oct/2026:13:31:15 +0000] "PATCH /api/config?restart=false HTTP/1.1" 200 4588 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}, @{Timestamp=10/1/2026 8:31:17 AM; Message=ACCESS: 192.168.1.162 - - [01/Oct/2026:13:31:16 +0000] "DELETE /api/auth HTTP/1.1" 204 558 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}, @{Timestamp=10/1/2026 8:31:26 AM; Message=ACCESS: 192.168.1.162 - - [01/Oct/2026:13:31:17 +0000] "POST /api/auth HTTP/1.1" 200 807 - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6; Priority=}…}
+NextID : 31
Pid : 388
File : /var/log/pihole/webserver.log
```
@@ -1214,7 +1214,7 @@ Get-PiHoleLogDnsmasq -PiHoleServer $PiHoleServer -Password $Password
```
-Log : {@{Timestamp=9/30/2026 3:18:16 PM; Message=started, version pi-hole-v2.93 cachesize 10000; Priority=}, @{Timestamp=9/30/2026 3:18:16 PM; Message=DNS service limited to local subnets; Priority=}, @{Timestamp=9/30/2026 3:18:16 PM; Message=compile time options: IPv6 GNU-getopt no-DBus no-UBus no-i18n IDN2 DHCP DHCPv6 Lua TFTP no-conntrack ipset no-nftset auth DNSSEC loop-detect inotify dumpfile; Priority=}, @{Timestamp=9/30/2026 3:18:16 PM; Message=using nameserver 8.8.8.8#53; Priority=}…}
+Log : {@{Timestamp=10/1/2026 8:28:22 AM; Message=started, version pi-hole-v2.93 cachesize 10000; Priority=}, @{Timestamp=10/1/2026 8:28:22 AM; Message=DNS service limited to local subnets; Priority=}, @{Timestamp=10/1/2026 8:28:22 AM; Message=compile time options: IPv6 GNU-getopt no-DBus no-UBus no-i18n IDN2 DHCP DHCPv6 Lua TFTP no-conntrack ipset no-nftset auth DNSSEC loop-detect inotify dumpfile; Priority=}, @{Timestamp=10/1/2026 8:28:22 AM; Message=using nameserver 8.8.8.8#53; Priority=}…}
NextID : 69
Pid : 388
File : /var/log/pihole/pihole.log
@@ -1228,8 +1228,8 @@ Get-PiHoleLogFtl -PiHoleServer $PiHoleServer -Password $Password
```
-Log : {@{Timestamp=9/30/2026 3:44:48 PM; Message= -> s_cost: 1024; Priority=DEBUG_API}, @{Timestamp=9/30/2026 3:44:48 PM; Message= -> t_cost: 32; Priority=DEBUG_API}, @{Timestamp=9/30/2026 3:44:48 PM; Message= -> Salt: 'ZM+UIgW0FxI1Ns0FbtOwxg=='; Priority=DEBUG_API}, @{Timestamp=9/30/2026 3:44:48 PM; Message= -> Hash: 'REBNkE/jmdkRF3uyFRUpnxcQCKuSaS3U30DMsqWp0yE='; Priority=DEBUG_API}…}
-NextID : 919
+Log : {@{Timestamp=10/1/2026 8:54:42 AM; Message=Wrote config file:; Priority=INFO}, @{Timestamp=10/1/2026 8:54:42 AM; Message= - 167 total entries; Priority=INFO}, @{Timestamp=10/1/2026 8:54:42 AM; Message= - 157 entries are default; Priority=INFO}, @{Timestamp=10/1/2026 8:54:42 AM; Message= - 10 entries are modified; Priority=INFO}…}
+NextID : 797
Pid : 388
File : /var/log/pihole/FTL.log
```
@@ -1258,31 +1258,31 @@ Get-PiHoleHistory -PiHoleServer $PiHoleServer -Password $Password
```
-Timestamp : 9/29/2026 4:25:00 PM
+Timestamp : 9/30/2026 3:15:00 PM
Total : 0
Cached : 0
Blocked : 0
Forwarded : 0
-Timestamp : 9/29/2026 4:35:00 PM
+Timestamp : 9/30/2026 3:25:00 PM
Total : 0
Cached : 0
Blocked : 0
Forwarded : 0
-Timestamp : 9/29/2026 4:45:00 PM
+Timestamp : 9/30/2026 3:35:00 PM
Total : 0
Cached : 0
Blocked : 0
Forwarded : 0
-Timestamp : 9/29/2026 4:55:00 PM
+Timestamp : 9/30/2026 3:45:00 PM
Total : 0
Cached : 0
Blocked : 0
Forwarded : 0
-Timestamp : 9/29/2026 5:05:00 PM
+Timestamp : 9/30/2026 3:55:00 PM
Total : 0
Cached : 0
Blocked : 0
@@ -1299,19 +1299,19 @@ Get-PiHoleHistoryClient -PiHoleServer $PiHoleServer -Password $Password
```
-Timestamp : 9/29/2026 4:25:00 PM
+Timestamp : 9/30/2026 3:15:00 PM
Clients : {@{IP=192.168.1.162; Name=; Count=0}, @{IP=others; Name=; Count=0}}
-Timestamp : 9/29/2026 4:35:00 PM
+Timestamp : 9/30/2026 3:25:00 PM
Clients : {@{IP=192.168.1.162; Name=; Count=0}, @{IP=others; Name=; Count=0}}
-Timestamp : 9/29/2026 4:45:00 PM
+Timestamp : 9/30/2026 3:35:00 PM
Clients : {@{IP=192.168.1.162; Name=; Count=0}, @{IP=others; Name=; Count=0}}
-Timestamp : 9/29/2026 4:55:00 PM
+Timestamp : 9/30/2026 3:45:00 PM
Clients : {@{IP=192.168.1.162; Name=; Count=0}, @{IP=others; Name=; Count=0}}
-Timestamp : 9/29/2026 5:05:00 PM
+Timestamp : 9/30/2026 3:55:00 PM
Clients : {@{IP=192.168.1.162; Name=; Count=0}, @{IP=others; Name=; Count=0}}
_(showing 5 of 145 results)_
@@ -1327,11 +1327,37 @@ Get-PiHoleHistoryDatabase -PiHoleServer $PiHoleServer -Password $Password
```
-Timestamp : 9/30/2026 4:10:00 PM
+Timestamp : 10/1/2026 9:00:00 AM
Total : 42
-Cached : 7
-Blocked : 8
+Cached : 5
+Blocked : 10
Forwarded : 27
+
+Timestamp : 10/1/2026 10:00:00 AM
+Total : 3
+Cached : 3
+Blocked : 0
+Forwarded : 0
+
+Timestamp : 10/1/2026 11:00:00 AM
+Total : 3
+Cached : 3
+Blocked : 0
+Forwarded : 0
+
+Timestamp : 10/1/2026 12:00:00 PM
+Total : 1
+Cached : 1
+Blocked : 0
+Forwarded : 0
+
+Timestamp : 10/1/2026 1:00:00 PM
+Total : 1
+Cached : 1
+Blocked : 0
+Forwarded : 0
+
+_(showing 5 of 7 results)_
```
### Get-PiHoleHistoryDatabaseClient
@@ -1344,8 +1370,22 @@ Get-PiHoleHistoryDatabaseClient -PiHoleServer $PiHoleServer -Password $Password
```
-Timestamp : 9/30/2026 4:10:00 PM
-Clients : {@{ClientId=3; Count=1}, @{ClientId=2; Count=1}, @{ClientId=5; Count=1}}
+Timestamp : 10/1/2026 3:00:00 PM
+Clients : @{ClientId=3; Count=1}
+
+Timestamp : 10/1/2026 2:00:00 PM
+Clients : @{ClientId=3; Count=1}
+
+Timestamp : 10/1/2026 1:00:00 PM
+Clients : @{ClientId=3; Count=1}
+
+Timestamp : 10/1/2026 12:00:00 PM
+Clients : @{ClientId=3; Count=1}
+
+Timestamp : 10/1/2026 11:00:00 AM
+Clients : @{ClientId=3; Count=1}
+
+_(showing 5 of 7 results)_
```
### Get-PiHoleNetworkGateway
@@ -1454,7 +1494,7 @@ Address : b8:27:eb:11:a4:d5
Broadcast : ff:ff:ff:ff:ff:ff
PermAddress : b8:27:eb:11:a4:d5
Stats : @{RxBytes=; TxBytes=; Bits=64}
-Addresses : {@{Family=inet; Scope=universe; Flags=; Prefixlen=24; Address=192.168.1.248; AddressType=private; Local=192.168.1.248; LocalType=private; Broadcast=192.168.1.255; BroadcastType=private; Label=wlan0; Prefered=76172; Valid=76172; Cstamp=1790649687.41; Tstamp=1790793450.81}, @{Family=inet6; Scope=link; Flags=permanent; Prefixlen=64; Address=fe80::ba27:ebff:fe11:a4d5; AddressType=link-local (LL); Prefered=4294967295; Valid=4294967295; Cstamp=1790649682.83; Tstamp=1790649682.83}}
+Addresses : {@{Family=inet; Scope=universe; Flags=; Prefixlen=24; Address=192.168.1.248; AddressType=private; Local=192.168.1.248; LocalType=private; Broadcast=192.168.1.255; BroadcastType=private; Label=wlan0; Prefered=63266; Valid=63266; Cstamp=1790649687.41; Tstamp=1790862827.1}, @{Family=inet6; Scope=link; Flags=permanent; Prefixlen=64; Address=fe80::ba27:ebff:fe11:a4d5; AddressType=link-local (LL); Prefered=4294967295; Valid=4294967295; Cstamp=1790649682.83; Tstamp=1790649682.83}}
```
### Get-PiHoleNetworkDevice
@@ -1470,20 +1510,20 @@ Get-PiHoleNetworkDevice -PiHoleServer $PiHoleServer -Password $Password
Id : 2
HwAddr : ip-127.0.0.1
Interface : lo
-FirstSeen : 9/30/2026 4:19:27 PM
-LastQuery : 9/30/2026 4:19:28 PM
-NumQueries : 3
+FirstSeen : 10/1/2026 9:03:33 AM
+LastQuery : 10/1/2026 3:00:00 PM
+NumQueries : 13
MacVendor :
-Ips : @{Ip=127.0.0.1; Name=localhost; LastSeen=9/30/2026 4:28:00 PM; NameUpdated=9/30/2026 4:28:00 PM}
+Ips : @{Ip=127.0.0.1; Name=localhost; LastSeen=10/1/2026 3:19:00 PM; NameUpdated=10/1/2026 3:19:00 PM}
Id : 1
HwAddr : 74:56:3c:bb:f4:0b
Interface : wlan0
-FirstSeen : 9/30/2026 4:19:27 PM
-LastQuery : 9/30/2026 4:19:27 PM
+FirstSeen : 10/1/2026 9:03:32 AM
+LastQuery : 10/1/2026 9:03:33 AM
NumQueries : 39
MacVendor : Giga-Byte Technology Co.,Ltd.
-Ips : @{Ip=192.168.1.162; Name=; LastSeen=9/30/2026 4:28:00 PM; NameUpdated=12/31/1969 6:00:00 PM}
+Ips : @{Ip=192.168.1.162; Name=; LastSeen=10/1/2026 3:19:00 PM; NameUpdated=12/31/1969 6:00:00 PM}
```
### Remove-PiHoleNetworkDevice
@@ -1524,6 +1564,43 @@ Error : Response status code does not indicate success: 400 (Bad Request).
## Authentication
+### Get-PiHoleAuthStatus
+
+_Password is optional - omit it to check whether your Pi-hole requires a login at all for this client, without authenticating. This test server does require login, so the result below is from a real authenticated check._
+
+```powershell
+Get-PiHoleAuthStatus -PiHoleServer $PiHoleServer -Password $Password
+```
+
+```
+
+Valid : True
+Totp : False
+Sid : 8sHZ0t+C5DwLRgIcKIM7/g=
+Csrf : 1xmNq2FNUPFo7bnCRoUzIQ=
+Validity : 1800
+Message : correct password
+```
+
+### Get-PiHoleAuthTotp
+
+```powershell
+Get-PiHoleAuthTotp -PiHoleServer $PiHoleServer -Password $Password
+```
+
+```
+
+Type : totp
+Account : pi.hole
+Issuer : Pi-hole%20API
+Algorithm : SHA1
+Digits : 6
+Period : 30
+Offset : 0
+Secret : XP4YEGF2S75KCDBN7BSW37AEA65AQZBQ
+Codes : {414882, 273855, 226959, 897753…}
+```
+
### Get-PiHoleCurrentAuthSession
```powershell
@@ -1533,27 +1610,13 @@ Get-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Password $Password
```
Id : 0
-CurrentSession : False
-Valid : True
-TlsLogin : True
-TlsMixed : False
-LoginAt : 9/30/2026 4:16:56 PM
-LastActive : 9/30/2026 4:16:56 PM
-ValidUntil : 9/30/2026 4:46:56 PM
-RemoteAddress : 192.168.1.162
-UserAgent : Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6
-XForwardedFor :
-App : True
-Cli : False
-
-Id : 1
CurrentSession : True
Valid : True
TlsLogin : True
TlsMixed : False
-LoginAt : 9/30/2026 4:19:34 PM
-LastActive : 9/30/2026 4:19:39 PM
-ValidUntil : 9/30/2026 4:49:39 PM
+LoginAt : 10/1/2026 3:10:59 PM
+LastActive : 10/1/2026 3:11:03 PM
+ValidUntil : 10/1/2026 3:41:03 PM
RemoteAddress : 192.168.1.162
UserAgent : Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6
XForwardedFor :
@@ -1566,13 +1629,13 @@ Cli : False
_Deletes a session by its ID (as shown by Get-PiHoleCurrentAuthSession), not the caller's own session._
```powershell
-Remove-PiHoleAuthSession -PiHoleServer $PiHoleServer -Password $Password -Id 1
+Remove-PiHoleAuthSession -PiHoleServer $PiHoleServer -Password $Password -Id 0
```
```
-Id : 1
+Id : 0
Status : Removed
```
diff --git a/tests/Authentication/Get-PiHoleAuthStatus.Integration.Tests.ps1 b/tests/Authentication/Get-PiHoleAuthStatus.Integration.Tests.ps1
new file mode 100644
index 0000000..26adfa0
--- /dev/null
+++ b/tests/Authentication/Get-PiHoleAuthStatus.Integration.Tests.ps1
@@ -0,0 +1,51 @@
+# Requires -Module Pester
+#
+# Integration tests that call a REAL Pi-hole server. Configure tests/IntegrationConfig.local.ps1
+# (copy it from IntegrationConfig.example.ps1) before running. Tests are skipped automatically
+# if that file is missing.
+
+$script:ConfigAvailable = Test-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1')
+
+Describe 'Get-PiHoleAuthStatus (Integration)' -Tag 'Integration' {
+ BeforeAll {
+ Import-Module .\PiHoleShell\PiHoleShell.psm1 -Force
+
+ $configPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1'
+ if (Test-Path $configPath) {
+ . $configPath
+ $script:PiHoleServer = $PiHoleServer
+ $script:PiHoleToken = $PiHoleToken
+ $script:PiHoleIgnoreSsl = $PiHoleIgnoreSsl
+ }
+ }
+
+ It 'returns a valid session when given a real password' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthStatus -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl
+ $result | Format-List | Out-String | Write-Host
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Valid | Should -BeTrue
+ }
+
+ # This test server requires a login for API clients, so calling with no password at all is
+ # expected to fail - this still exercises the real anonymous-check code path. A Pi-hole
+ # configured to skip auth for trusted local clients would instead return a valid session here.
+ It 'errors when called with no password and the server requires login' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthStatus -PiHoleServer $script:PiHoleServer -IgnoreSsl $script:PiHoleIgnoreSsl -ErrorVariable errOut -ErrorAction SilentlyContinue
+
+ $errOut | Should -Not -BeNullOrEmpty
+ }
+
+ It 'returns the raw API response when RawOutput is set' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthStatus -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl -RawOutput $true
+ $result | Format-List | Out-String | Write-Host
+
+ $result.PSObject.Properties.Name | Should -Contain 'session'
+ }
+
+ It 'errors when given a bad password' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthStatus -PiHoleServer $script:PiHoleServer -Password 'definitely-not-the-real-token' -IgnoreSsl $script:PiHoleIgnoreSsl -ErrorVariable errOut -ErrorAction SilentlyContinue
+
+ $errOut | Should -Not -BeNullOrEmpty
+ }
+}
diff --git a/tests/Authentication/Get-PiHoleAuthTotp.Integration.Tests.ps1 b/tests/Authentication/Get-PiHoleAuthTotp.Integration.Tests.ps1
new file mode 100644
index 0000000..90ac40d
--- /dev/null
+++ b/tests/Authentication/Get-PiHoleAuthTotp.Integration.Tests.ps1
@@ -0,0 +1,42 @@
+# Requires -Module Pester
+#
+# Integration tests that call a REAL Pi-hole server. Configure tests/IntegrationConfig.local.ps1
+# (copy it from IntegrationConfig.example.ps1) before running. Tests are skipped automatically
+# if that file is missing.
+
+$script:ConfigAvailable = Test-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1')
+
+Describe 'Get-PiHoleAuthTotp (Integration)' -Tag 'Integration' {
+ BeforeAll {
+ Import-Module .\PiHoleShell\PiHoleShell.psm1 -Force
+
+ $configPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1'
+ if (Test-Path $configPath) {
+ . $configPath
+ $script:PiHoleServer = $PiHoleServer
+ $script:PiHoleToken = $PiHoleToken
+ $script:PiHoleIgnoreSsl = $PiHoleIgnoreSsl
+ }
+ }
+
+ It 'returns a suggested TOTP secret as a formatted object' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthTotp -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl
+ $result | Format-List | Out-String | Write-Host
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Secret | Should -Not -BeNullOrEmpty
+ }
+
+ It 'returns the raw API response when RawOutput is set' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthTotp -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl -RawOutput $true
+ $result | Format-List | Out-String | Write-Host
+
+ $result.PSObject.Properties.Name | Should -Contain 'totp'
+ }
+
+ It 'errors when given a bad password' -Skip:(-not $script:ConfigAvailable) {
+ $result = Get-PiHoleAuthTotp -PiHoleServer $script:PiHoleServer -Password 'definitely-not-the-real-token' -IgnoreSsl $script:PiHoleIgnoreSsl -ErrorVariable errOut -ErrorAction SilentlyContinue
+
+ $errOut | Should -Not -BeNullOrEmpty
+ }
+}
diff --git a/tools/Update-ExampleOutput.ps1 b/tools/Update-ExampleOutput.ps1
index 4da478d..865609d 100644
--- a/tools/Update-ExampleOutput.ps1
+++ b/tools/Update-ExampleOutput.ps1
@@ -108,6 +108,15 @@ function Invoke-Quietly {
Write-Host "Capturing example output against $PiHoleServer ..."
#region Authentication
+Add-Example -Category Authentication -FunctionName 'Get-PiHoleAuthStatus' `
+ -Invocation 'Get-PiHoleAuthStatus -PiHoleServer $PiHoleServer -Password $Password' `
+ -Note 'Password is optional - omit it to check whether your Pi-hole requires a login at all for this client, without authenticating. This test server does require login, so the result below is from a real authenticated check.' `
+ -Result (Get-PiHoleAuthStatus -PiHoleServer $PiHoleServer -Password $PiHoleToken -IgnoreSsl $PiHoleIgnoreSsl)
+
+Add-Example -Category Authentication -FunctionName 'Get-PiHoleAuthTotp' `
+ -Invocation 'Get-PiHoleAuthTotp -PiHoleServer $PiHoleServer -Password $Password' `
+ -Result (Get-PiHoleAuthTotp -PiHoleServer $PiHoleServer -Password $PiHoleToken -IgnoreSsl $PiHoleIgnoreSsl)
+
Add-Example -Category Authentication -FunctionName 'Get-PiHoleCurrentAuthSession' `
-Invocation 'Get-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Password $Password' `
-Result (Get-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Password $PiHoleToken -IgnoreSsl $PiHoleIgnoreSsl)