From 5d0e308c1c57bea30d2ad9008b6dbc7936d58645 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 27 Oct 2021 22:16:46 -0400 Subject: add a separate "register" command to log in with api keys --- src/api.rs | 91 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 42 deletions(-) (limited to 'src/api.rs') diff --git a/src/api.rs b/src/api.rs index f4198b5..d8e35fe 100644 --- a/src/api.rs +++ b/src/api.rs @@ -576,42 +576,60 @@ impl Client { Ok(prelogin_res.kdf_iterations) } + pub async fn register( + &self, + email: &str, + device_id: &str, + apikey: &crate::locked::ApiKey, + ) -> Result<()> { + let connect_req = ConnectPasswordReq { + grant_type: "client_credentials".to_string(), + username: email.to_string(), + password: None, + scope: "api".to_string(), + // XXX unwraps here are not necessarily safe + client_id: String::from_utf8(apikey.client_id().to_vec()) + .unwrap(), + client_secret: Some( + String::from_utf8(apikey.client_secret().to_vec()).unwrap(), + ), + device_type: 8, + device_identifier: device_id.to_string(), + device_name: "rbw".to_string(), + device_push_token: "".to_string(), + two_factor_token: None, + two_factor_provider: None, + }; + let client = reqwest::Client::new(); + let res = client + .post(&self.identity_url("/connect/token")) + .form(&connect_req) + .send() + .await + .map_err(|source| Error::Reqwest { source })?; + if let reqwest::StatusCode::OK = res.status() { + Ok(()) + } else { + let code = res.status().as_u16(); + Err(classify_login_error(&res.json_with_path().await?, code)) + } + } + pub async fn login( &self, email: &str, device_id: &str, - creds: &crate::locked::HashedLoginCredentials, + password_hash: &crate::locked::PasswordHash, two_factor_token: Option<&str>, two_factor_provider: Option, ) -> Result<(String, String, String)> { - let (grant_type, scope, password, client_id, client_secret) = - match creds { - crate::locked::HashedLoginCredentials::Password { - password_hash, - } => ( - "password", - "api offline_access", - Some(base64::encode(password_hash.hash())), - &b"desktop"[..], - None, - ), - crate::locked::HashedLoginCredentials::ApiKey { apikey } => ( - "client_credentials", - "api", - None, - apikey.client_id(), - Some(apikey.client_secret()), - ), - }; let connect_req = ConnectPasswordReq { - grant_type: grant_type.to_string(), + grant_type: "password".to_string(), username: email.to_string(), - password, - scope: scope.to_string(), - // XXX unwraps here are not necessarily safe - client_id: String::from_utf8(client_id.to_vec()).unwrap(), - client_secret: client_secret - .map(|secret| String::from_utf8(secret.to_vec()).unwrap()), + password: Some(base64::encode(password_hash.hash())), + scope: "api offline_access".to_string(), + client_id: "desktop".to_string(), + client_secret: None, device_type: 8, device_identifier: device_id.to_string(), device_name: "rbw".to_string(), @@ -621,24 +639,13 @@ impl Client { two_factor_provider: two_factor_provider.map(|ty| ty as u32), }; let client = reqwest::Client::new(); - let mut headers = reqwest::header::HeaderMap::new(); - if connect_req.client_secret.is_none() - && connect_req.password.is_some() - { - headers.insert( - "auth-email", - // unwrap is safe because url safe base64 strings are always - // valid header values - reqwest::header::HeaderValue::from_str( - &base64::encode_config(email, base64::URL_SAFE_NO_PAD), - ) - .unwrap(), - ); - } let res = client .post(&self.identity_url("/connect/token")) .form(&connect_req) - .headers(headers) + .header( + "auth-email", + base64::encode_config(email, base64::URL_SAFE_NO_PAD), + ) .send() .await .map_err(|source| Error::Reqwest { source })?; -- cgit v1.2.3-54-g00ecf