sigoden/dufs · error · anyhow::Error
Token expired
Error message
Token expired
What it means
Token payloads embed a millisecond Unix expiry timestamp after the 64-byte signature. `verify_token` compares it against current time and rejects the request with 'Token expired' once `now > exp`. This bounds how long a signed token remains usable.
Solutions
- Request a fresh token from `generate_token` and retry.
- Synchronize the client clock (NTP) if it is ahead of the server's.
- Increase TOKEN_EXPIRATION in the server config/code if longer validity is intended.
- Implement client-side refresh before expiry.
Example fix
// before
let token = stored_token; // may be old
// after
let token = if token_expired(stored_token) { auth.generate_token(path, user)? } else { stored_token }; Defensive patterns
Strategy: retry
Validate before calling
fn token_expired(token: &str, skew_ms: u64) -> bool {
hex::decode(token).ok().and_then(|r| r.get(64..72).map(|e| u64::from_be_bytes(e.try_into().unwrap())))
.map(|exp| now_ms() + skew_ms > exp).unwrap_or(true)
} Try / catch
match verify_result { Err(e) if e.to_string() == "Token expired" => { let t = auth.generate_token(path, user)?; retry_with(t); }, other => other } Prevention
- Refresh tokens before expiry on the client side
- Sync clocks with NTP
- Keep a fallback re-authentication flow
- Tune TOKEN_EXPIRATION to expected session length
When it happens
Trigger: Presenting a token to `guard`/`verify_token` after TOKEN_EXPIRATION milliseconds have elapsed since `generate_token` created it (system clock far ahead also triggers it).
Common situations: Long-lived browser sessions reusing an old URL token; client clocks skewed ahead of server time; caching a token across days.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of sigoden/dufs@fe7fd564f8 (2026-09-09).
Data as JSON: /api/errors/01d6a65ddcedf021.
Report an issue: GitHub.
Appendix: source
Thrown at src/auth.rs:193
raw.extend_from_slice(user.as_bytes());
Ok(hex::encode(raw))
}
fn verify_token<'a>(&'a self, token: &str, path: &str) -> Result<(String, &'a AccessPaths)> {
let raw = hex::decode(token)?;
if raw.len() < 72 {
bail!("Invalid token");
}
let sig_bytes = &raw[..64];
let exp_bytes = &raw[64..72];
let user_bytes = &raw[72..];
let exp = u64::from_be_bytes(exp_bytes.try_into()?);
if unix_now().as_millis() as u64 > exp {
bail!("Token expired");
}
let user = std::str::from_utf8(user_bytes)?;
let (pass, ap) = self
.users
.get(user)
.ok_or_else(|| anyhow!("Not found user '{user}'"))?;
let sig = Signature::from_bytes(&<[u8; 64]>::try_from(sig_bytes)?);
let message = format!("{path}:{exp}");
derive_secret_key(user, pass).verify(message.as_bytes(), &sig)?;
Ok((user.to_string(), ap))
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct AccessPaths {View on GitHub (pinned to fe7fd564f8)