Skip to content

Commit c040cc7

Browse files
committed
nonce verification
- Improved error logging for invalid auth cookies and ID token verification. - Introduced nonce verification logic to ensure security during OIDC authentication. - Adjusted parameters for nonce hashing to optimize for short-lived tokens.
1 parent 3b500c7 commit c040cc7

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

src/webserver/oidc.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ where
271271
return self.handle_unauthenticated_request(request);
272272
}
273273
Err(e) => {
274-
log::error!("Found an invalid SQLPage auth cookie: {e}");
274+
log::error!("An auth cookie is present but could not be verified: {e:?}");
275275
return self.handle_unauthenticated_request(request);
276276
}
277277
}
@@ -379,14 +379,15 @@ fn get_sqlpage_auth_cookie(
379379
};
380380
let cookie_value = cookie.value().to_string();
381381

382+
let state = get_state_from_cookie(request)?;
382383
let verifier = oidc_client.id_token_verifier();
383384
let id_token = CoreIdToken::from_str(&cookie_value)
384-
.with_context(|| anyhow!("Invalid SQLPage auth cookie"))?;
385+
.with_context(|| format!("Invalid SQLPage auth cookie: {cookie_value:?}"))?;
385386

386-
let nonce_verifier = |_: Option<&Nonce>| Ok(());
387+
let nonce_verifier = |nonce: Option<&Nonce>| check_nonce(nonce, &state.nonce);
387388
let claims: &IdTokenClaims<EmptyAdditionalClaims, CoreGenderClaim> = id_token
388389
.claims(&verifier, nonce_verifier)
389-
.with_context(|| anyhow!("Invalid SQLPage auth cookie"))?;
390+
.with_context(|| format!("Could not verify the ID token: {cookie_value:?}"))?;
390391
log::debug!("The current user is: {claims:?}");
391392
Ok(Some(cookie_value))
392393
}
@@ -562,15 +563,40 @@ struct OidcLoginState {
562563
fn hash_nonce(nonce: &Nonce) -> String {
563564
use argon2::password_hash::{rand_core::OsRng, PasswordHasher, SaltString};
564565
let salt = SaltString::generate(&mut OsRng);
565-
// low-cost parameters
566-
let params = argon2::Params::new(8, 1, 1, None).expect("bug: invalid Argon2 parameters");
566+
// low-cost parameters: oidc tokens are short-lived
567+
let params = argon2::Params::new(8, 1, 1, Some(16)).expect("bug: invalid Argon2 parameters");
567568
let argon2 = argon2::Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params);
568569
let hash = argon2
569570
.hash_password(nonce.secret().as_bytes(), &salt)
570571
.expect("bug: failed to hash nonce");
571572
hash.to_string()
572573
}
573574

575+
fn check_nonce(id_token_nonce: Option<&Nonce>, state_nonce: &Nonce) -> Result<(), String> {
576+
match id_token_nonce {
577+
Some(id_token_nonce) => nonce_matches(id_token_nonce, state_nonce),
578+
None => Err("No nonce found in the ID token".to_string()),
579+
}
580+
}
581+
582+
fn nonce_matches(id_token_nonce: &Nonce, state_nonce: &Nonce) -> Result<(), String> {
583+
log::debug!("Checking nonce: {} == {}", id_token_nonce.secret(), state_nonce.secret());
584+
let hash = argon2::password_hash::PasswordHash::new(&id_token_nonce.secret()).map_err(|e| {
585+
format!(
586+
"Failed to parse state nonce ({}): {e}",
587+
id_token_nonce.secret()
588+
)
589+
})?;
590+
argon2::password_hash::PasswordVerifier::verify_password(
591+
&argon2::Argon2::default(),
592+
state_nonce.secret().as_bytes(),
593+
&hash,
594+
)
595+
.map_err(|e| format!("Failed to verify nonce ({}): {e}", state_nonce.secret()))?;
596+
log::debug!("Nonce successfully verified");
597+
Ok(())
598+
}
599+
574600
impl OidcLoginState {
575601
fn new(request: &ServiceRequest, auth_url: AuthUrlParams) -> Self {
576602
Self {

0 commit comments

Comments
 (0)