@@ -108,7 +108,7 @@ public MicrosoftGraphTokenProvider(
108108 useDeviceCode ) ;
109109
110110 var script = BuildPowerShellScript ( tenantId , validatedScopes , useDeviceCode , clientAppId ) ;
111- var result = await ExecuteWithFallbackAsync ( script , ct ) ;
111+ var result = await ExecuteWithFallbackAsync ( script , useDeviceCode , ct ) ;
112112 var token = ProcessResult ( result ) ;
113113
114114 if ( string . IsNullOrWhiteSpace ( token ) )
@@ -216,16 +216,17 @@ private static string BuildScopesArray(string[] scopes)
216216
217217 private async Task < CommandResult > ExecuteWithFallbackAsync (
218218 string script ,
219+ bool useDeviceCode ,
219220 CancellationToken ct )
220221 {
221222 // Try PowerShell Core first (cross-platform)
222- var result = await ExecutePowerShellAsync ( "pwsh" , script , ct ) ;
223+ var result = await ExecutePowerShellAsync ( "pwsh" , script , useDeviceCode , ct ) ;
223224
224225 // Fallback to Windows PowerShell if pwsh is not available
225226 if ( ! result . Success && IsPowerShellNotFoundError ( result ) )
226227 {
227228 _logger . LogDebug ( "PowerShell Core not found, falling back to Windows PowerShell" ) ;
228- result = await ExecutePowerShellAsync ( "powershell" , script , ct ) ;
229+ result = await ExecutePowerShellAsync ( "powershell" , script , useDeviceCode , ct ) ;
229230 }
230231
231232 return result ;
@@ -234,9 +235,10 @@ private async Task<CommandResult> ExecuteWithFallbackAsync(
234235 private async Task < CommandResult > ExecutePowerShellAsync (
235236 string shell ,
236237 string script ,
238+ bool useDeviceCode ,
237239 CancellationToken ct )
238240 {
239- var arguments = BuildPowerShellArguments ( shell , script ) ;
241+ var arguments = BuildPowerShellArguments ( shell , script , useDeviceCode ) ;
240242
241243 return await _executor . ExecuteWithStreamingAsync (
242244 command : shell ,
@@ -247,11 +249,12 @@ private async Task<CommandResult> ExecutePowerShellAsync(
247249 cancellationToken : ct ) ;
248250 }
249251
250- private static string BuildPowerShellArguments ( string shell , string script )
252+ private static string BuildPowerShellArguments ( string shell , string script , bool useDeviceCode )
251253 {
252- var baseArgs = shell == "pwsh"
253- ? "-NoProfile -NonInteractive"
254- : "-NoLogo -NoProfile -NonInteractive" ;
254+ // Never use -NonInteractive for Graph authentication as it prevents both:
255+ // - Device code prompts from being displayed
256+ // - Interactive browser windows from opening (WAM)
257+ var baseArgs = shell == "pwsh" ? "-NoProfile" : "-NoLogo -NoProfile" ;
255258
256259 var wrappedScript = $ "try {{ { script } }} catch {{ Write-Error $_.Exception.Message; exit 1 }}";
257260
@@ -268,14 +271,24 @@ private static string BuildPowerShellArguments(string shell, string script)
268271 return null ;
269272 }
270273
271- var token = result . StandardOutput ? . Trim ( ) ;
274+ var output = result . StandardOutput ? . Trim ( ) ;
272275
273- if ( string . IsNullOrWhiteSpace ( token ) )
276+ if ( string . IsNullOrWhiteSpace ( output ) )
274277 {
275278 _logger . LogWarning ( "PowerShell succeeded but returned empty output" ) ;
276279 return null ;
277280 }
278281
282+ // Extract the JWT token from output - PowerShell may include WARNING lines
283+ // JWT tokens start with "eyJ" (base64 encoded '{"')
284+ var token = ExtractJwtFromOutput ( output ) ;
285+
286+ if ( string . IsNullOrWhiteSpace ( token ) )
287+ {
288+ _logger . LogWarning ( "Could not extract JWT token from PowerShell output" ) ;
289+ return null ;
290+ }
291+
279292 if ( ! IsValidJwtFormat ( token ) )
280293 {
281294 _logger . LogWarning ( "Returned token does not appear to be a valid JWT" ) ;
@@ -285,6 +298,42 @@ private static string BuildPowerShellArguments(string shell, string script)
285298 return token ;
286299 }
287300
301+ private static string ? ExtractJwtFromOutput ( string output )
302+ {
303+ // Split output into lines and find the JWT token
304+ // JWT tokens start with "eyJ" and contain exactly two dots
305+ var lines = output . Split ( new [ ] { '\r ' , '\n ' } , StringSplitOptions . RemoveEmptyEntries ) ;
306+
307+ foreach ( var line in lines )
308+ {
309+ var trimmed = line . Trim ( ) ;
310+ // Skip WARNING, ERROR, and other PowerShell output lines
311+ if ( trimmed . StartsWith ( "WARNING:" , StringComparison . OrdinalIgnoreCase ) ||
312+ trimmed . StartsWith ( "ERROR:" , StringComparison . OrdinalIgnoreCase ) ||
313+ trimmed . StartsWith ( "VERBOSE:" , StringComparison . OrdinalIgnoreCase ) ||
314+ trimmed . StartsWith ( "DEBUG:" , StringComparison . OrdinalIgnoreCase ) )
315+ {
316+ continue ;
317+ }
318+
319+ // Check if this line looks like a JWT token
320+ if ( IsValidJwtFormat ( trimmed ) )
321+ {
322+ return trimmed ;
323+ }
324+ }
325+
326+ // Fallback: if no line matches, return the trimmed output
327+ // (in case the token is on a single line without prefixes)
328+ var trimmedOutput = output . Trim ( ) ;
329+ if ( IsValidJwtFormat ( trimmedOutput ) )
330+ {
331+ return trimmedOutput ;
332+ }
333+
334+ return null ;
335+ }
336+
288337 private static bool IsPowerShellNotFoundError ( CommandResult result )
289338 {
290339 if ( string . IsNullOrWhiteSpace ( result . StandardError ) )
0 commit comments