{"record":{"id":"01d6a65ddcedf021","repo":"sigoden/dufs","slug":"token-expired","errorCode":null,"errorMessage":"Token expired","messagePattern":"Token expired","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/auth.rs","lineNumber":193,"sourceCode":"        raw.extend_from_slice(user.as_bytes());\n\n        Ok(hex::encode(raw))\n    }\n\n    fn verify_token<'a>(&'a self, token: &str, path: &str) -> Result<(String, &'a AccessPaths)> {\n        let raw = hex::decode(token)?;\n\n        if raw.len() < 72 {\n            bail!(\"Invalid token\");\n        }\n\n        let sig_bytes = &raw[..64];\n        let exp_bytes = &raw[64..72];\n        let user_bytes = &raw[72..];\n\n        let exp = u64::from_be_bytes(exp_bytes.try_into()?);\n        if unix_now().as_millis() as u64 > exp {\n            bail!(\"Token expired\");\n        }\n\n        let user = std::str::from_utf8(user_bytes)?;\n        let (pass, ap) = self\n            .users\n            .get(user)\n            .ok_or_else(|| anyhow!(\"Not found user '{user}'\"))?;\n\n        let sig = Signature::from_bytes(&<[u8; 64]>::try_from(sig_bytes)?);\n\n        let message = format!(\"{path}:{exp}\");\n        derive_secret_key(user, pass).verify(message.as_bytes(), &sig)?;\n        Ok((user.to_string(), ap))\n    }\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq)]\npub struct AccessPaths {","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/sigoden/dufs/blob/fe7fd564f80dfbac361c8e0589c3845638149d38/src/auth.rs#L175-L211","documentation":"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.","triggerScenarios":"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).","commonSituations":"Long-lived browser sessions reusing an old URL token; client clocks skewed ahead of server time; caching a token across days.","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."],"exampleFix":"// before\nlet token = stored_token; // may be old\n// after\nlet token = if token_expired(stored_token) { auth.generate_token(path, user)? } else { stored_token };","handlingStrategy":"retry","validationCode":"fn token_expired(token: &str, skew_ms: u64) -> bool {\n    hex::decode(token).ok().and_then(|r| r.get(64..72).map(|e| u64::from_be_bytes(e.try_into().unwrap())))\n        .map(|exp| now_ms() + skew_ms > exp).unwrap_or(true)\n}","typeGuard":null,"tryCatchPattern":"match verify_result { Err(e) if e.to_string() == \"Token expired\" => { let t = auth.generate_token(path, user)?; retry_with(t); }, other => other }","preventionTips":["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"],"tags":["rust","authentication","token-expiry"],"backgroundTag":"jwt-token-expired","analyzedSha":"fe7fd564f80dfbac361c8e0589c3845638149d38","analyzedAt":"2026-09-09T13:01:22.843Z","contentChangedAt":"2026-09-09T13:01:22.843Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}