roundcube/roundcubemail · error · RuntimeException
Failed to validate JWT: invalid aud value
Error message
Failed to validate JWT: invalid aud value
What it means
jwt_decode() checks that when a token carries an 'aud' (audience) claim, the configured client_id must be among its audiences. This guards against accepting tokens minted for other relying parties.
Solutions
- Verify 'oauth_client_id' matches the client registered with the IDP for Roundcube.
- Decode the token and compare 'aud' with the configured client_id.
- Add Roundcube's client to the IDP's allowed audience list, or request tokens with the correct audience scope.
- Obtain a fresh token via the normal authorization-code flow rather than reusing one from another app.
Example fix
// before "aud": ["other-app"], // config client_id = 'roundcube' // after "aud": ["roundcube"],
Defensive patterns
Strategy: validation
Validate before calling
$claims = json_decode(base64_decode(strtr(explode('.', $token)[1], '-_', '+/')), true);
$aud = isset($claims['aud']) ? (array) $claims['aud'] : [];
if ($aud && !in_array(RCUBE_OAUTH_CLIENT_ID, $aud, true)) { /* abort: wrong audience */ } Type guard
function audIncludes(array $claims, string $clientId): bool { return !isset($claims['aud']) || in_array($clientId, (array) $claims['aud'], true); } Try / catch
try { $payload = $oauth->jwt_decode($token); } catch (\RuntimeException $e) { // force re-login / token refresh } Prevention
- Verify IDP audience restrictions include the Roundcube client.
- Decode tokens during integration to confirm audience claims.
- Request tokens with the audience scope matching the configured client_id.
When it happens
Trigger: parse_tokens() -> jwt_decode() where the token body has no mismatching 'azp' but its 'aud' array/string does not contain $this->options['client_id'].
Common situations: Roundcube configured with the wrong client_id; IDP audience restriction excluding Roundcube's client; token reuse across services; multi-tenant IDP issuing tokens for a sibling client.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Failed to validate JWT: invalid azp value
- Failed to validate JWT: missing aud/azp value
- OIDC: Handle only logout events
- OIDC: event has no "sub"
- OIDC: event has non-empty "nonce"
AI-assisted analysis of roundcube/roundcubemail@4b54c2acfb (2026-09-14).
Data as JSON: /api/errors/7fded22729db9b2d.
Report an issue: GitHub.
Appendix: source
Thrown at program/include/rcmail_oauth.php:450
$this->fetch_jwks();
// Validate the token (throws exceptions)
$header = new \stdClass();
$body = (array) JWT::decode($jwt, JWK::parseKeySet($this->jwks), $header);
$header = (array) $header;
} else {
[$headb64, $bodyb64, $cryptob64] = explode('.', $jwt);
$header = json_decode(static::base64url_decode($headb64), true);
$body = json_decode(static::base64url_decode($bodyb64), true);
// $crypto = static::base64url_decode($cryptob64);
}
// FIXME depends on body type: ID, Logout, Bearer, Refresh,
if (isset($body['azp']) && $body['azp'] !== $this->options['client_id']) {
throw new \RuntimeException('Failed to validate JWT: invalid azp value');
} elseif (isset($body['aud']) && !in_array($this->options['client_id'], (array) $body['aud'])) {
throw new \RuntimeException('Failed to validate JWT: invalid aud value');
} elseif (!isset($body['azp']) && !isset($body['aud'])) {
throw new \RuntimeException('Failed to validate JWT: missing aud/azp value');
}
// if defined in parameters, check that issuer match
if (isset($this->options['issuer']) && $body['iss'] !== $this->options['issuer']) {
throw new \RuntimeException('Failed to validate JWT: issuer mismatch');
}
// check that token is not an outdated message
if (isset($body['exp']) && (time() > $body['exp'])) {
throw new \RuntimeException('Failed to validate JWT: expired message');
}
$body['header'] = $header;
$this->log_debug('jwt: %s', json_encode($body));
View on GitHub (pinned to 4b54c2acfb)