Skip to content

Commit 22902b0

Browse files
committed
api fixes
1 parent 49d629e commit 22902b0

3 files changed

Lines changed: 198 additions & 20 deletions

File tree

Modules/CIPPCore/Public/Authentication/New-CIPPAPIConfig.ps1

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ function New-CIPPAPIConfig {
2222
if ($AppId) {
2323
$APIApp = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications(appid='$($AppId)')" -NoAuthCheck $true -AsApp $true
2424
Write-Information "Found existing app with AppId $AppId"
25+
26+
# Validate and repair identifier URI for existing apps
27+
try {
28+
$RepairResult = Repair-CippApiIdentifierUri -AppId $AppId -ApplicationObjectId $APIApp.id
29+
if ($RepairResult.Fixed) {
30+
Write-Information "Repaired identifier URI: $($RepairResult.Message)"
31+
Write-LogMessage -headers $Headers -API $APINAME -tenant 'None' -message "Repaired identifier URI for app $AppId $($RepairResult.Message)" -Sev 'Info'
32+
} else {
33+
Write-Information "Identifier URI validation: $($RepairResult.Message)"
34+
}
35+
} catch {
36+
Write-Warning "Failed to validate/repair identifier URI for existing app: $($_.Exception.Message)"
37+
# Don't fail the whole operation if URI repair fails
38+
}
2539
} else {
2640
$CreateBody = @{
2741
api = @{
@@ -122,29 +136,58 @@ function New-CIPPAPIConfig {
122136
if (-not $APIPassword) {
123137
throw 'Failed to create application password after retries. The app may not have replicated yet; wait a moment and retry.'
124138
}
125-
Write-Information 'Adding App URL'
139+
140+
Write-Information 'Adding Application Identifier URI'
126141
$Step = 'Adding Application Identifier URI'
127-
$IdentifierUriBody = "{`"identifierUris`":[`"api://$($APIApp.appId)`"]}"
128-
$IdentifierUriUpdated = $false
129-
for ($Attempt = 1; $Attempt -le 6; $Attempt++) {
130-
try {
131-
New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/applications/$($APIApp.id)" -AsApp $true -NoAuthCheck $true -type PATCH -body $IdentifierUriBody -maxRetries 3 | Out-Null
132-
$IdentifierUriUpdated = $true
133-
break
134-
} catch {
135-
$IsNotReplicatedYet = $_.Exception.Message -match "Resource '.*' does not exist or one of its queried reference-property objects are not present"
136-
if ($IsNotReplicatedYet -and $Attempt -lt 6) {
137-
$DelaySeconds = 3
138-
Write-Information "Application object not yet replicated for identifier URI update (attempt $Attempt of 6). Retrying in $DelaySeconds second(s)."
139-
Start-Sleep -Seconds $DelaySeconds
140-
try {
141-
$APIApp = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications(appid='$($APIApp.appId)')" -NoAuthCheck $true -AsApp $true
142-
} catch {
143-
Write-Information "Application lookup retry failed before identifier URI retry: $($_.Exception.Message)"
142+
$DesiredIdentifierUri = "api://$($APIApp.appId)"
143+
144+
# Check if identifier URI already exists
145+
if ($APIApp.identifierUris -contains $DesiredIdentifierUri) {
146+
Write-Information "Application Identifier URI '$DesiredIdentifierUri' already set, skipping."
147+
$IdentifierUriUpdated = $true
148+
} else {
149+
Write-Information "Setting Application Identifier URI to '$DesiredIdentifierUri'"
150+
$IdentifierUriBody = @{
151+
identifierUris = @($DesiredIdentifierUri)
152+
}
153+
$IdentifierUriUpdated = $false
154+
for ($Attempt = 1; $Attempt -le 6; $Attempt++) {
155+
try {
156+
New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/applications/$($APIApp.id)" -AsApp $true -NoAuthCheck $true -type PATCH -body $IdentifierUriBody -maxRetries 3 | Out-Null
157+
Write-Information "Successfully set identifier URI on attempt $Attempt"
158+
$IdentifierUriUpdated = $true
159+
break
160+
} catch {
161+
$ErrorMsg = $_.Exception.Message
162+
Write-Information "Identifier URI update attempt $Attempt failed: $ErrorMsg"
163+
164+
$IsNotReplicatedYet = $ErrorMsg -match "Resource '.*' does not exist or one of its queried reference-property objects are not present"
165+
$IsConflict = $ErrorMsg -match "Another object with the same value for property identifierUris already exists" -or $ErrorMsg -match "Property identifierUris is invalid"
166+
167+
if ($IsConflict) {
168+
Write-Warning "Identifier URI conflict detected: $ErrorMsg"
169+
Write-Information "This may indicate the URI is already in use by another application or the app registration needs manual cleanup."
170+
throw
144171
}
145-
continue
172+
173+
if ($IsNotReplicatedYet -and $Attempt -lt 6) {
174+
$DelaySeconds = 3
175+
Write-Information "Application object not yet replicated for identifier URI update (attempt $Attempt of 6). Retrying in $DelaySeconds second(s)."
176+
Start-Sleep -Seconds $DelaySeconds
177+
try {
178+
$APIApp = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications(appid='$($APIApp.appId)')" -NoAuthCheck $true -AsApp $true
179+
Write-Information "Re-fetched app: identifierUris=$($APIApp.identifierUris -join ', ')"
180+
} catch {
181+
Write-Information "Application lookup retry failed before identifier URI retry: $($_.Exception.Message)"
182+
}
183+
continue
184+
}
185+
186+
if ($Attempt -ge 6) {
187+
Write-Warning "Failed to set identifier URI after $Attempt attempts. Last error: $ErrorMsg"
188+
}
189+
throw
146190
}
147-
throw
148191
}
149192
}
150193

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
function Repair-CippApiIdentifierUri {
2+
<#
3+
.SYNOPSIS
4+
Validates and repairs the Application ID URI (api://{appId}) for a CIPP API client
5+
.DESCRIPTION
6+
Checks if an application has the correct identifier URI set (api://{appId}) and fixes it if missing or incorrect.
7+
This is required for client_credentials (app-only) authentication to work properly with EasyAuth.
8+
.PARAMETER AppId
9+
The Application (Client) ID of the app to check/repair
10+
.PARAMETER ApplicationObjectId
11+
Optional. The object ID of the application. If not provided, will be looked up.
12+
.EXAMPLE
13+
Repair-CippApiIdentifierUri -AppId '12345678-1234-1234-1234-123456789012'
14+
.OUTPUTS
15+
PSCustomObject with properties:
16+
- Fixed: boolean indicating if a fix was applied
17+
- PreviousUri: the previous identifier URI (if any)
18+
- CurrentUri: the current/fixed identifier URI
19+
- Message: description of what happened
20+
#>
21+
[CmdletBinding(SupportsShouldProcess)]
22+
param(
23+
[Parameter(Mandatory = $true)]
24+
[string]$AppId,
25+
26+
[Parameter(Mandatory = $false)]
27+
[string]$ApplicationObjectId
28+
)
29+
30+
try {
31+
# Get the application details
32+
$App = if ($ApplicationObjectId) {
33+
New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications/$ApplicationObjectId" -NoAuthCheck $true -AsApp $true -ErrorAction Stop
34+
} else {
35+
$Apps = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications?`$filter=appId eq '$AppId'&`$select=id,appId,identifierUris" -NoAuthCheck $true -AsApp $true -ErrorAction Stop
36+
if ($Apps -is [array] -and $Apps.Count -gt 0) {
37+
$Apps[0]
38+
} elseif ($Apps.id) {
39+
$Apps
40+
} else {
41+
throw "Application with AppId '$AppId' not found"
42+
}
43+
}
44+
45+
$DesiredUri = "api://$($App.appId)"
46+
$CurrentUris = @($App.identifierUris)
47+
48+
Write-Information "Application '$($App.appId)': Current identifier URIs: $($CurrentUris -join ', ')"
49+
50+
# Check if the desired URI is already present
51+
if ($CurrentUris -contains $DesiredUri) {
52+
return [PSCustomObject]@{
53+
Fixed = $false
54+
PreviousUri = $CurrentUris -join ', '
55+
CurrentUri = $DesiredUri
56+
Message = "Identifier URI '$DesiredUri' already correctly configured"
57+
}
58+
}
59+
60+
# Need to add/fix the URI
61+
Write-Information "Identifier URI missing or incorrect. Setting to '$DesiredUri'"
62+
63+
if ($PSCmdlet.ShouldProcess($App.appId, "Set identifier URI to '$DesiredUri'")) {
64+
$PatchBody = @{
65+
identifierUris = @($DesiredUri)
66+
}
67+
68+
$Retries = 0
69+
$MaxRetries = 3
70+
$Success = $false
71+
72+
while (-not $Success -and $Retries -lt $MaxRetries) {
73+
try {
74+
$Retries++
75+
New-GraphPOSTRequest -uri "https://graph.microsoft.com/v1.0/applications/$($App.id)" -AsApp $true -NoAuthCheck $true -type PATCH -body $PatchBody -maxRetries 1 | Out-Null
76+
$Success = $true
77+
Write-Information "Successfully set identifier URI on attempt $Retries"
78+
} catch {
79+
$ErrorMsg = $_.Exception.Message
80+
Write-Warning "Attempt $Retries to set identifier URI failed: $ErrorMsg"
81+
82+
if ($Retries -lt $MaxRetries) {
83+
Start-Sleep -Seconds 2
84+
} else {
85+
throw "Failed to set identifier URI after $MaxRetries attempts: $ErrorMsg"
86+
}
87+
}
88+
}
89+
90+
return [PSCustomObject]@{
91+
Fixed = $true
92+
PreviousUri = $CurrentUris -join ', '
93+
CurrentUri = $DesiredUri
94+
Message = "Identifier URI successfully set to '$DesiredUri'"
95+
}
96+
}
97+
} catch {
98+
Write-Warning "Failed to repair identifier URI for AppId '$AppId': $($_.Exception.Message)"
99+
throw
100+
}
101+
}

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecApiClient.ps1

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,40 @@ function Invoke-ExecApiClient {
228228
}
229229
$Body = @($Results)
230230
}
231+
'RepairUri' {
232+
$Client = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$($Request.Body.ClientId)'"
233+
if (!$Client) {
234+
$Results = @{
235+
resultText = 'API client not found'
236+
state = 'error'
237+
}
238+
} else {
239+
try {
240+
$RepairResult = Repair-CippApiIdentifierUri -AppId $Request.Body.ClientId
241+
242+
if ($RepairResult.Fixed) {
243+
Write-LogMessage -headers $Request.Headers -API 'ExecApiClient' -message "Repaired identifier URI for $($Client.AppName) $($RepairResult.Message)" -Sev 'Info'
244+
$Results = @{
245+
resultText = "Identifier URI fixed for $($Client.AppName). $($RepairResult.Message)"
246+
state = 'success'
247+
}
248+
} else {
249+
$Results = @{
250+
resultText = "Identifier URI already correct for $($Client.AppName). $($RepairResult.Message)"
251+
state = 'info'
252+
}
253+
}
254+
} catch {
255+
$ErrorMessage = Get-CippException -Exception $_
256+
Write-LogMessage -headers $Request.Headers -API 'ExecApiClient' -message "Failed to repair identifier URI for $($Client.AppName) $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage
257+
$Results = @{
258+
resultText = "Failed to repair identifier URI for $($Client.AppName) $($ErrorMessage.NormalizedError)"
259+
state = 'error'
260+
}
261+
}
262+
}
263+
$Body = @($Results)
264+
}
231265
'Delete' {
232266
try {
233267
if ($Request.Body.ClientId) {

0 commit comments

Comments
 (0)