clockworklabs/SpacetimeDB · error · TokenValidationError::Other

Error fetching public key for issuer {raw_issuer}

Error message

Error fetching public key for issuer {raw_issuer}

What it means

CachingOidcTokenValidator extracts the raw issuer from the token, then fetches that issuer's JWKS public keys through an async cache. If the fetch fails — network error, unreachable OIDC endpoints, or an issuer the node does not recognize — the cache returns None and this error is produced, because validation cannot proceed without the issuer's keys.

Source

Thrown at crates/core/src/auth/token_validation.rs:236

        let keys = key_or_error?;
        let validator = JwksValidator {
            issuer: raw_issuer.into(),
            keyset: keys,
        };
        Ok(Arc::new(validator))
    }
}

#[async_trait]
impl TokenValidator for CachingOidcTokenValidator {
    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {
        let raw_issuer = get_raw_issuer(token)?;
        log::debug!("Getting validator for issuer {}", raw_issuer.clone());
        let validator = self
            .cache
            .get(String::from(raw_issuer.clone()).into())
            .await
            .ok_or_else(|| anyhow::anyhow!("Error fetching public key for issuer {raw_issuer}"))?;
        validator.validate_token(token).await
    }
}

// This is a token validator that uses OIDC to validate tokens.
// This will look up the public key for the issuer and validate against that key.
// This currently has no caching.
pub struct OidcTokenValidator;

// Get the issuer out of a token without validating the signature.
fn get_raw_issuer(token: &str) -> Result<Box<str>, TokenValidationError> {
    let mut validation = Validation::new(jsonwebtoken::Algorithm::ES256);
    validation.set_required_spec_claims(&REQUIRED_CLAIMS);
    validation.validate_aud = false;
    // We are disabling signature validation, because we need to get the issuer before we can validate.
    validation.insecure_disable_signature_validation();
    let data = decode::<IncomingClaims>(token, &DecodingKey::from_secret(b"fake"), &validation)?;
    Ok(data.claims.issuer)

View on GitHub (pinned to 524b4487d9)

Solutions

  1. From the server host, verify reachability: `curl <iss>/.well-known/openid-configuration` and the jwks_uri it lists.
  2. Open outbound firewall/proxy access to the identity provider domains.
  3. Confirm the server's issuer allow-list includes the token's iss value.
  4. Retry after the provider recovers — JWKS fetch failures are often transient.
Defensive patterns

Strategy: retry

Validate before calling

async function issuerReachable(issuer: string): Promise<boolean> {
  try {
    const res = await fetch(`${issuer.replace(/\/$/, '')}/.well-known/openid-configuration`, { signal: AbortSignal.timeout(5000) });
    return res.ok;
  } catch { return false; }
}
if (!(await issuerReachable(iss))) throw new Error(`OIDC issuer ${iss} unreachable — check egress/DNS`);

Try / catch

for (let attempt = 1; attempt <= 5; attempt++) {
  try { return await validateOidcToken(token); }
  catch (e) {
    if (!String(e).includes('Error fetching public key')) throw e;
    await sleep(2 ** attempt * 100); // JWKS fetch is network-bound: back off and retry
  }
}

Prevention

When it happens

Trigger: The server cannot reach the issuer's .well-known/openid-configuration or its jwks_uri (DNS failure, blocked egress, TLS error); the OIDC provider is down; the token's iss points to an issuer the node is not configured to trust.

Common situations: Self-hosted nodes in containers without outbound network access; corporate proxies blocking the identity provider; misconfigured issuer allow-lists; IdP outages.

Related errors


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