@@ -4,6 +4,7 @@ namespace OneIdentity.SafeguardDotNet.BrowserLogin;
44
55using System ;
66using System . Threading ;
7+ using System . Threading . Tasks ;
78
89using Serilog ;
910
@@ -17,49 +18,83 @@ public static class DefaultBrowserLogin
1718 /// Connect to Safeguard by launching the default browser for OAuth2/PKCE authentication.
1819 /// Opens a local TCP listener to receive the authorization code callback from the browser.
1920 /// </summary>
21+ /// <remarks>
22+ /// WARNING: This method blocks indefinitely until the browser callback is received.
23+ /// If the user does not complete authentication, this call will never return.
24+ /// For programmatic cancellation, use <see cref="ConnectAsync"/> with a
25+ /// <see cref="CancellationToken"/> or
26+ /// <see cref="Safeguard.AgentBasedLoginUtils.CreateConsoleCancellationToken"/>.
27+ /// </remarks>
2028 /// <param name="appliance">Network address of Safeguard appliance</param>
2129 /// <param name="username">Optional username to pre-fill the login form</param>
2230 /// <param name="port">Local TCP port to listen for OAuth callback (default: 8400)</param>
2331 /// <param name="apiVersion">Target API version to use (default: 4)</param>
2432 /// <param name="ignoreSsl">Ignore validation of Safeguard appliance SSL certificate (default: false)</param>
2533 /// <returns>Reusable Safeguard API connection</returns>
2634 public static ISafeguardConnection Connect (
27- string appliance , string username = "" , int port = 8400 , int apiVersion = Safeguard . DefaultApiVersion , bool ignoreSsl = false )
35+ string appliance ,
36+ string username = "" ,
37+ int port = 8400 ,
38+ int apiVersion = Safeguard . DefaultApiVersion ,
39+ bool ignoreSsl = false )
40+ {
41+ return ConnectAsync ( appliance , username , port , apiVersion , ignoreSsl , CancellationToken . None )
42+ . GetAwaiter ( ) . GetResult ( ) ;
43+ }
44+
45+ /// <summary>
46+ /// Connect to Safeguard by launching the default browser for OAuth2/PKCE authentication (async).
47+ /// Opens a local TCP listener to receive the authorization code callback from the browser.
48+ /// Returns when the user completes authentication or the cancellation token is triggered.
49+ /// </summary>
50+ /// <remarks>
51+ /// WARNING: This method blocks indefinitely until the browser callback is received.
52+ /// If no <paramref name="cancellationToken"/> is provided, the call will never return
53+ /// if the user does not complete authentication. Always provide a cancellation token
54+ /// with a timeout or use <see cref="Safeguard.AgentBasedLoginUtils.CreateConsoleCancellationToken"/>
55+ /// to enable Ctrl+C cancellation.
56+ /// </remarks>
57+ /// <param name="appliance">Network address of Safeguard appliance</param>
58+ /// <param name="username">Optional username to pre-fill the login form</param>
59+ /// <param name="port">Local TCP port to listen for OAuth callback (default: 8400)</param>
60+ /// <param name="apiVersion">Target API version to use (default: 4)</param>
61+ /// <param name="ignoreSsl">Ignore validation of Safeguard appliance SSL certificate (default: false)</param>
62+ /// <param name="cancellationToken">Cancellation token to abort the flow.</param>
63+ /// <returns>Reusable Safeguard API connection</returns>
64+ /// <exception cref="SafeguardDotNetException">Thrown when authentication fails or API error.</exception>
65+ /// <exception cref="OperationCanceledException">Thrown when cancellation is requested.</exception>
66+ public static async Task < ISafeguardConnection > ConnectAsync (
67+ string appliance ,
68+ string username = "" ,
69+ int port = 8400 ,
70+ int apiVersion = Safeguard . DefaultApiVersion ,
71+ bool ignoreSsl = false ,
72+ CancellationToken cancellationToken = default )
2873 {
2974 Log . Debug ( "Calling RSTS for primary authentication" ) ;
3075
3176 var oauthCodeVerifier = Safeguard . AgentBasedLoginUtils . OAuthCodeVerifier ( ) ;
32- var tokenExtractor = new AuthorizationCodeExtractor ( ) ;
3377 var browserLauncher = new BrowserLauncher ( appliance , oauthCodeVerifier ) ;
3478
35- using var source = new CancellationTokenSource ( ) ;
36- Console . CancelKeyPress += ( sender , e ) => { source . Cancel ( ) ; } ;
37-
3879 browserLauncher . Show ( username , port ) ;
39- tokenExtractor . Listen ( port , source . Token ) ;
4080
41- if ( string . IsNullOrEmpty ( tokenExtractor . AuthorizationCode ) )
42- {
43- throw new SafeguardDotNetException ( "Unable to obtain authorization code" ) ;
44- }
81+ var authorizationCode = await AuthorizationCodeExtractor . ListenAsync ( port , cancellationToken ) . ConfigureAwait ( false ) ;
4582
4683 Log . Debug ( "Redeeming RSTS authorization code" ) ;
4784
48- using var rstsAccessToken = Safeguard . AgentBasedLoginUtils . PostAuthorizationCodeFlow (
49- appliance , tokenExtractor . AuthorizationCode , oauthCodeVerifier , Safeguard . AgentBasedLoginUtils . RedirectUri ) ;
85+ using var rstsAccessToken = await Safeguard . AgentBasedLoginUtils . PostAuthorizationCodeFlowAsync (
86+ appliance ,
87+ authorizationCode ,
88+ oauthCodeVerifier ,
89+ Safeguard . AgentBasedLoginUtils . RedirectUriTcpListener ,
90+ ignoreSsl ,
91+ cancellationToken )
92+ . ConfigureAwait ( false ) ;
5093
5194 Log . Debug ( "Exchanging RSTS access token" ) ;
5295
53- var responseObject = Safeguard . AgentBasedLoginUtils . PostLoginResponse ( appliance , rstsAccessToken , apiVersion ) ;
54-
55- var statusValue = responseObject . GetValue ( "Status" ) ? . ToString ( ) ;
56-
57- if ( string . IsNullOrEmpty ( statusValue ) || statusValue != "Success" )
58- {
59- throw new SafeguardDotNetException ( $ "Error response status { statusValue } from RSTS") ;
60- }
61-
62- using var accessToken = responseObject . GetValue ( "UserToken" ) ? . ToString ( ) . ToSecureString ( ) ;
63- return Safeguard . Connect ( appliance , accessToken , apiVersion , ignoreSsl ) ;
96+ return await Safeguard . AgentBasedLoginUtils . ExchangeRstsTokenForConnectionAsync (
97+ appliance , rstsAccessToken , apiVersion , ignoreSsl , cancellationToken )
98+ . ConfigureAwait ( false ) ;
6499 }
65100}
0 commit comments