Security policy API SDK: API token expiry not enforced, old tokens still work

API tokens issued by the security-policy module aren’t respecting expiry timestamps. We’ve configured tokens with 24-hour expiry, but tokens issued 3+ days ago are still accepted by the platform. This is a major security risk for our compliance audit.

Token configuration:

{
  "tokenType": "API_KEY",
  "expiryHours": 24,
  "revocable": true
}

We’ve verified the expiry timestamp is correctly set in the token metadata when issued. However, the platform’s API gateway doesn’t seem to check expiry during validation. Token expiry enforcement should happen at validation time, but the revocation list polling mechanism might not be working properly. We need the SDK validation logic to strictly enforce token expiry. Has anyone else noticed token expiry being ignored in oiot-23 security policy?

Your token expiry enforcement issue stems from three interconnected problems in the oiot-23 security architecture:

Token Expiry Enforcement: The platform’s API gateway uses a two-tier validation strategy that causes expired tokens to remain valid longer than configured. First-tier validation checks token signature and basic structure, but expiry validation is deferred to second-tier database lookup, which is cached aggressively.

To enforce strict expiry, configure the gateway to validate JWT claims on every request:

{
  "gatewayPolicy": {
    "tokenValidation": {
      "validateExpiry": true,
      "cacheValidation": false,
      "enforceClockSkew": 60
    }
  }
}

Set cacheValidation to false to disable validation caching. This increases latency by 20-50ms per request but ensures real-time expiry enforcement. The enforceClockSkew parameter (in seconds) accounts for time synchronization differences between servers.

For opaque (non-JWT) tokens, enable database validation on every request:

SecurityConfig config = new SecurityConfig();
config.setTokenValidationMode("REALTIME");
config.setCacheTokenValidation(false);
securityClient.updateConfig(config);

Revocation List Polling: The SDK’s default revocation list polling interval (30 minutes) is too long for security-critical applications. Expired tokens should be automatically added to the revocation list, but oiot-23 doesn’t do this by default - it only tracks manually revoked tokens.

Implement automatic expiry-based revocation:

// Scheduled job to revoke expired tokens
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
  List<Token> expiredTokens = securityClient.getExpiredTokens();
  for (Token token : expiredTokens) {
    securityClient.revokeToken(token.getId(), "EXPIRED");
  }
}, 0, 5, TimeUnit.MINUTES);

Run this job every 5 minutes to move expired tokens to the revocation list. This ensures they’re blocked even if validation caching is enabled.

Configure SDK revocation list polling for more frequent updates:

SDKConfig sdkConfig = new SDKConfig();
sdkConfig.setRevocationListPollInterval(300); // 5 minutes
sdkConfig.setRevocationListCacheEnabled(false);

Disabling the revocation list cache forces the SDK to fetch the latest list on every validation.

SDK Validation Logic: The SDK’s client-side validation doesn’t check token expiry by default - it relies on server-side validation. For defense-in-depth, implement client-side expiry validation:

public boolean isTokenValid(String token) {
  try {
    DecodedJWT jwt = JWT.decode(token);
    Date expiry = jwt.getExpiresAt();

    if (expiry.before(new Date())) {
      logger.warn("Token expired: {}", token);
      return false;
    }

    // Check against revocation list
    if (securityClient.isTokenRevoked(jwt.getId())) {
      logger.warn("Token revoked: {}", token);
      return false;
    }

    return true;
  } catch (JWTDecodeException e) {
    return false;
  }
}

Call this validation before every API request to catch expired tokens before they reach the server.

Additional Security Measures: For compliance audit requirements, implement token rotation:

// Rotate tokens before expiry
if (token.getExpiresAt().getTime() - System.currentTimeMillis() < 3600000) {
  // Less than 1 hour until expiry - rotate
  Token newToken = securityClient.rotateToken(token.getId());
  // Update client configuration with new token
}

Enable audit logging for token validation failures:

AuditConfig auditConfig = new AuditConfig();
auditConfig.setLogExpiredTokenAttempts(true);
auditConfig.setLogRevocationChecks(true);
securityClient.updateAuditConfig(auditConfig);

This creates an audit trail of expired token usage attempts, which is critical for compliance.

Implementing these three layers - gateway-level expiry enforcement, automatic revocation of expired tokens, and client-side validation - ensures strict token expiry enforcement that meets security audit requirements.

The revocation list polling interval is probably too long. By default, the SDK polls the revocation list every 30 minutes. If a token expires between polling intervals, it continues to work until the next poll. You can configure the polling interval in the SDK client configuration. Set it to 5 minutes or less for stricter expiry enforcement, though this increases load on the security service.

The API gateway caches token validation results for performance reasons. By default, the cache TTL is 4 hours, which means expired tokens continue working until the cache entry expires. You need to reduce the validation cache TTL or disable caching entirely for security-sensitive applications. Check the gateway configuration settings.

There’s a known issue in oiot-23.1 where the token expiry check only happens during initial authentication, not on subsequent API calls. Once a token passes the first validation, it’s considered valid for the duration of the session regardless of expiry. This was supposed to be fixed in 23.2 but I’m not sure if that patch was actually released. Check your platform version.

We had the same issue during our SOC 2 audit. The problem is that token expiry is configured correctly but the revocation list isn’t being updated when tokens expire. The platform only adds manually revoked tokens to the revocation list, not automatically expired ones. You need to implement a scheduled job that explicitly revokes expired tokens by adding them to the revocation list. This forces the validation logic to reject them.