clockworklabs/SpacetimeDB · error

Identity mismatch: token identity {token_identity:?} does no

Error message

Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}

What it means

SpacetimeDB computes the caller identity as Identity::from_claims(iss, sub). If the token additionally carries an explicit identity claim, it must equal that computed value (crates/auth/src/identity.rs:121); otherwise conversion fails. This stops tokens from asserting an identity other than the one their iss/sub hash to.

Source

Thrown at crates/auth/src/identity.rs:121

            return Err(anyhow::anyhow!("Issuer too long: {:?}", self.issuer));
        }
        if self.subject.len() > 128 {
            return Err(anyhow::anyhow!("Subject too long: {:?}", self.subject));
        }
        // The issuer and subject must be non-empty.
        if self.issuer.is_empty() {
            return Err(anyhow::anyhow!("Issuer empty"));
        }
        if self.subject.is_empty() {
            return Err(anyhow::anyhow!("Subject empty"));
        }

        let computed_identity = Identity::from_claims(&self.issuer, &self.subject);
        // If an identity is provided, it must match the computed identity.
        if let Some(token_identity) = self.identity
            && token_identity != computed_identity
        {
            return Err(anyhow::anyhow!(
                    "Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}",
                ));
        }

        Ok(SpacetimeIdentityClaims {
            identity: computed_identity,
            subject: self.subject,
            issuer: self.issuer,
            audience: self.audience,
            iat: self.iat,
            exp: self.exp,
            extra: self.extra,
        })
    }
}

#[cfg(test)]
mod tests {

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Drop the identity claim from the token — it is optional and SpacetimeDB derives it from iss/sub
  2. Or recompute it: Identity::from_claims(current_iss, current_sub), and embed exactly that value
  3. After any iss/sub change, treat the result as a new identity and re-grant subscriptions/permissions to it

Example fix

// before (JWT payload)
{ "iss": "https://new-issuer.example.com", "sub": "12345", "identity": "<hash-of-old-issuer+sub>" }

// after
{ "iss": "https://new-issuer.example.com", "sub": "12345" }
Defensive patterns

Strategy: validation

Validate before calling

// If you include an identity claim, verify it matches iss/sub before use:
// (identity = Identity::from_claims(iss, sub) server-side; keep them in sync or omit it)
function sanitizeClaims(claims: Record<string, unknown>) {
  const { identity, ...rest } = claims;
  if (identity !== undefined) {
    console.warn('Dropping stale identity claim; SpacetimeDB derives it from iss/sub');
  }
  return rest;
}

Prevention

When it happens

Trigger: Minting a token with an identity claim copied from an old iss/sub pair, then changing the issuer or subject (URL change, provider migration, casing/format tweak changes the hash) without recomputing identity; hand-assembled tokens with a stale identity field.

Common situations: Re-issuing tokens under a new issuer URL while reusing the old identity claim; token tooling that hardcodes an identity value; provider changing subject formatting after an upgrade.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/46b3daf4dc9392c9. Report an issue: GitHub.