1- using System ;
1+ using Microsoft . Identity . Client ;
2+ using System ;
23using System . Collections . Concurrent ;
34using System . Diagnostics ;
5+ using System . IO ;
46using System . Linq ;
57using System . Net ;
68using System . Net . Http ;
@@ -165,42 +167,70 @@ internal enum UrlMatchType
165167 }
166168 }
167169
170+ // Using code from MSAL
171+ // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Platforms/netstandard/NetCorePlatformProxy.cs
172+
168173 internal static void OpenBrowserForInteractiveLogin ( string url , int port , CancellationTokenSource cancellationTokenSource )
169174 {
170175 // Fixes encoding of scopes and redirect_uri issue on MacOS. It has no negative effects on Windows.
171176 url = WebUtility . UrlDecode ( url ) ;
172177
173- try
178+ if ( OperatingSystem . IsWindows ( ) )
174179 {
175-
176- ProcessStartInfo psi = new ProcessStartInfo
180+ try
177181 {
178- FileName = url ,
179- UseShellExecute = true
180- } ;
181- Process . Start ( psi ) ;
182-
183- }
184- catch
185- {
186- // hack because of this: https://github.com/dotnet/corefx/issues/10361
187- if ( OperatingSystem . IsWindows ( ) )
182+ var psi = new ProcessStartInfo
183+ {
184+ FileName = url ,
185+ UseShellExecute = true
186+ } ;
187+ Process . Start ( psi ) ;
188+ }
189+ catch
188190 {
191+ // hack because of this: https://github.com/dotnet/corefx/issues/10361
192+ url = url . Replace ( "&" , "^&" ) ;
189193 Process . Start ( new ProcessStartInfo ( "cmd" , $ "/c start { url } ") { CreateNoWindow = true } ) ;
190194 }
191- else if ( OperatingSystem . IsLinux ( ) )
195+ }
196+ else if ( OperatingSystem . IsLinux ( ) )
197+ {
198+ string sudoUser = Environment . GetEnvironmentVariable ( "SUDO_USER" ) ;
199+ if ( ! string . IsNullOrWhiteSpace ( sudoUser ) )
192200 {
193- Process . Start ( "xdg-open" , url ) ;
201+ throw new MsalClientException ( MsalError . LinuxXdgOpen ) ;
194202 }
195- else if ( OperatingSystem . IsMacOS ( ) )
203+ try
196204 {
197- Process . Start ( "open" , url ) ;
205+ bool opened = false ;
206+ foreach ( string openTool in GetOpenToolsLinux ( ) )
207+ {
208+ if ( TryGetExecutablePath ( openTool , out string openToolPath ) )
209+ {
210+ OpenLinuxBrowser ( openToolPath , url ) ;
211+ opened = true ;
212+ break ;
213+ }
214+ }
215+
216+ if ( ! opened )
217+ {
218+ throw new MsalClientException ( MsalError . LinuxXdgOpen ) ;
219+ }
198220 }
199- else
221+ catch
200222 {
201- throw new PlatformNotSupportedException ( RuntimeInformation . OSDescription ) ;
223+ throw new MsalClientException ( MsalError . LinuxXdgOpen ) ;
202224 }
203225 }
226+ else if ( OperatingSystem . IsMacOS ( ) )
227+ {
228+ Process . Start ( "/usr/bin/open" , url ) ;
229+ }
230+ else
231+ {
232+ throw new PlatformNotSupportedException ( RuntimeInformation . OSDescription ) ;
233+ }
204234 }
205235
206236 internal static int FindFreeLocalhostRedirectUri ( )
@@ -216,5 +246,49 @@ internal static int FindFreeLocalhostRedirectUri()
216246 listener ? . Stop ( ) ;
217247 }
218248 }
249+
250+ internal static void OpenLinuxBrowser ( string openToolPath , string url )
251+ {
252+ ProcessStartInfo psi = new ProcessStartInfo ( openToolPath , url )
253+ {
254+ RedirectStandardOutput = true ,
255+ RedirectStandardError = true ,
256+ UseShellExecute = false
257+ } ;
258+
259+ Process . Start ( psi ) ;
260+
261+ }
262+
263+ internal static string [ ] GetOpenToolsLinux ( )
264+ {
265+ return [ "xdg-open" , "gnome-open" , "kfmclient" , "microsoft-edge" , "wslview" ] ;
266+ }
267+
268+ /// <summary>
269+ /// Searches through PATH variable to find the path to the specified executable.
270+ /// </summary>
271+ /// <param name="executable">Executable to find the path for.</param>
272+ /// <param name="path">Location of the specified executable.</param>
273+ /// <returns></returns>
274+ internal static bool TryGetExecutablePath ( string executable , out string path )
275+ {
276+ string pathEnvVar = Environment . GetEnvironmentVariable ( "PATH" ) ;
277+ if ( pathEnvVar != null )
278+ {
279+ var paths = pathEnvVar . Split ( ':' ) ;
280+ foreach ( var basePath in paths )
281+ {
282+ path = Path . Combine ( basePath , executable ) ;
283+ if ( File . Exists ( path ) )
284+ {
285+ return true ;
286+ }
287+ }
288+ }
289+
290+ path = null ;
291+ return false ;
292+ }
219293 }
220294}
0 commit comments