fix: fall back to device code when browser auth fails on macOS#290
Conversation
MsalBrowserCredential.GetTokenAsync now catches PlatformNotSupportedException (thrown by MSAL on macOS 15.x) and wraps it as MsalAuthenticationFailedException so it is properly classified rather than appearing as an unexpected error. AuthenticateInteractivelyAsync detects this platform exception at the token acquisition site and automatically falls back to device code flow, allowing a365 setup to complete on macOS without manual workarounds. Extracts CreateDeviceCodeCredential helper to eliminate the duplicated DeviceCodeCredential setup between the normal device code path and the fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moved device code authentication info logs to calling sites so they're only shown when relevant. Changed platform-not-supported log level in MsalBrowserCredential from Error to Warning to better reflect recoverable conditions.
Replaced PowerShell install logic with a Bash script (install-cli.sh) that handles cleaning, building, packing, and installing the Agent 365 CLI from a local NuGet package. PowerShell now locates bash and invokes the script, unifying installation across platforms and simplifying maintenance.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
This pull request fixes a critical authentication issue on macOS 15.x where PlatformNotSupportedException is thrown during browser-based authentication. The fix implements automatic fallback to device code flow when browser authentication is unavailable. Additionally, the PR consolidates install scripts to eliminate duplication by using a single bash implementation across all platforms.
Changes:
- Implements automatic fallback from browser authentication to device code flow when
PlatformNotSupportedExceptionis encountered - Improves auth logging by using
LogWarninginstead ofLogErrorfor recoverable platform exceptions and moving device code messages to call sites - Consolidates install scripts: new
install-cli.sh(bash) provides single cross-platform implementation;install-cli.ps1becomes thin wrapper that locates Git Bash on Windows
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/.../Services/MsalBrowserCredential.cs |
Catches PlatformNotSupportedException during token acquisition and wraps it as MsalAuthenticationFailedException; changes log level from error to warning for this recoverable condition |
src/.../Services/AuthenticationService.cs |
Implements fallback logic to retry with device code flow when browser auth fails; extracts CreateDeviceCodeCredential helper to eliminate duplication; moves device code log messages to call sites |
scripts/cli/install-cli.sh |
New cross-platform bash script that handles build, pack, and install workflow; eliminates ~100 lines of duplicated PowerShell logic |
scripts/cli/install-cli.ps1 |
Replaced implementation with thin wrapper that locates Git Bash on Windows and delegates to install-cli.sh |
Comments suppressed due to low confidence (2)
scripts/cli/install-cli.sh:1
- According to the custom coding guidelines (Rule 2), all C# and script files should have proper Microsoft copyright headers. This new script file is missing the required copyright header. Please add the following at the top of the file (after the shebang line):
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
#!/usr/bin/env bash
src/Microsoft.Agents.A365.DevTools.Cli/Services/AuthenticationService.cs:250
- The new fallback behavior from browser authentication to device code flow is a significant change to the authentication flow, but there are no tests covering this specific scenario. Consider adding a test that verifies the fallback occurs correctly when PlatformNotSupportedException is thrown. This would help ensure the fix continues to work as macOS and authentication libraries evolve.
catch (MsalAuthenticationFailedException ex) when (useInteractiveBrowser && ex.InnerException is PlatformNotSupportedException)
{
_logger.LogWarning("Browser authentication is not supported on this platform, falling back to device code flow...");
_logger.LogInformation("Using device code authentication...");
_logger.LogInformation("Please sign in with your Microsoft account");
var deviceCodeCredential = CreateDeviceCodeCredential(effectiveTenantId, effectiveClientId);
tokenResult = await deviceCodeCredential.GetTokenAsync(tokenRequestContext, default);
}
- install-cli.ps1: replace $IsWindows with $env:OS -eq 'Windows_NT' for PowerShell 5.1 compatibility ($IsWindows is undefined/null in PS 5.1) - install-cli.sh: add copyright header, set -u, show dotnet tool update output - AuthenticationService: extract CreateBrowserCredential and CreateDeviceCodeCredential as protected virtual TokenCredential to enable test injection without changing the DI contract - AuthenticationServiceTests: add ThrowingTokenCredential, StubTokenCredential, TestableAuthenticationService and 4 tests covering macOS PlatformNotSupportedException fallback behavior Follows up on #290 (macOS browser auth platform fallback). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#292) * fix: PS 5.1 install compatibility and macOS auth testability follow-up - install-cli.ps1: replace $IsWindows with $env:OS -eq 'Windows_NT' for PowerShell 5.1 compatibility ($IsWindows is undefined/null in PS 5.1) - install-cli.sh: add copyright header, set -u, show dotnet tool update output - AuthenticationService: extract CreateBrowserCredential and CreateDeviceCodeCredential as protected virtual TokenCredential to enable test injection without changing the DI contract - AuthenticationServiceTests: add ThrowingTokenCredential, StubTokenCredential, TestableAuthenticationService and 4 tests covering macOS PlatformNotSupportedException fallback behavior Follows up on #290 (macOS browser auth platform fallback). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix parameter order for CreateDeviceCodeCredential method Update CreateDeviceCodeCredential to use (clientId, tenantId) order in both implementation and tests. Update all call sites to match new signature. Add xUnit collection to authentication tests to prevent parallel execution and avoid shared resource conflicts. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: fall back to device code when browser auth fails on macOS MsalBrowserCredential.GetTokenAsync now catches PlatformNotSupportedException (thrown by MSAL on macOS 15.x) and wraps it as MsalAuthenticationFailedException so it is properly classified rather than appearing as an unexpected error. AuthenticateInteractivelyAsync detects this platform exception at the token acquisition site and automatically falls back to device code flow, allowing a365 setup to complete on macOS without manual workarounds. Extracts CreateDeviceCodeCredential helper to eliminate the duplicated DeviceCodeCredential setup between the normal device code path and the fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Improve device code auth logging and error handling Moved device code authentication info logs to calling sites so they're only shown when relevant. Changed platform-not-supported log level in MsalBrowserCredential from Error to Warning to better reflect recoverable conditions. * Refactor CLI install: delegate to Bash script Replaced PowerShell install logic with a Bash script (install-cli.sh) that handles cleaning, building, packing, and installing the Agent 365 CLI from a local NuGet package. PowerShell now locates bash and invokes the script, unifying installation across platforms and simplifying maintenance. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…soft#290) * fix: fall back to device code when browser auth fails on macOS MsalBrowserCredential.GetTokenAsync now catches PlatformNotSupportedException (thrown by MSAL on macOS 15.x) and wraps it as MsalAuthenticationFailedException so it is properly classified rather than appearing as an unexpected error. AuthenticateInteractivelyAsync detects this platform exception at the token acquisition site and automatically falls back to device code flow, allowing a365 setup to complete on macOS without manual workarounds. Extracts CreateDeviceCodeCredential helper to eliminate the duplicated DeviceCodeCredential setup between the normal device code path and the fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Improve device code auth logging and error handling Moved device code authentication info logs to calling sites so they're only shown when relevant. Changed platform-not-supported log level in MsalBrowserCredential from Error to Warning to better reflect recoverable conditions. * Refactor CLI install: delegate to Bash script Replaced PowerShell install logic with a Bash script (install-cli.sh) that handles cleaning, building, packing, and installing the Agent 365 CLI from a local NuGet package. PowerShell now locates bash and invokes the script, unifying installation across platforms and simplifying maintenance. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
microsoft#292) * fix: PS 5.1 install compatibility and macOS auth testability follow-up - install-cli.ps1: replace $IsWindows with $env:OS -eq 'Windows_NT' for PowerShell 5.1 compatibility ($IsWindows is undefined/null in PS 5.1) - install-cli.sh: add copyright header, set -u, show dotnet tool update output - AuthenticationService: extract CreateBrowserCredential and CreateDeviceCodeCredential as protected virtual TokenCredential to enable test injection without changing the DI contract - AuthenticationServiceTests: add ThrowingTokenCredential, StubTokenCredential, TestableAuthenticationService and 4 tests covering macOS PlatformNotSupportedException fallback behavior Follows up on microsoft#290 (macOS browser auth platform fallback). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix parameter order for CreateDeviceCodeCredential method Update CreateDeviceCodeCredential to use (clientId, tenantId) order in both implementation and tests. Update all call sites to match new signature. Add xUnit collection to authentication tests to prevent parallel execution and avoid shared resource conflicts. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…RL opening - MsalBrowserCredential: catch PlatformNotSupportedException and fall back to device code flow instead of re-throwing, so macOS/Linux headless environments always get a working auth path - InteractiveGraphAuthService: eagerly acquire token before constructing GraphServiceClient to surface auth failures at construction time rather than silently returning a broken client (Gap 2 from PR #290) - InteractiveGraphAuthService: inject optional credentialFactory for testability - AuthenticationService: replace Console.WriteLine with structured logger calls in device code callback - Add BrowserHelper with cross-platform TryOpenUrl (Windows/macOS/Linux) - Remove duplicated TryOpenBrowser methods from BlueprintSubcommand and A365CreateInstanceRunner; both now call BrowserHelper.TryOpenUrl directly - Add 5 unit tests covering the eager-auth gap and credential caching behavior
…RL opening (#309) * fix: device code fallback for macOS browser auth and cross-platform URL opening - MsalBrowserCredential: catch PlatformNotSupportedException and fall back to device code flow instead of re-throwing, so macOS/Linux headless environments always get a working auth path - InteractiveGraphAuthService: eagerly acquire token before constructing GraphServiceClient to surface auth failures at construction time rather than silently returning a broken client (Gap 2 from PR #290) - InteractiveGraphAuthService: inject optional credentialFactory for testability - AuthenticationService: replace Console.WriteLine with structured logger calls in device code callback - Add BrowserHelper with cross-platform TryOpenUrl (Windows/macOS/Linux) - Remove duplicated TryOpenBrowser methods from BlueprintSubcommand and A365CreateInstanceRunner; both now call BrowserHelper.TryOpenUrl directly - Add 5 unit tests covering the eager-auth gap and credential caching behavior * fix: address PR review comments and handle Linux xdg_open_failed error - MsalBrowserCredential: also catch MsalClientException(linux_xdg_open_failed) for device code fallback on Linux/WSL (confirmed via WSL Ubuntu 24.04 repro); extract shared AcquireTokenWithDeviceCodeFallbackAsync to avoid duplication - BrowserHelper: fall back to Console.Error when logger is null so the manual URL is never silently lost regardless of caller - BlueprintSubcommand: log consent URL before TryOpenUrl and pass logger so users on headless platforms always see the URL to complete admin consent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: auto-fix public client flows, add requirements check to blueprint, clean AADSTS7000218 error - ClientAppValidator: add EnsurePublicClientFlowsEnabledAsync (Step 7) that auto-detects and enables 'Allow public client flows' on the app registration via az rest GET/PATCH — required for MSAL device code fallback on macOS/Linux; non-fatal if PATCH fails - BlueprintSubcommand: run config requirements checks (LocationCheck + ClientAppCheck) before blueprint logic, with --skip-requirements opt-out; matches AllSubcommand pattern - MsalBrowserCredential: catch AADSTS7000218/invalid_client before general MsalException to emit clean actionable error message pointing to 'a365 setup requirements' instead of stack trace - Tests: add EnsurePublicClientFlowsEnabledAsync tests (enabled/disabled/patch-fails) and CreateCommand_ShouldHaveSkipRequirementsOption test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: eliminate double auth on Linux, improve PS module error handling - MsalBrowserCredential: add shared in-memory MSAL token cache for Linux so all MsalBrowserCredential instances within one CLI invocation share the same token cache, eliminating repeated device code prompts during multi-step operations (e.g. blueprint creation + client secret creation) - BrowserHelper: remove exception object from LogWarning so xdg-open failures no longer emit a full stack trace in the output - MicrosoftGraphTokenProvider: detect missing PowerShell module error and log actionable guidance to run 'a365 setup requirements' - SetupHelpers: replace misleading "please sign in when prompted" message with one that mentions missing PS module as a common cause - PowerShellModulesRequirementCheck: auto-install missing modules via Install-Module -Scope CurrentUser -Force -AllowClobber when detected; returns Success if auto-install succeeds, Failure with manual steps if not Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add system requirement checks to all setup commands that use Graph PS auth setup blueprint was running only config checks (Location + ClientApp), missing PowerShellModulesRequirementCheck entirely. setup permissions mcp/bot/custom and setup permissions copilotstudio had no requirement checks at all. All four now gate on GetSystemRequirementChecks() before executing Graph operations, so missing Microsoft.Graph.Authentication is caught and auto-installed upfront rather than failing mid-run. Closes partial work toward #106. * Skip requirements on dry run; clarify browser fallback Requirements checks are now skipped during dry runs to prevent unintended mutations. Updated `TryOpenUrl` documentation to clarify fallback behavior and logging when browser launch fails. * fix: skip requirements on dry run, self-correct missing PS modules in Graph token acquisition - Skip system requirement checks (including auto-install) when --dry-run is set in PermissionsSubcommand (mcp/bot/custom) and CopilotStudioSubcommand to preserve non-mutating dry-run semantics - MicrosoftGraphTokenProvider now detects a missing/broken Microsoft.Graph module at runtime, auto-installs both required Graph modules, and retries token acquisition once before failing; error message clarifies auto-install was attempted if retry also fails - PowerShellModulesRequirementCheck.ExecutePowerShellCommandAsync returns stderr instead of null on failure so auto-install debug logs are actionable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: extract real JWT from Graph request headers; unify PS auth scopes to reduce login prompts Microsoft.Graph.Authentication v2+ returns an opaque (non-JWT) token from $ctx.AccessToken that is rejected when used as a Bearer token. Switch to always extracting the token from the Authorization header of a live Invoke-MgGraphRequest call, which always contains the real JWT. Add AgentIdentityBlueprint.UpdateAuthProperties.All to RequiredPermissionGrantScopes so all PowerShell-based Graph operations (OAuth2 grants, service principal lookups, and inheritable permissions) use the same scope set. This ensures Connect-MgGraph authenticates once and the access token is reused from the in-process cache for all downstream calls, reducing device code prompts from 4 to 3 on first run (1 PS + 2 MSAL for different resources). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: extract last stdout line as JWT token; add clean error for missing config - MicrosoftGraphTokenProvider: extract last non-empty stdout line as token. Connect-MgGraph in headless Linux/WSL falls back to device code and writes the 'To sign in...' prompt to stdout, contaminating the full StandardOutput string. Trimming the whole output returns a non-JWT string; taking only the last line (where $token is always written) fixes the JWT validation warning and the resulting SETUP_VALIDATION_FAILED for inheritable permissions. - ConfigFileNotFoundException: new Agent365Exception subclass for missing a365.config.json. ConfigService.LoadAsync now throws this instead of a raw FileNotFoundException, so the global handler displays a clean actionable error ('run a365 config init') instead of a full stack trace. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: unify PS token cache key in RemoveStale and DeployCommand; update test Both RemoveStaleCustomPermissionsAsync (PermissionsSubcommand) and DeployCommand used a hardcoded 2-scope array missing DelegatedPermissionGrant.ReadWrite.All. This produced a different sorted cache key than RequiredPermissionGrantScopes, causing a second Connect-MgGraph device-code prompt during 'setup blueprint' even though a token was already cached from the inheritable permissions step. Switch both sites to AuthenticationConstants.RequiredPermissionGrantScopes so all Graph token acquisitions share one cache entry per CLI invocation. Update Agent365ConfigServiceTests to expect ConfigFileNotFoundException instead of FileNotFoundException, matching the new exception type thrown by ConfigService. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: improve auth prompt UX and clarify custom permissions message MicrosoftGraphTokenProvider: replace misleading "Device Code: False" log with platform-specific guidance so users know what to expect: - Windows: "A browser window will open for authentication..." - Linux/macOS: "A device code prompt will appear below..." This avoids confusion when Connect-MgGraph auto-switches to device code in headless environments even though useDeviceCode=false was requested. PermissionsSubcommand: change "No custom blueprint permissions configured" to "No custom blueprint permissions specified in config. Skipping." to make clear this is about config content, not a system state or error condition. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: retry blueprint SP lookup on Azure AD propagation delay LookupServicePrincipalByAppIdAsync can return null for 10-30s after blueprint creation due to Azure AD eventual consistency. Use the existing RetryHelper (already used for inheritable permissions verification) to retry up to 5 times with 5s base delay / exponential backoff before throwing the 'service principal may not have propagated' error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: fix admin consent polling via MSAL token with Application.Read.All Root cause: SP lookup and consent grant queries used az CLI token (scopes=null) which lacks Application.Read.All and DelegatedPermissionGrant.ReadWrite.All. Graph silently returned HTTP 200 with empty array, leaving blueprintSpId=null and causing polling to time out after 180s even after consent was granted. Changes: - BlueprintSubcommand: pass RequiredPermissionGrantScopes to SP lookups and CheckConsentExistsAsync; resolve blueprintSpId once at start of consent flow; use new MSAL PollAdminConsentAsync when SP ID is available - AdminConsentHelper: add MSAL PollAdminConsentAsync overload using GraphApiService; add scopes param to CheckConsentExistsAsync; add progress/timeout log messages - BlueprintLookupService: add optional scopes param to GetServicePrincipalByAppIdAsync - GraphApiService: add debug logging for failed GET; fix JsonDocument disposal - CommandExecutor: add optional outputTransform to ExecuteWithStreamingAsync - MicrosoftGraphTokenProvider: reformat PS device code output to MSAL box style - AuthenticationService: reduce token cache log noise (LogInfo -> LogDebug) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: fall back to MSAL when PS Connect-MgGraph fails on any platform When PowerShell Connect-MgGraph fails (NullRef in DeviceCodeCredential on Linux, no TTY, module issues, or any other reason), fall back to MsalBrowserCredential to acquire the Graph token. On Windows this uses WAM; on Linux/macOS it uses device code with silent-first logic. The token is stored in MicrosoftGraphTokenProvider's in-process cache so subsequent calls (inheritable permissions, custom permissions) within the same CLI invocation reuse it without re-prompting. Also adds silent token acquisition attempt before device code in MsalBrowserCredential.AcquireTokenWithDeviceCodeFallbackAsync to reduce unnecessary device code prompts when a cached token exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address PR review comments - disposal, logging, exit handling, doc fixes - ConfigFileNotFoundException: derive from FileNotFoundException so existing catch sites (CleanupCommand, DeployCommand, etc.) continue to work - InteractiveGraphAuthService: remove unused Azure.Identity using; move credential factory resolution inside try/catch for consistent error wrapping - BrowserHelper: include exception object in LogWarning for structured logging - PowerShellModulesRequirementCheck: fix GenerateInstallationInstructions to produce valid PS syntax for multi-module Install-Module calls - PermissionsSubcommand / CopilotStudioSubcommand: replace Environment.Exit(1) with ExceptionHandler.ExitWithCleanup(1) to flush output before exit - MicrosoftGraphTokenProvider: dispose HttpResponseMessage after token extraction - MsalBrowserCredential: use platform-neutral path notation in cache doc comment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address second round of PR review comments - ConfigFileNotFoundException: add explicit using System.IO to avoid implicit global usings dependency - SetupHelpers: remove unnecessary ! null-forgiving operator on awaited task (it only asserts the Task itself is non-null, not the result) - BrowserHelper: use ArgumentList instead of Arguments for open/xdg-open to avoid argument parsing issues with special characters in URLs - MsalBrowserCredential: filter cached MSAL account by tenant ID before silent auth to avoid authenticating as wrong identity when multiple accounts are cached; fall through to device code if no tenant match - MicrosoftGraphTokenProvider: reuse IsPowerShellModuleMissingError in ProcessResult to eliminate duplicated string-matching logic Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add CHANGELOG, NuGet release notes, and review process updates - CHANGELOG.md: Keep a Changelog format covering [Unreleased], 1.1.0, 1.0.0 - Directory.Build.props: PackageReleaseNotes points to CHANGELOG.md (fixes NuGet warning) - src/DEVELOPER.md: update Release Process to describe workflow; add CHANGELOG to PR checklist - .github/copilot-instructions.md: add CHANGELOG reminder to Code Review Mindset - .claude/agents/pr-code-reviewer.md: add CHANGELOG check in Step 2 - .claude/agents/code-reviewer.md: add CHANGELOG check to Self-Verification - .claude/agents/code-review-manager.md: add CHANGELOG to Project Context standards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address PR review comments - StringComparison, PSGallery, encoding, docs - MsalBrowserCredential.cs: StringComparison.Ordinal on Contains calls; comment clarifying same-tenant account edge case - InteractiveGraphAuthService.cs: StringComparison.Ordinal on all Contains calls in exception filters - PowerShellModulesRequirementCheck.cs: pin Install-Module to -Repository PSGallery - MicrosoftGraphTokenProvider.cs: pin both Install-Module calls to -Repository PSGallery - GraphApiService.cs: fix U+FFFD encoding artifact in comment - CommandExecutor.cs: document outputPrefix applies only to first line (outputTransform must not return multi-line) - BlueprintLookupService.cs: comment that DisplayName is not populated in this lookup path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Fixes
a365 setupfailing on macOS 15.x with aPlatformNotSupportedException(message: "macOS 15.3.1") during authentication. MSAL throws this exception when the system browser is unavailable for interactive auth. Previously this bubbled up as an unhandled error; now the CLI automatically falls back to device code flow so the user can complete sign-in from any browser.Changes
1. macOS browser auth fallback to device code (main fix)
MsalBrowserCredential.GetTokenAsyncnow catchesPlatformNotSupportedExceptionand wraps it asMsalAuthenticationFailedExceptionso it is properly classified rather than escaping as an unexpected errorAuthenticateInteractivelyAsyncdetects the platform exception at the token acquisition site and automatically retries using device code flowCreateDeviceCodeCredentialhelper to eliminate duplicatedDeviceCodeCredentialsetup between the normal device code path and the new fallback path2. Auth logging improvements
LogErrortoLogWarningforPlatformNotSupportedExceptioninMsalBrowserCredential— this is a recoverable condition, not an error3. Cross-platform install script
install-cli.sh(bash) as the single implementation for building, packing, and installing the CLI tool locallyinstall-cli.ps1with a thin wrapper that locates Git Bash and delegates toinstall-cli.shon all platformsFiles Changed
src/.../Services/MsalBrowserCredential.csPlatformNotSupportedException, useLogWarningsrc/.../Services/AuthenticationService.csCreateDeviceCodeCredential, fix log timingscripts/cli/install-cli.shscripts/cli/install-cli.ps1Closes #291