diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 0000000..8681531 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,39 @@ +title: "" +body: + - type: markdown + attributes: + value: | + Ideas is for shaping something before it becomes a request. Say what you + are trying to achieve rather than the control you picture, and it will be + clear whether the application should grow a feature or already has one. + + - type: dropdown + id: version + attributes: + label: Which version are you using? + options: + - 4.0 beta + - 3.x + - Neither yet, just looking + validations: + required: true + + - type: textarea + id: goal + attributes: + label: What are you trying to achieve? + validations: + required: true + + - type: textarea + id: idea + attributes: + label: How do you picture it working? + + - type: textarea + id: scale + attributes: + label: How often, and at what scale? + description: > + How many tenants, how many policies, how often you do it. Scale changes + the answer more than anything else here. diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml new file mode 100644 index 0000000..eade962 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/q-a.yml @@ -0,0 +1,64 @@ +title: "[Question] " +labels: ["needs-triage"] +body: + - type: markdown + attributes: + value: | + Most questions here turn out to be one of four things: a sign-in method + the embedded window cannot complete, a list that looks short because the + version you are on stops paging, a permission the app registration does + not hold, or an object type that has no public Graph endpoint. The fields + below let that be spotted straight away. + + - type: dropdown + id: version + attributes: + label: Version + options: + - 4.0 beta + - 3.x + validations: + required: true + + - type: dropdown + id: signin + attributes: + label: How do you sign in? + options: + - Interactive, embedded window (the default in v3) + - Interactive, Web Account Manager (WAM) + - Interactive, system browser (the default in v4) + - Device code + - Application and secret + - Application and certificate + - Managed identity or federated credential + - Bring your own token + validations: + required: true + + - type: dropdown + id: cloud + attributes: + label: Cloud + options: + - Public (commercial) + - US Government (GCC High) + - US Government (DoD) + - China (21Vianet) + validations: + required: true + + - type: textarea + id: question + attributes: + label: What are you trying to do? + validations: + required: true + + - type: textarea + id: tried + attributes: + label: What have you tried, and what happened? + description: > + Paste any error text here. **Remove tenant identifiers, user names and + tokens first.** diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..9c07b7c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# GitHub renders this as the "Sponsor" button on the repository page +# (public repositories, default branch). Buy Me a Coffee takes the +# username, not the URL: https://buymeacoffee.com/MickeK +buy_me_a_coffee: MickeK diff --git a/.github/ISSUE_TEMPLATE/bug-v3.yml b/.github/ISSUE_TEMPLATE/bug-v3.yml new file mode 100644 index 0000000..b4ec919 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-v3.yml @@ -0,0 +1,116 @@ +name: Bug report (version 3.x) +description: Something is wrong in the current release. Windows only. +title: "[3.x] " +labels: ["bug", "v3", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Version 3 stays supported until 4.0 leaves beta. + + Before filing, check whether 4.0 already fixes it. Several long-standing + reports here are addressed there: sign-in with passkeys and other + phishing-resistant methods, lists that stopped at 20, 100 or a few + hundred objects, sovereign cloud sign-in, and running without a user + present. The 4.0 beta lives on the `v4` branch. + + - type: checkboxes + id: preflight + attributes: + label: Before reporting + options: + - label: > + If my sign-in involves a passkey, security key, Windows Hello or a + phishing-resistant policy: I know the embedded sign-in window cannot + complete those, and I have said so below rather than reporting it as + a broken login. + required: true + - label: > + I searched existing issues and discussions, including closed ones. + required: true + + - type: input + id: version + attributes: + label: Version + placeholder: 3.10.3 + validations: + required: true + + - type: dropdown + id: signin + attributes: + label: How did you sign in? + options: + - Interactive, embedded window (the default) + - Interactive, other + - Application and secret + - Application and certificate + - Not signed in / sign-in is the problem + validations: + required: true + + - type: dropdown + id: cloud + attributes: + label: Cloud + options: + - Public (commercial) + - US Government (GCC High) + - US Government (DoD) + - China (21Vianet) + validations: + required: true + + - type: input + id: powershell + attributes: + label: PowerShell version + description: Run `$PSVersionTable.PSVersion`. + placeholder: "5.1.22621.4391" + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Which part of the application? + options: + - Sign-in and authentication + - Export + - Import + - Copy + - Compare + - Documentation output + - Assignments, groups or filters + - Bulk or silent operations + - ADMX or tools + - The user interface itself + - Something else + validations: + required: true + + - type: textarea + id: what + attributes: + label: What happened, and what did you expect instead? + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + validations: + required: true + + - type: textarea + id: log + attributes: + label: Log + description: > + The relevant lines, or the error text. **Remove tenant identifiers, user + names, access tokens and secrets before pasting.** + render: text + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug-v4.yml b/.github/ISSUE_TEMPLATE/bug-v4.yml new file mode 100644 index 0000000..7327b65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-v4.yml @@ -0,0 +1,154 @@ +name: Bug report (version 4.0 beta) +description: Something is wrong in 4.0. Runs on Windows, macOS and Linux. +title: "[4.0] " +labels: ["bug", "v4", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for testing the beta. Four fields below do most of the work: + **how you signed in**, **which cloud**, **which operating system** and + **the log**. Reports without them usually need a round trip before + anything can happen. + + - type: checkboxes + id: preflight + attributes: + label: Before reporting + description: These three cover the majority of beta reports so far. + options: + - label: > + I read the release notes for this beta, including the breaking changes. + required: true + - label: > + If my sign-in involves a passkey, security key, Windows Hello or any + phishing-resistant policy: I enabled **Use Web Account Manager (WAM) + for login** or **Use system browser for login** in Settings, and tried + again. The embedded window cannot complete those methods. + required: true + - label: > + My problem is not Inventory Policies returning 403. That endpoint is + not published on the public Graph API, so the application cannot read + it with a normal sign-in. It is a Microsoft limitation, not a bug. + A bring-your-own-token sign-in can reach it. + required: true + + - type: input + id: version + attributes: + label: Version + description: The About dialog, or the ModuleVersion in IntuneManagement.psd1. + placeholder: 4.0.0-beta1 + validations: + required: true + + - type: dropdown + id: signin + attributes: + label: How did you sign in? + description: > + The single most useful field in this form. Roughly a third of all reports + on this project have turned out to be sign-in behaviour rather than the + feature being reported. + options: + - Interactive, embedded window + - Interactive, Web Account Manager (WAM) + - Interactive, system browser (the default) + - Device code + - Application and secret + - Application and certificate + - Managed identity or federated credential + - Bring your own token + - Not signed in / sign-in is the problem + validations: + required: true + + - type: dropdown + id: cloud + attributes: + label: Cloud + options: + - Public (commercial) + - US Government (GCC High) + - US Government (DoD) + - China (21Vianet) + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Operating system + description: 4.0 runs the full application outside Windows, so this now matters. + options: + - Windows + - macOS (Apple Silicon) + - macOS (Intel) + - Linux + validations: + required: true + + - type: input + id: powershell + attributes: + label: PowerShell edition and version + description: Run `$PSVersionTable.PSVersion` and `$PSVersionTable.PSEdition`. + placeholder: "7.4.6, Core" + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Which part of the application? + options: + - Sign-in and authentication + - Export + - Import + - Copy + - Compare + - Documentation output + - Assignments, groups or filters + - Bulk operations + - ADMX or tools + - The user interface itself + - Automation through the PowerShell commands + - Something else + validations: + required: true + + - type: input + id: objecttype + attributes: + label: Which object type, if it is specific to one + placeholder: Settings Catalog, Conditional Access, Win32 app, ... + + - type: textarea + id: what + attributes: + label: What happened, and what did you expect instead? + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. Sign in to ... + 2. Open ... + 3. Click ... + validations: + required: true + + - type: textarea + id: log + attributes: + label: Log + description: > + The relevant lines from the log file, or the error text. **Remove tenant + identifiers, user names, access tokens and secrets before pasting.** If + an exported policy file is needed, redact it or use one from a lab tenant. + render: text + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c29d6df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,17 @@ +# Turns off the "open a blank issue" escape hatch, so every report arrives +# through a form with the fields that make it answerable. Questions go to +# Discussions, which is where most of them already end up. +blank_issues_enabled: false +contact_links: + - name: Question, or not sure it is a bug + url: https://github.com/Micke-K/IntuneManagement/discussions/categories/q-a + about: Ask in Q&A. Most "it does not show my policies" reports are answered there in a day. + - name: Feature idea worth discussing first + url: https://github.com/Micke-K/IntuneManagement/discussions/categories/ideas + about: Ideas that are still taking shape belong here. A concrete request can go straight to an issue. + - name: Version 4.0 beta feedback + url: https://github.com/Micke-K/IntuneManagement/discussions/categories/announcements + about: Testing the 4.0 beta? Start at the pinned announcement, which lists what to try first. + - name: Security vulnerability + url: https://github.com/Micke-K/IntuneManagement/security/advisories/new + about: Never report a security problem in a public issue. Use private reporting. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..25cc7ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,71 @@ +name: Feature request +description: Something the application should do and does not. +title: "[Request] " +labels: ["enhancement", "needs-triage"] +body: + - type: markdown + attributes: + value: | + If the idea is still taking shape, [Ideas in + Discussions](https://github.com/Micke-K/IntuneManagement/discussions/categories/ideas) + is the better room. Use this form when you can describe the outcome you + want. + + - type: dropdown + id: version + attributes: + label: Which version is this for? + description: > + This one is read by a human, not by automation, so say what you mean. + A request that 4.0 already covers is worth checking against the release + notes first. + options: + - Version 4.0 + - Version 3.x + - Either + validations: + required: true + + - type: textarea + id: problem + attributes: + label: What are you trying to do? + description: > + The situation, not the solution. "I move policies between two tenants + every month and have to redo the assignments by hand" tells more than + "add a button". + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: What would you like it to do? + validations: + required: true + + - type: textarea + id: workaround + attributes: + label: How do you handle it today? + description: Including "not at all", which is useful to know. + + - type: dropdown + id: area + attributes: + label: Which part of the application? + options: + - Export + - Import + - Copy + - Compare + - Documentation output + - Assignments, groups or filters + - Bulk operations + - ADMX or tools + - The user interface + - Automation through the PowerShell commands + - A policy type that is not supported yet + - Something else + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6f6f3d5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,47 @@ + + + + +## What does this change? + + + +## Related issue + + + +## How was it tested? + + + +- Version: +- Cloud: +- Operating system and PowerShell version: +- Tests run: + +## Anything a reviewer should know + + diff --git a/.gitignore b/.gitignore index c33ff82..e5d849d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ Extensions_dev/ .gitignore CloudAPIPowerShellManagement.log +AGENTS.md +CLAUDE.md diff --git a/CloudAPIPowerShellManagement.psd1 b/CloudAPIPowerShellManagement.psd1 index 50dc672..e5fa987 100644 --- a/CloudAPIPowerShellManagement.psd1 +++ b/CloudAPIPowerShellManagement.psd1 @@ -12,7 +12,7 @@ RootModule = 'CloudAPIPowerShellManagement.psm1' # Version number of this module. -ModuleVersion = '3.10.3' +ModuleVersion = '3.11.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/Core.psm1 b/Core.psm1 index 5b1f98c..16690f7 100644 --- a/Core.psm1 +++ b/Core.psm1 @@ -660,7 +660,7 @@ function Show-AboutDialog Set-XamlProperty $script:dlgAbout "lstModules" "ItemsSource" $loadedItems - Add-XamlEvent $script:dlgAbout "linkSource" "Add_RequestNavigate" ({ [System.Diagnostics.Process]::Start($_.Uri.AbsoluteUri); $_.Handled = $true }) + Add-XamlEvent $script:dlgAbout "linkSource" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) Show-ModalForm "About" $script:dlgAbout } @@ -674,8 +674,10 @@ function Show-UpdatesDialog Add-XamlEvent $script:dlgUpdates "btnClose" "add_click" { $script:dlgUpdates = $null - Show-ModalObject - } + Show-ModalObject + } + + Add-XamlEvent $script:dlgUpdates "linkSource" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) $fileContent = Get-Content -Raw -Path ($global:AppRootFolder + "\ReleaseNotes.md") try @@ -697,7 +699,11 @@ function Show-UpdatesDialog $params.Add("UseBasicParsing", $true) } - $content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/ReleaseNotes.md" @params + # The notes at the newest 3.x release tag, never at the default branch: that + # branch will carry version 4 once the branches are renamed. + $latestVer = Get-LatestGitHubVersion $params + $notesRef = if($latestVer) { "?ref=$($latestVer.ToString())" } else { "" } + $content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/ReleaseNotes.md$notesRef" @params if($content) { $txt = [System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($content.content))) @@ -722,6 +728,37 @@ function Show-UpdatesDialog Show-ModalForm "Release Notes" $script:dlgUpdates -HideButtons } +# Newest published 3.x release on GitHub, or $null. +# +# releases/latest answers the newest release of ANY version. The day 4.0.0 is +# published it would tell every 3.x installation to upgrade to a breaking +# change, and reading the manifest on the default branch stops working once the +# branches are renamed for version 4. The releases list filtered to this major +# is the one source that survives both. Pre-releases and drafts are skipped. +function Get-LatestGitHubVersion +{ + param($Params = @{}) + + $latest = $null + try + { + $releases = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/releases?per_page=100" @Params + foreach($release in @($releases)) + { + if($release.draft -or $release.prerelease) { continue } + $ver = $null + try { $ver = [version](([string]$release.tag_name) -replace '^v','') } catch { continue } + if($ver.Major -ne 3) { continue } + if($null -eq $latest -or $ver -gt $latest) { $latest = $ver } + } + } + catch + { + Write-Log "Failed to list GitHub releases: $($_.Exception.Message)" 2 + } + return $latest +} + function Get-IsLatestVersion { if($global:MainAppStarted -ne $true) @@ -740,35 +777,7 @@ function Get-IsLatestVersion $params.Add("UseBasicParsing", $true) } - $content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/releases/latest" @params - if($content.Name) - { - try - { - $gitHubVer = [version]$content.Name - } - catch {} - } - - if($null -eq $gitHubVer) - { - $params = @{} - $proxyURI = Get-ProxyURI - if($proxyURI) - { - $params.Add("proxy", $proxyURI) - $params.Add("UseBasicParsing", $true) - } - - $content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/CloudAPIPowerShellManagement.psd1" @params - $gitHubText = [System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($content.content))) - $gitHubInfo = Get-ModuleDataTable $gitHubText - try - { - $gitHubVer = [version]$gitHubInfo.ModuleVersion - } - catch {} - } + $gitHubVer = Get-LatestGitHubVersion $params if(-not $gitHubVer) { @@ -2485,9 +2494,9 @@ function Get-MainWindow { $script:welcomeForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\Welcome.xaml") -AddVariables - Add-XamlEvent $script:welcomeForm "gitHubLink" "Add_RequestNavigate" ({ [System.Diagnostics.Process]::Start($_.Uri.AbsoluteUri); $_.Handled = $true }) - Add-XamlEvent $script:welcomeForm "licenseLink" "Add_RequestNavigate" ({ [System.Diagnostics.Process]::Start($_.Uri.AbsoluteUri); $_.Handled = $true }) - Add-XamlEvent $script:welcomeForm "addCustomApp" "Add_RequestNavigate" ({ [System.Diagnostics.Process]::Start($_.Uri.AbsoluteUri); $_.Handled = $true }) + Add-XamlEvent $script:welcomeForm "gitHubLink" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) + Add-XamlEvent $script:welcomeForm "licenseLink" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) + Add-XamlEvent $script:welcomeForm "addCustomApp" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) Add-XamlEvent $script:welcomeForm "chkAcceptConditions" "add_click" { $global:btnAcceptConditions.IsEnabled = ($this.IsChecked -eq $true) @@ -2522,7 +2531,7 @@ function Get-MainWindow if($appIdChangeInformed -ne "true") { $script:oldAzureAppForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\OldAzureApp.xaml") - Add-XamlEvent $script:oldAzureAppForm "addCustomApp" "Add_RequestNavigate" ({ [System.Diagnostics.Process]::Start($_.Uri.AbsoluteUri); $_.Handled = $true }) + Add-XamlEvent $script:oldAzureAppForm "addCustomApp" "Add_RequestNavigate" ({ Start-Process $_.Uri.AbsoluteUri; $_.Handled = $true }) Add-XamlEvent $script:oldAzureAppForm "btnOK" "add_click" { if((Get-XamlProperty $script:oldAzureAppForm "chkChangeApp" "IsChecked") -eq $true) { diff --git a/Extensions/EndpointManager.psm1 b/Extensions/EndpointManager.psm1 index 75341c2..fee121a 100644 --- a/Extensions/EndpointManager.psm1 +++ b/Extensions/EndpointManager.psm1 @@ -2906,7 +2906,7 @@ function Start-PostExportApplications } else { - Write-Log "Cound not find encryption file" + Write-Log "Could not find encryption file" } } } @@ -2966,7 +2966,7 @@ function Add-ScriptExportApplications function Start-PostGetApplications { param($obj, $objectType) - if($obj.Object.dependentAppCount -is [Int] -and ($obj.Object.dependentAppCount -gt 0 -or $obj.Object.supersededAppCount -gt 0)) { + if(($obj.Object.dependentAppCount -as [int]) -gt 0 -or ($obj.Object.supersededAppCount -as [int]) -gt 0) { $relationships = (Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.Id)/relationships?`$filter=targetType%20eq%20microsoft.graph.mobileAppRelationshipType%27child%27").value $dependencyApps = @() $supersededApps = @() diff --git a/Extensions/MSALAuthentication.psm1 b/Extensions/MSALAuthentication.psm1 index 32fc7d2..e9d3e32 100644 --- a/Extensions/MSALAuthentication.psm1 +++ b/Extensions/MSALAuthentication.psm1 @@ -10,7 +10,7 @@ This module manages Authentication for the application with MSAL. It is also res #> function Get-ModuleVersion { - '3.9.8a' + '3.9.9' } $global:msalAuthenticator = $null @@ -137,12 +137,28 @@ function Invoke-InitializeModule Description = "Use WAM for enhanced login methods" }) "MSAL" + Add-SettingsObject (New-Object PSObject -Property @{ + Title = "Use System Browser for login" + Key = "UseSystemBrowser" + Type = "Boolean" + DefaultValue = $false + Description = "Use the default system browser (Edge/Chrome/Firefox) for interactive login instead of the embedded WebView. Required for passkey / FIDO2 sign-in. Redirect URI is forced to http://localhost when enabled. Takes precedence over WAM. Note: Requires restart" + }) "MSAL" + $script:MSALUseWAM = Get-SettingValue "UseWAM" if($script:MSALUseWAM -and $PSVersionTable.PSVersion.Major -lt 7) { Write-Log "WAM is only supported in PowerShell 7 and later. Disabling WAM" 2 $script:MSALUseWAM = $false } + # System Browser takes precedence over WAM: passkey/FIDO2 login works in the real browser + # but not in the WAM pane or the embedded WebView. If both are enabled, disable WAM. + $script:MSALUseSystemBrowser = Get-SettingValue "UseSystemBrowser" + if($script:MSALUseSystemBrowser -and $script:MSALUseWAM) { + Write-Log "Both UseWAM and UseSystemBrowser are enabled - the system browser takes precedence for interactive login" 2 + $script:MSALUseWAM = $false + } + Add-MSALPrereq } @@ -931,9 +947,21 @@ function Get-MSALApp [void]$appBuilder.WithAuthority($authority) - if($appInfo.RedirectUri) { [void]$appBuilder.WithRedirectUri($appInfo.RedirectUri) } + # System Browser mode: MSAL's system-browser flow only accepts loopback redirects, + # so any custom redirect URI (nativeclient, ms-appx-web://, etc.) is replaced with + # http://localhost. The app registration in Entra must have this loopback URI added + # under the "Mobile and desktop applications" platform for the login to succeed. + $redirectUri = $appInfo.RedirectUri + if($script:MSALUseSystemBrowser -and $redirectUri -and ($redirectUri -notmatch '^http://localhost')) { + Write-LogDebug "UseSystemBrowser: overriding redirect URI '$redirectUri' with http://localhost" + $redirectUri = "http://localhost" + } + elseif($script:MSALUseSystemBrowser -and -not $redirectUri) { + $redirectUri = "http://localhost" + } + if($redirectUri) { [void]$appBuilder.WithRedirectUri($redirectUri) } - [void] $appBuilder.WithClientName("CloudAPIPowerShellManagement") + [void] $appBuilder.WithClientName("CloudAPIPowerShellManagement") [void] $appBuilder.WithClientVersion($PSVersionTable.PSVersion) if($script:MSALUseWAM) { @@ -1288,14 +1316,23 @@ function Connect-MSALUser [IntPtr]$ParentWindow = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle if ($ParentWindow) { - [void]$aquireTokenObj.WithParentActivityOrWindow($ParentWindow) + [void]$aquireTokenObj.WithParentActivityOrWindow($ParentWindow) + } + + # UseSystemBrowser: force MSAL to launch the default system browser instead of + # any embedded WebView. Required for passkey / FIDO2 sign-in which the embedded + # WebView cannot service. + if($script:MSALUseSystemBrowser) + { + try { [void]$aquireTokenObj.WithUseEmbeddedWebView($false) } + catch { Write-LogDebug "WithUseEmbeddedWebView unavailable: $($_.Exception.Message)" } } # If we need a consent (e.g. App is not approved in the environment) - if ($script:authenticationFailure.Classification -eq "ConsentRequired") + if ($script:authenticationFailure.Classification -eq "ConsentRequired") { - Write-Log "Interactive login with Consent prompt" - [void]$aquireTokenObj.WithPrompt([Microsoft.Identity.Client.Prompt]::Consent) + Write-Log "Interactive login with Consent prompt" + [void]$aquireTokenObj.WithPrompt([Microsoft.Identity.Client.Prompt]::Consent) } $authResult = Get-MsalAuthenticationToken $aquireTokenObj @@ -1346,8 +1383,18 @@ function Connect-MSALUser $appBuilder = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($global:appObj.ClientID) if($tenantId) { [void]$appBuilder.WithAuthority("https://$((Get-MSALAppAuthority))/$($tenantId)") } else { [void]$appBuilder.WithAuthority($global:MSALApp.Authority) } - if($global:appObj.RedirectUri) { [void]$appBuilder.WithRedirectUri($global:appObj.RedirectUri) } - + + # Match the redirect URI substitution done in Get-MSALApp - keeps this + # secondary MSAL app consistent with System Browser mode. + $tenantRedirectUri = $global:appObj.RedirectUri + if($script:MSALUseSystemBrowser -and $tenantRedirectUri -and ($tenantRedirectUri -notmatch '^http://localhost')) { + $tenantRedirectUri = "http://localhost" + } + elseif($script:MSALUseSystemBrowser -and -not $tenantRedirectUri) { + $tenantRedirectUri = "http://localhost" + } + if($tenantRedirectUri) { [void]$appBuilder.WithRedirectUri($tenantRedirectUri) } + Add-MSALProxy $appBuilder $app = $appBuilder.Build() @@ -1366,7 +1413,12 @@ function Connect-MSALUser $AquireTokenObj = $app.AcquireTokenInteractive($tmpScope) #[void]$AquireTokenObj.WithAccount($authResult.Account) [void]$AquireTokenObj.WithLoginHint($authResult.Account.Username) - [void]$AquireTokenObj.WithPrompt([Microsoft.Identity.Client.Prompt]::NoPrompt) + [void]$AquireTokenObj.WithPrompt([Microsoft.Identity.Client.Prompt]::NoPrompt) + if($script:MSALUseSystemBrowser) + { + try { [void]$AquireTokenObj.WithUseEmbeddedWebView($false) } + catch { Write-LogDebug "WithUseEmbeddedWebView unavailable: $($_.Exception.Message)" } + } $tmpResults = Get-MsalAuthenticationToken $AquireTokenObj } diff --git a/README.md b/README.md index 2b43bc9..8865227 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # IntuneManagement with PowerShell and WPF UI +> **Version 4.0 is in beta.** A full rewrite that also runs on macOS and Linux, +> with every operation available as a PowerShell command for automation. +> Try it from the [`v4` branch](../../tree/v4) or the [4.0.0-beta1 pre-release](../../releases/tag/4.0.0-beta1). +> Version 3 stays here and stays supported until 4.0 is final. +> Report version 4 problems with the *Bug report (version 4.0 beta)* form. +

diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 5203a93..8d6dad3 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,36 @@ # Release Notes + +## About version 4 + +Version 4.0 is in beta on the `v4` branch ([4.0.0-beta1](https://github.com/Micke-K/IntuneManagement/releases/tag/4.0.0-beta1)). It is a rewrite: a single PowerShell module +with `IM`-prefixed commands, an Avalonia UI that runs on Windows, macOS and Linux, and a +public automation API. Exports made with 3.x import into 4.0. Version 3 stays supported +here until 4.0 is final. From 3.11.0 the update check only offers 3.x releases; older +3.x installations will be offered 4.0.0 once it is published as a final release. + +## 3.11.0 - 2026-09-23 + +**New features** + +- **Sign in with the system browser**
+ Interactive sign-in can run in your default browser instead of the embedded window, + which is where passkeys, security keys and other phishing-resistant methods work. + Turn on **Use system browser for login** in Settings.
+ Based on [Issue 435](https://github.com/Micke-K/IntuneManagement/issues/435) + and [Discussion 425](https://github.com/Micke-K/IntuneManagement/discussions/425)
+ +**Fixes** + +- Links to GitHub in the About, Updates and Welcome dialogs did not open the browser
+ Based on [Issue 428](https://github.com/Micke-K/IntuneManagement/issues/428)
+- Silent bulk compare failed with an `op_Addition` error and wrote no result CSV when + using the **Exported Files with Intune Objects (Id)** provider
+ [PR 429](https://github.com/Micke-K/IntuneManagement/pull/429) by McKenzieCo
+- Typo in the missing-permissions message
+ [PR 424](https://github.com/Micke-K/IntuneManagement/pull/424) by BuggyAl
+- The update check only offers 3.x releases and reads the release notes of the newest + 3.x release, so a version 4 release is never offered to a version 3 installation.
+ ## 3.10.3 - 2026-05-11 **Fixes** diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..cf93d1e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,67 @@ +# Security Policy + +## Supported versions + +| Version | Branch | Status | +|---|---|---| +| 4.0 beta | `v4` | Pre-release. Fixes go here. | +| 3.x | default branch | Supported until 4.0 leaves beta. Security fixes only after that. | +| 2.x and earlier | - | Not supported. | + +## Reporting a vulnerability + +**Do not open a public issue for a security problem.** + +Use GitHub's private vulnerability reporting: go to the **Security** tab of this +repository and choose **Report a vulnerability**. That opens a private thread +visible only to the maintainer, and it works even though this repository has no +public contact address. + +If that is unavailable to you, contact the maintainer through the link in the +repository profile and ask for a private channel before sending any detail. + +### What to include + +The more of this you can provide, the faster it can be confirmed: + +- The version (`4.0.0-beta1`, `3.10.3`, ...) and how you installed it. +- PowerShell edition and version, and the operating system. +- What an attacker can do, not only what looks wrong. +- Steps to reproduce, ideally against a lab tenant. +- Whether it needs an already signed-in session, and what permissions that + session holds. + +**Never include real tenant identifiers, access tokens, client secrets, +certificates or exported policy files from a production tenant.** Redact them, or +reproduce against a lab tenant. + +### What to expect + +This is a single-maintainer project worked on outside business hours. An +acknowledgement usually takes a few days. A fix ships in the next release for +the affected branch, and the release notes credit the reporter unless you ask +otherwise. + +## Scope + +This is an administrative client that runs on your own machine with credentials +you supply. Reports that are in scope include: + +- Credentials, tokens or secrets written somewhere they should not be, or kept + in memory or on disk longer than needed. +- A path where the application sends tenant data anywhere other than the Microsoft + cloud endpoint it is signed in to. +- Code execution from data the application reads: an exported policy file, an + ADMX file, a documentation template or an imported settings file. +- Anything that causes an operation to run against a different tenant than the + one selected. + +The following are **not** vulnerabilities in this project: + +- An account having more permission in Microsoft Intune than you expected. That + is tenant configuration, not this application. +- Anything requiring an attacker who already controls the machine or the signed-in + session. At that point they can use the Microsoft Graph API directly. +- Missing hardening that Microsoft Entra or Intune is responsible for, such as + token lifetime or conditional access. +- Results from a scanner with no demonstrated impact.