@@ -2,10 +2,10 @@ use std::time::Duration;
22
33use indicatif:: ProgressBar ;
44
5- use crate :: OauthLoginFlow ;
65pub use crate :: callback_server:: Error ;
7- use crate :: oauth;
8- use crate :: oauth:: login_flow:: OauthLoginFlowState ;
6+ use crate :: callback_server:: OauthCallbackServer ;
7+ use crate :: oauth:: api:: { AuthenticateWithCode , Pkce , send_async} ;
8+ use crate :: oauth:: { self , Credentials } ;
99
1010pub struct LoginOptions {
1111 pub open_browser : bool ,
@@ -42,47 +42,92 @@ pub async fn token() -> Result<(), Error> {
4242/// This first checks if valid credentials already exist locally,
4343/// and doesn't perform the login flow if so, unless `options.force_login` is set to `true`.
4444pub async fn login ( options : LoginOptions ) -> Result < ( ) , Error > {
45+ let mut login_hint = None ;
46+ if !options. force_login {
47+ // NOTE: If the loading fails for whatever reason, we debug log the error
48+ // and have the user login again as if nothing happened.
49+ match oauth:: load_credentials ( ) {
50+ Ok ( Some ( credentials) ) => {
51+ login_hint = Some ( credentials. user ( ) . email . clone ( ) ) ;
52+ match oauth:: refresh_credentials ( credentials) . await {
53+ Ok ( credentials) => {
54+ println ! ( "You're already logged in as: {}" , credentials. user( ) . email) ;
55+ println ! ( "Note: We've refreshed your credentials." ) ;
56+ println ! ( "Note: Run `rerun auth login --force` to login again." ) ;
57+ return Ok ( ( ) ) ;
58+ }
59+ Err ( err) => {
60+ re_log:: debug!( "refreshing credentials failed: {err}" ) ;
61+ // Credentials are bad, login again.
62+ // fallthrough
63+ }
64+ }
65+ }
66+
67+ Ok ( None ) => {
68+ // No credentials yet, login as usual.
69+ // fallthrough
70+ }
71+
72+ Err ( err) => {
73+ re_log:: debug!(
74+ "validating credentials failed, logging user in again anyway. reason: {err}"
75+ ) ;
76+ // fallthrough
77+ }
78+ }
79+ }
80+
81+ let p = ProgressBar :: new_spinner ( ) ;
82+
4583 // Login process:
4684
4785 // 1. Start web server listening for token
48- let login_flow = match OauthLoginFlow :: init ( options. force_login ) . await ? {
49- OauthLoginFlowState :: AlreadyLoggedIn ( credentials) => {
50- println ! ( "You're already logged in as: {}" , credentials. user( ) . email) ;
51- println ! ( "Note: We've refreshed your credentials." ) ;
52- println ! ( "Note: Run `rerun auth login --force` to login again." ) ;
53- return Ok ( ( ) ) ;
54- }
55- OauthLoginFlowState :: LoginFlowStarted ( login_flow) => login_flow,
56- } ;
57-
58- let progress_bar = ProgressBar :: new_spinner ( ) ;
86+ let pkce = Pkce :: new ( ) ;
87+ let server = OauthCallbackServer :: new ( & pkce, login_hint. as_deref ( ) ) ?;
88+ p. inc ( 1 ) ;
5989
6090 // 2. Open authorization URL in browser
61- let login_url = login_flow. get_login_url ( ) ;
91+ let login_url = server. get_login_url ( ) ;
92+
93+ // Once the user opens the link, they are redirected to the login UI.
94+ // If they were already logged in, it will immediately redirect them
95+ // to the login callback with an authorization code.
96+ // That code is then sent by our callback page back to the web server here.
6297 if options. open_browser {
63- progress_bar . println ( "Opening login page in your browser." ) ;
64- progress_bar . println ( "Once you've logged in, the process will continue here." ) ;
65- progress_bar . println ( format ! (
98+ p . println ( "Opening login page in your browser." ) ;
99+ p . println ( "Once you've logged in, the process will continue here." ) ;
100+ p . println ( format ! (
66101 "Alternatively, manually open this url: {login_url}"
67102 ) ) ;
68103 webbrowser:: open ( login_url) . ok ( ) ; // Ok to ignore error here. The user can just open the above url themselves.
69104 } else {
70- progress_bar . println ( "Open the following page in your browser:" ) ;
71- progress_bar . println ( login_url) ;
105+ p . println ( "Open the following page in your browser:" ) ;
106+ p . println ( login_url) ;
72107 }
73- progress_bar. inc ( 1 ) ;
74-
75- // 3. Wait for login to finish
76- progress_bar. set_message ( "Waiting for browser…" ) ;
77- let credentials = loop {
78- if let Some ( code) = login_flow. poll ( ) . await ? {
79- break code;
108+ p. inc ( 1 ) ;
109+
110+ // 3. Wait for callback
111+ p. set_message ( "Waiting for browser…" ) ;
112+ let code = loop {
113+ match server. check_for_browser_response ( ) ? {
114+ None => {
115+ p. inc ( 1 ) ;
116+ std:: thread:: sleep ( Duration :: from_millis ( 10 ) ) ;
117+ }
118+ Some ( response) => break response,
80119 }
81- progress_bar. inc ( 1 ) ;
82- std:: thread:: sleep ( Duration :: from_millis ( 10 ) ) ;
83120 } ;
84121
85- progress_bar. finish_and_clear ( ) ;
122+ // 4. Exchange code for credentials
123+ let auth = send_async ( AuthenticateWithCode :: new ( & code, & pkce) )
124+ . await
125+ . map_err ( |err| Error :: Generic ( err. into ( ) ) ) ?;
126+
127+ // 5. Store credentials
128+ let credentials = Credentials :: from_auth_response ( auth. into ( ) ) ?. ensure_stored ( ) ?;
129+
130+ p. finish_and_clear ( ) ;
86131
87132 println ! (
88133 "Success! You are now logged in as {}" ,
0 commit comments