sigoden/dufs · error · anyhow::Error

Invalid token

Error message

Invalid token

What it means

`verify_token` hex-decodes a bearer token and requires at least 72 bytes: a 64-byte signature, 8-byte expiry, and a user name. Shorter tokens cannot possibly contain that layout, so they are rejected immediately with 'Invalid token' before signature verification.

Solutions

  1. Regenerate the token with `Auth::generate_token(path, user)` and use the full returned string.
  2. Confirm the token is valid hex and sent intact (no truncation by proxies or copy/paste).
  3. Ensure the Authorization header uses the token format the server expects.
  4. If tokens come from an older version, re-login/re-issue since formats may differ.

Example fix

// before
let token = "a1b2c3"; // truncated
// after
let token = auth.generate_token("/files", "alice")?; // full 64+8+user hex
Defensive patterns

Strategy: try-catch

Validate before calling

fn token_plausible(t: &str) -> bool { t.len() >= 144 && hex::decode(t).map(|r| r.len() >= 72).unwrap_or(false) }

Try / catch

match err.downcast_ref::<String>() { Some(m) if m == "Invalid token" => reissue_token_and_retry(), _ => return_401() }

Prevention

When it happens

Trigger: Calling `guard` (via `verify_token`) with a token string that is truncated, not the output of `generate_token`, hex-corrupted, or includes only the signature portion.

Common situations: Manually copying part of a token out of logs/URLs; an intermediary stripping characters; hand-crafting tokens for testing; old tokens from a previous token scheme.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of sigoden/dufs@fe7fd564f8 (2026-09-09). Data as JSON: /api/errors/da1fc5b284ae9520. Report an issue: GitHub.

Appendix: source

Thrown at src/auth.rs:184

            .ok_or_else(|| anyhow!("Not found user '{user}'"))?;
        let exp = unix_now().as_millis() as u64 + TOKEN_EXPIRATION;
        let message = format!("{path}:{exp}");
        let mut signing_key = derive_secret_key(user, pass);
        let sig = signing_key.sign(message.as_bytes()).to_bytes();

        let mut raw = Vec::with_capacity(64 + 8 + user.len());
        raw.extend_from_slice(&sig);
        raw.extend_from_slice(&exp.to_be_bytes());
        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)?);

View on GitHub (pinned to fe7fd564f8)