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. +