apereo/cas · error · IllegalArgumentException
Proof JWT is missing iat
Error message
Proof JWT is missing iat
What it means
Thrown by OidcVerifiableCredentialJwtProofValidator.verifyFreshness when the proof JWT lacks an 'iat' (issued-at) claim. Proof freshness requires the issued-at timestamp to bound the proof's validity window and prevent replay of old proofs.
Solutions
- Set the iat claim (claimsSet.issueTime(new Date())) to the current UTC time when building the proof JWT.
- Regenerate the proof for each credential request so iat is current.
- Use a proof-builder library that enforces standard time claims.
Example fix
// before
JWTClaimsSet claims = new JWTClaimsSet.Builder().issuer(holderId).build();
// after
JWTClaimsSet claims = new JWTClaimsSet.Builder().issuer(holderId)
.issueTime(new Date()).build(); Defensive patterns
Strategy: validation
Validate before calling
if (claims.getIssueTime() == null) {
throw new IllegalArgumentException("Proof JWT must include iat");
} Prevention
- Always call issueTime(new Date()) when building proof claims.
- Regenerate proofs per request rather than reusing templates.
When it happens
Trigger: Building the proof JWT claims set without calling issueTime(...) or setting the iat claim; a JWT library that omits iat unless explicitly requested; stripped claims after re-serialization.
Common situations: Minimal hand-rolled JWT builders omitting standard time claims; copied claim-set code from non-proof contexts (e.g. ID tokens) that did not set iat.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Proof iat is in the future
- Proof nonce is invalid or missing
- Proof JWT signature validation failed
- Proof audience does not match credential issuer
- Proof JWT algorithm is invalid
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/2b8295e08fd6d4e7.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/oidc/vc/issuer/proof/OidcVerifiableCredentialJwtProofValidator.java:102
protected void verifyAlgorithm(final SignedJWT signedJwt, final JWK holderJwk) {
val alg = signedJwt.getHeader().getAlgorithm();
if (alg == null || Algorithm.NONE.equals(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm is invalid");
}
if (holderJwk instanceof RSAKey && !JWSAlgorithm.Family.RSA.contains(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm does not match RSA holder key");
}
if (holderJwk instanceof ECKey && !JWSAlgorithm.Family.EC.contains(alg)) {
throw new IllegalArgumentException("Proof JWT algorithm does not match EC holder key");
}
}
protected void verifyFreshness(final SignedJWT signedJwt) throws ParseException {
val claims = signedJwt.getJWTClaimsSet();
val issuedAt = claims.getIssueTime();
if (issuedAt == null) {
throw new IllegalArgumentException("Proof JWT is missing iat");
}
val now = Instant.now(Clock.systemUTC());
val iat = issuedAt.toInstant();
if (iat.isAfter(now.plusSeconds(SECONDS_IN_FUTURE))) {
throw new IllegalArgumentException("Proof iat is in the future");
}
if (iat.isBefore(now.minus(Duration.ofMinutes(MINUTES_IN_PAST)))) {
throw new IllegalArgumentException("Proof JWT is too old");
}
}
}
View on GitHub (pinned to e7288fc434)