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
- From the server host, verify reachability: `curl <iss>/.well-known/openid-configuration` and the jwks_uri it lists.
- Open outbound firewall/proxy access to the identity provider domains.
- Confirm the server's issuer allow-list includes the token's iss value.
- 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
- Allow-list identity provider domains in firewall/egress rules.
- Add a startup health check for the OIDC discovery endpoint.
- Cache JWKS results with TTL so transient IdP blips don't fail every request.
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
- Issuer mismatch: got {:?}, expected {:?}
- No kid found
- Issuer too long: {:?}
- Subject too long: {:?}
- Issuer empty
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/cf0e6c83f73c1a3f.
Report an issue: GitHub.