Skip to content

Commit 93812ea

Browse files
committed
Add fast OIDC client refresh on auth errors
1 parent c24479d commit 93812ea

File tree

1 file changed

+21
-6
lines changed

1 file changed

+21
-6
lines changed

src/webserver/oidc.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;
4141
const SQLPAGE_AUTH_COOKIE_NAME: &str = "sqlpage_auth";
4242
const SQLPAGE_REDIRECT_URI: &str = "/sqlpage/oidc_callback";
4343
const SQLPAGE_STATE_COOKIE_NAME: &str = "sqlpage_oidc_state";
44-
const OIDC_CLIENT_REFRESH_INTERVAL: Duration = Duration::from_secs(60 * 60);
44+
const OIDC_CLIENT_MAX_REFRESH_INTERVAL: Duration = Duration::from_secs(60 * 60);
45+
const OIDC_CLIENT_MIN_REFRESH_INTERVAL: Duration = Duration::from_secs(5);
4546

4647
#[derive(Clone, Debug, Serialize, Deserialize)]
4748
#[serde(transparent)]
@@ -184,7 +185,14 @@ impl OidcState {
184185
/// Refreshes the OIDC client from the provider metadata URL if it has expired.
185186
/// Most providers update their signing keys periodically.
186187
pub async fn refresh_if_expired(&self, service_request: &ServiceRequest) {
187-
if self.client.read().await.last_update.elapsed() > OIDC_CLIENT_REFRESH_INTERVAL {
188+
if self.client.read().await.last_update.elapsed() > OIDC_CLIENT_MAX_REFRESH_INTERVAL {
189+
self.refresh(service_request).await;
190+
}
191+
}
192+
193+
/// When an authentication error is encountered, refresh the OIDC client info faster
194+
pub async fn refresh_on_error(&self, service_request: &ServiceRequest) {
195+
if self.client.read().await.last_update.elapsed() > OIDC_CLIENT_MIN_REFRESH_INTERVAL {
188196
self.refresh(service_request).await;
189197
}
190198
}
@@ -337,6 +345,7 @@ async fn handle_request(
337345
}
338346
Err(e) => {
339347
log::debug!("An auth cookie is present but could not be verified. Redirecting to OIDC provider to re-authenticate. {e:?}");
348+
oidc_state.refresh_on_error(&request).await;
340349
handle_unauthenticated_request(oidc_state, request).await
341350
}
342351
}
@@ -419,7 +428,7 @@ async fn process_oidc_callback(
419428
) -> anyhow::Result<HttpResponse> {
420429
let http_client = get_http_client_from_appdata(request)?;
421430

422-
let state = get_state_from_cookie(request)?;
431+
let state = get_state_from_cookie(request).context("Failed to read oidc state cookie")?;
423432

424433
let params = Query::<OidcCallbackParams>::from_query(query_string)
425434
.with_context(|| {
@@ -442,7 +451,9 @@ async fn process_oidc_callback(
442451
let redirect_target = validate_redirect_url(state.initial_url);
443452
log::info!("Redirecting to {redirect_target} after a successful login");
444453
let mut response = build_redirect_response(redirect_target);
445-
set_auth_cookie(&mut response, &token_response, oidc_state).await?;
454+
set_auth_cookie(&mut response, &token_response, oidc_state)
455+
.await
456+
.context("Failed to set auth cookie")?;
446457
Ok(response)
447458
}
448459

@@ -456,7 +467,8 @@ async fn exchange_code_for_token(
456467
oidc_callback_params.code,
457468
))?
458469
.request_async(&AwcHttpClient::from_client(http_client))
459-
.await?;
470+
.await
471+
.context("Failed to exchange code for token")?;
460472
Ok(token_response)
461473
}
462474

@@ -471,7 +483,10 @@ async fn set_auth_cookie(
471483
.id_token()
472484
.context("No ID token found in the token response. You may have specified an oauth2 provider that does not support OIDC.")?;
473485

474-
let claims = oidc_state.get_token_claims(id_token.clone(), None).await?;
486+
let claims = oidc_state
487+
.get_token_claims(id_token.clone(), None)
488+
.await
489+
.context("Invalid token returned by OIDC provider")?;
475490
let expiration = claims.expiration();
476491
let max_age_seconds = expiration.signed_duration_since(Utc::now()).num_seconds();
477492

0 commit comments

Comments
 (0)