{"record":{"id":"9f1e2bb77a809174","repo":"clockworklabs/SpacetimeDB","slug":"issuer-mismatch-got-expected","errorCode":null,"errorMessage":"Issuer mismatch: got {:?}, expected {:?}","messagePattern":"Issuer mismatch: got (.+?), expected (.+?)","errorType":"validation","errorClass":"TokenValidationError::Other","httpStatus":null,"severity":"error","filePath":"crates/core/src/auth/token_validation.rs","lineNumber":173,"sourceCode":"\n        // TODO: We should require a specific audience at some point.\n        validation.validate_aud = false;\n\n        let data = decode::<IncomingClaims>(token, self, &validation)?;\n        let claims = data.claims;\n        claims.try_into().map_err(TokenValidationError::Other)\n    }\n}\n\n#[async_trait]\nimpl TokenValidator for BasicTokenValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        // This validates everything but the issuer.\n        let claims = self.public_key.validate_token(token).await?;\n        if let Some(expected_issuer) = &self.issuer\n            && *claims.issuer != **expected_issuer\n        {\n            return Err(TokenValidationError::Other(anyhow::anyhow!(\n                \"Issuer mismatch: got {:?}, expected {:?}\",\n                claims.issuer,\n                expected_issuer\n            )));\n        }\n        Ok(claims)\n    }\n}\n\n// Validates tokens by looking up public keys and caching them.\npub struct CachingOidcTokenValidator {\n    cache: async_cache::AsyncCache<Arc<JwksValidator>, KeyFetcher>,\n}\n\nimpl CachingOidcTokenValidator {\n    pub fn new(refresh_duration: Duration, expiry: Option<Duration>) -> Self {\n        let cache = async_cache::Options::new(refresh_duration, KeyFetcher)\n            .with_expire(expiry)","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/core/src/auth/token_validation.rs#L155-L191","documentation":"BasicTokenValidator validated the JWT's signature and standard claims, but the iss (issuer) claim does not equal the issuer the server was configured to expect. Signature validity alone is insufficient: the token must originate from the configured identity provider or deployment issuer.","triggerScenarios":"Sending a token minted by a different SpacetimeDB deployment or OIDC provider than the one configured (e.g. a testnet token against a self-hosted node); the server's expected issuer configuration changed after the token was issued; enterprise OIDC where the iss URL differs between environments.","commonSituations":"Reusing SPACETIMEDB_SPACETIME_TOKEN across deployments; server config changed the expected issuer; the identity provider migrated its issuer URL after tokens were minted.","solutions":["Re-login against the correct server to obtain a token with the expected issuer.","Decode the token payload (e.g. jwt.io or `spacetime identity decode`) and compare its iss claim with the issuer the server expects.","If you operate the server, align the configured issuer with the identity provider's actual issuer URL.","Do not reuse tokens across deployments that have different issuers."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function assertIssuerMatches(token: string, expectedIssuer: string): void {\n  const iss = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString()).iss;\n  if (iss !== expectedIssuer) {\n    throw new Error(`token issuer '${iss}' does not match server issuer '${expectedIssuer}' — re-login`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await callApi(token);\n} catch (e) {\n  if (String(e).includes('Issuer mismatch')) {\n    token = await freshLogin(server); // mint token from the right deployment\n    return callApi(token);\n  }\n  throw e;\n}","preventionTips":["Never reuse tokens across deployments with different issuers.","After changing server auth config, force all clients to re-login.","Keep per-environment token stores keyed by server URL."],"tags":["spacetimedb","jwt","auth","issuer","oidc"],"backgroundTag":"jwt-issuer-mismatch","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}