theonedev/onedev · error · AuthenticationException

Invalid issue date of ID token

Error message

Invalid issue date of ID token

What it means

Thrown by OpenIdConnector.processTokenResponse when the ID token's iat (issue time) claim is in the future by more than a 10-second tolerance (now.plusSeconds(10)). The connector rejects tokens apparently issued in the future, which indicates clock skew or a forged/manipulated token.

Source

Thrown at server-plugin/server-plugin-sso-openid/src/main/java/io/onedev/server/plugin/sso/openid/OpenIdConnector.java:225

			else
				return null;
		} else {
			return null;
		}
	}
	
	protected SsoAuthenticated processTokenResponse(OIDCTokenResponse tokenResponse) {
		try {
			JWT idToken = tokenResponse.getOIDCTokens().getIDToken();
			JWTClaimsSet claims = idToken.getJWTClaimsSet();
			
			if (!claims.getIssuer().equals(getCachedProviderMetadata().getIssuer()))
				throw new AuthenticationException(_T("Inconsistent issuer in provider metadata and ID token"));
			
			DateTime now = new DateTime();
			
			if (claims.getIssueTime() != null && claims.getIssueTime().after(now.plusSeconds(10).toDate()))
				throw new AuthenticationException(_T("Invalid issue date of ID token"));
			
			if (claims.getExpirationTime() != null && now.toDate().after(claims.getExpirationTime()))
				throw new AuthenticationException(_T("ID token was expired"));

			Session.get().setAttribute(SESSION_ATTR_ID_TOKEN, idToken.serialize());

			String subject = claims.getSubject();
			String email = StringUtils.trimToNull(claims.getStringClaim("email"));

			Boolean emailVerified = claims.getBooleanClaim("email_verified");
			if (emailVerified == null)
				emailVerified = claims.getBooleanClaim("emailVerified");
			if (emailVerified != null && !emailVerified)
				email = null;

			String userName = StringUtils.trimToNull(claims.getStringClaim("preferred_username"));
			String fullName = StringUtils.trimToNull(claims.getStringClaim("name"));
			List<String> groups;

View on GitHub (pinned to d44925c47c)

Solutions

  1. Synchronize clocks with NTP on the OneDev server (e.g. systemd-timesyncd/chrony) and on the identity provider host.
  2. Restart the SSO login to get a freshly issued token after fixing clock skew.
  3. Check container/VM time settings (host clock passthrough, no paused/snapshotted clocks).
  4. If skew is small and unavoidable, note the 10-second tolerance and reduce skew below it.

Example fix

# before: clock skewed
# after
sudo chronyc makestep   # or configure NTP on the OneDev host
Defensive patterns

Strategy: retry

Validate before calling

long skewMinutes = Math.abs(DateTime.now().getMillis() - System.currentTimeMillis());
// verify host clock via NTP before SSO rollout: chronyc tracking / ntpq -p

Try / catch

try {
    auth = connector.handleAuthResponse(...);
} catch (AuthenticationException e) {
    if (e.getMessage().contains("Invalid issue date")) {
        // resync clocks (NTP) and retry login
    }
}

Prevention

When it happens

Trigger: claims.getIssueTime() is after server time plus 10 seconds when the token response is processed — the identity provider's clock is ahead of the OneDev server clock, or a misbehaving proxy returned a cached/pre-issued token with a future iat.

Common situations: NTP not running or drifting on either the OneDev server or the identity provider host; containers with wrong timezone/clock settings; VM resumed from snapshot with skewed clock.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/bd784b278bfc8ff2. Report an issue: GitHub.