feat: add automatic token refresh on 401 responses during profile sync
Add RefreshRequest struct for token refresh API calls. Update sync_current_session to detect 401 responses and automatically refresh access tokens using refresh token before retrying profile sync. Store refreshed access and refresh tokens in existing session state. Extract profile URL to variable for reuse in retry logic.
This commit is contained in:
@@ -75,6 +75,11 @@ struct LoginRequest<'a> {
|
||||
password: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RefreshRequest<'a> {
|
||||
refresh_token: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LoginResponse {
|
||||
#[serde(rename = "access_token")]
|
||||
@@ -406,7 +411,7 @@ fn current_metrics(app: &AppHandle) -> Result<TunnelMetrics, String> {
|
||||
}
|
||||
|
||||
async fn sync_current_session(app: &AppHandle) -> Result<SessionState, String> {
|
||||
let existing = {
|
||||
let mut existing = {
|
||||
let state = app.state::<AppState>();
|
||||
let session = state.session.lock().map_err(|_| "Unable to read client state".to_string())?;
|
||||
session.clone().ok_or_else(|| "No enrolled profile is available yet".to_string())?
|
||||
@@ -417,13 +422,49 @@ async fn sync_current_session(app: &AppHandle) -> Result<SessionState, String> {
|
||||
.build()
|
||||
.map_err(|err| err.to_string())?;
|
||||
|
||||
let response = client
|
||||
.get(format!("{}/api/v1/me/profile", existing.server_url.trim_end_matches('/')))
|
||||
let profile_url = format!("{}/api/v1/me/profile", existing.server_url.trim_end_matches('/'));
|
||||
let mut response = client
|
||||
.get(&profile_url)
|
||||
.bearer_auth(&existing.access_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| format!("Profile sync failed: {}", err))?;
|
||||
|
||||
if response.status().as_u16() == 401 {
|
||||
let refresh = client
|
||||
.post(format!("{}/api/v1/auth/refresh", existing.server_url.trim_end_matches('/')))
|
||||
.json(&RefreshRequest {
|
||||
refresh_token: &existing.refresh_token,
|
||||
})
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| format!("Session refresh failed: {}", err))?;
|
||||
|
||||
if !refresh.status().is_success() {
|
||||
let status = refresh.status();
|
||||
let body = refresh
|
||||
.text()
|
||||
.await
|
||||
.unwrap_or_else(|_| "<unable to read response body>".into());
|
||||
return Err(format!("Session refresh failed with status {}: {}", status, body));
|
||||
}
|
||||
|
||||
let refreshed = refresh
|
||||
.json::<LoginResponse>()
|
||||
.await
|
||||
.map_err(|err| format!("Unable to decode refresh response: {}", err))?;
|
||||
|
||||
existing.access_token = refreshed.access_token;
|
||||
existing.refresh_token = refreshed.refresh_token;
|
||||
|
||||
response = client
|
||||
.get(&profile_url)
|
||||
.bearer_auth(&existing.access_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| format!("Profile sync failed: {}", err))?;
|
||||
}
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let body = response
|
||||
|
||||
Reference in New Issue
Block a user