Skip to content

fix(jwtsvid): return null from getIssuedAt when the iat claim is absent - #480

Merged
maxlambrecht merged 1 commit into
spiffe:mainfrom
NopeCharon:fix/jwt-svid-missing-iat
Oct 29, 2026
Merged

maxlambrecht merged 1 commit into
spiffe:mainfrom
NopeCharon:fix/jwt-svid-missing-iat

Conversation

@NopeCharon

Copy link
Copy Markdown
Contributor

What

Make JwtSvid.getIssuedAt() return null when the JWT-SVID has no iat claim instead of throwing a NullPointerException. When iat is present it still returns a defensive copy, as before. The method now has Javadoc that describes the null return.

CachedJwtSource.isTokenPastHalfLifetime now calls getIssuedAt() instead of reading the raw "iat" entry from getClaims(), which #430 added to avoid this NPE. Its behavior is unchanged: SVIDs without iat are still refreshed.

Fixes #477

Why

iat is optional in a JWT-SVID, and parseInsecure/parseAndValidate accept tokens without it. The public getter, however, dereferenced the field unconditionally, so any caller reading the issued-at time of such a token crashed.

How tested

  • New tests in JwtSvidParseInsecureTest: a token without iat returns null, and a token with iat returns the value as a copy that cannot change the SVID. The first test fails with an NPE without this change.
  • ./gradlew :java-spiffe-core:build (includes the full core test suite, including CachedJwtSourceTest, and the binary compatibility check) on JDK 17.

@maxlambrecht maxlambrecht left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

The 'iat' claim is optional in a JWT-SVID, and JwtSvid.parseInsecure and
JwtSvid.parseAndValidate accept tokens without it. In that case the
issuedAt field is null, and JwtSvid.getIssuedAt() threw a
NullPointerException while making its defensive copy. Any caller that
read the issued-at time of such a token crashed.

getIssuedAt() now returns null when the token has no 'iat' claim, and
otherwise still returns a defensive copy, matching getExpiry(). The
method gains Javadoc describing the null return.

CachedJwtSource.isTokenPastHalfLifetime previously avoided the NPE by
reading the raw "iat" entry from the claims map. It now uses
getIssuedAt() directly. Its behavior is unchanged: SVIDs without 'iat'
are still treated as needing a refresh.

Tests cover a token without 'iat' (getIssuedAt returns null) and a token
with 'iat' (the value is returned and the internal state cannot be
modified through the returned Date).

Signed-off-by: NopeCharon <115794254+NopeCharon@users.noreply.github.com>
@maxlambrecht
maxlambrecht force-pushed the fix/jwt-svid-missing-iat branch from ca4e3b1 to d1ba2e9 Compare October 29, 2026 15:37
@maxlambrecht
maxlambrecht merged commit c3a1973 into spiffe:main Oct 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JwtSvid.getIssuedAt() throws NullPointerException when the token has no iat claim

2 participants