Private
Public Access
1
0

implements authentication/token retrieval/keyring

This commit is contained in:
2025-05-03 01:06:50 -07:00
parent 461c37bd20
commit 26d54f91d5
11 changed files with 234 additions and 99 deletions

View File

@@ -53,7 +53,8 @@ pub enum Error {
ClientError(String),
HTTPError(hyper::Error),
SerdeError(serde_json::Error),
DecodeError,
DecodeError(String),
Unauthorized,
}
impl std::error::Error for Error {
@@ -192,7 +193,7 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
let body = || -> Body { serde_json::to_string(&credentials).unwrap().into() };
let token: AuthResponse = self.request_with_body_retry("authenticate", Method::POST, body, false).await?;
let token = JwtToken::new(&token.jwt).map_err(|_| Error::DecodeError)?;
let token = JwtToken::new(&token.jwt).map_err(|e| Error::DecodeError(e.to_string()))?;
log::debug!("Saving token: {:?}", token);
self.auth_store.set_token(token.clone()).await;
@@ -239,7 +240,6 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
}
async fn open_event_socket(&mut self) -> Result<WebsocketEventSocket, Self::Error> {
use tungstenite::http::StatusCode;
use tungstenite::handshake::client::Request as TungsteniteRequest;
use tungstenite::handshake::client::generate_key;
@@ -259,21 +259,45 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
.body(())
.expect("Unable to build websocket request");
match &auth {
Some(token) => {
let header_value = token.to_header_value().to_str().unwrap().parse().unwrap(); // ugh
request.headers_mut().insert("Authorization", header_value);
}
None => {
log::warn!(target: "websocket", "Proceeding without auth token.");
}
}
log::debug!("Websocket request: {:?}", request);
if let Some(token) = &auth {
let header_value = token.to_header_value().to_str().unwrap().parse().unwrap(); // ugh
request.headers_mut().insert("Authorization", header_value);
match connect_async(request).await.map_err(Error::from) {
Ok((socket, response)) => {
log::debug!("Websocket connected: {:?}", response.status());
Ok(WebsocketEventSocket::new(socket))
}
Err(e) => match e {
Error::ClientError(ce) => match ce.as_str() {
"HTTP error: 401 Unauthorized" | "Unauthorized" => {
// Try to authenticate
if let Some(credentials) = &self.auth_store.get_credentials().await {
log::warn!("Websocket connection failed, attempting to authenticate");
let new_token = self.authenticate(credentials.clone()).await?;
self.auth_store.set_token(new_token).await;
// try again on the next attempt.
return Err(Error::Unauthorized);
} else {
log::error!("Websocket unauthorized, no credentials provided");
return Err(Error::ClientError("Unauthorized, no credentials provided".into()));
}
}
_ => Err(Error::Unauthorized)
}
_ => Err(e)
}
}
let (socket, response) = connect_async(request).await.map_err(Error::from)?;
log::debug!("Websocket connected: {:?}", response.status());
if response.status() != StatusCode::SWITCHING_PROTOCOLS {
return Err(Error::ClientError("Websocket connection failed".into()));
}
Ok(WebsocketEventSocket::new(socket))
}
}
@@ -384,7 +408,7 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
// If JSON deserialization fails, try to interpret it as plain text
// Unfortunately the server does return things like this...
let s = str::from_utf8(&body).map_err(|_| Error::DecodeError)?;
let s = str::from_utf8(&body).map_err(|e| Error::DecodeError(e.to_string()))?;
serde_plain::from_str(s).map_err(|_| json_err)
}
}?;