Start the device code login flow
()
| 123 | |
| 124 | /// Start the device code login flow |
| 125 | pub async fn login() -> Result<AuthTokens, Box<dyn std::error::Error>> { |
| 126 | let client = reqwest::Client::new(); |
| 127 | |
| 128 | // Step 1: Request device code |
| 129 | eprintln!("Requesting device code..."); |
| 130 | |
| 131 | let device_code_url = format!("https://{}/oauth/device/code", AUTH0_DOMAIN); |
| 132 | let response = client |
| 133 | .post(&device_code_url) |
| 134 | .form(&[ |
| 135 | ("client_id", AUTH0_CLIENT_ID), |
| 136 | ("scope", "openid profile email offline_access"), |
| 137 | ("audience", AUTH0_AUDIENCE), |
| 138 | ]) |
| 139 | .send() |
| 140 | .await?; |
| 141 | |
| 142 | if !response.status().is_success() { |
| 143 | let text = response.text().await?; |
| 144 | return Err(format!("Failed to get device code: {}", text).into()); |
| 145 | } |
| 146 | |
| 147 | let device_code: DeviceCodeResponse = response.json().await?; |
| 148 | |
| 149 | // Step 2: Display login instructions to stderr |
| 150 | eprintln!(); |
| 151 | eprintln!("To login, open this URL in your browser:"); |
| 152 | eprintln!(); |
| 153 | eprintln!(" {}", device_code.verification_uri_complete); |
| 154 | eprintln!(); |
| 155 | eprintln!("Or go to {} and enter code: {}", device_code.verification_uri, device_code.user_code); |
| 156 | eprintln!(); |
| 157 | eprint!("Waiting for login..."); |
| 158 | io::stderr().flush()?; |
| 159 | |
| 160 | // Step 3: Poll for token |
| 161 | let token_url = format!("https://{}/oauth/token", AUTH0_DOMAIN); |
| 162 | let poll_interval = Duration::from_secs(device_code.interval.max(5)); // At least 5 seconds |
| 163 | let deadline = std::time::Instant::now() + Duration::from_secs(device_code.expires_in); |
| 164 | |
| 165 | loop { |
| 166 | if std::time::Instant::now() > deadline { |
| 167 | eprintln!(); |
| 168 | return Err("Login timed out. Please try again.".into()); |
| 169 | } |
| 170 | |
| 171 | tokio::time::sleep(poll_interval).await; |
| 172 | eprint!("."); |
| 173 | io::stderr().flush()?; |
| 174 | |
| 175 | let response = client |
| 176 | .post(&token_url) |
| 177 | .form(&[ |
| 178 | ("grant_type", "urn:ietf:params:oauth:grant-type:device_code"), |
| 179 | ("device_code", &device_code.device_code), |
| 180 | ("client_id", AUTH0_CLIENT_ID), |
| 181 | ]) |
| 182 | .send() |
no test coverage detected