Skip to content

[SDK-566] Accept fractional seconds for expiringAuthTokenRefreshPeriod - #1079

Open
franco-zalamena-iterable wants to merge 4 commits into
feature/SDK-547-jwt-timer-racefrom
feature/SDK-566-auth-refresh-period-units
Open

[SDK-566] Accept fractional seconds for expiringAuthTokenRefreshPeriod#1079
franco-zalamena-iterable wants to merge 4 commits into
feature/SDK-547-jwt-timer-racefrom
feature/SDK-566-auth-refresh-period-units

Conversation

@franco-zalamena-iterable

@franco-zalamena-iterable franco-zalamena-iterable commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📝 Summary

Android now accepts fractional seconds for expiringAuthTokenRefreshPeriod, matching iOS/RN/Flutter, and validates the value instead of silently scheduling refreshes after expiry.

🎟️ Jira Ticket: SDK-566

📖 Description

Stacked on #1077 — base is feature/SDK-547-jwt-timer-race, not master. Both branches modify IterableAuthManager.java and the same CHANGELOG section, so stacking avoids a conflict. Merge #1077 first; this will auto-retarget to master.

⚠️ The ticket's premise is wrong — please read before reviewing

SDK-566 states that Android interprets expiringAuthTokenRefreshPeriod in milliseconds while the other SDKs use seconds, and asks for a breaking unit change plus a coordinated version bump.

Android already uses seconds. Verified against all four codebases:

Platform Public API Unit Default
Android Builder.setExpiringAuthTokenRefreshPeriod(...) seconds (* 1000L internally) 60 s
iOS config.expiringAuthTokenRefreshPeriod: TimeInterval seconds 60 s
React Native IterableConfig.expiringAuthTokenRefreshPeriod seconds 60 s
Flutter IterableConfig.expiringAuthTokenRefreshPeriod seconds 60 s

The 60000L the ticket cites is the Builder's internal millisecond representation of 60 s, not a public seconds-valued default. The * 1000L conversion has been there since 2020 (090bff97f, 2cc5f9800).

Corroboration: the RN and Flutter bridges pass seconds straight into the Android setter. If the premise were true, every hybrid app would already have a 1000x bug.

So making the requested change would introduce the bug the ticket aims to prevent. It is deliberately not done here. No breaking change, no coordinated version bump.

What the real gap was

Android accepted whole seconds only (Long), while iOS/RN/Flutter accept fractional seconds. 0.5 was not expressible. That is a genuine parity gap, and it is what this PR fixes.

1. Fractional seconds (Added)
New setExpiringAuthTokenRefreshPeriod(double) overload. The Long overload is deprecated and delegates to it, so existing callers are unaffected — source and binary compatible. Overload resolution verified for 120L, a boxed Long, a bare int, and 0.5; none are ambiguous.

2. Input validation (Fixed) — two real bugs, both reproduced:

  • Negative-60 became -60000 ms, and the period is subtracted when computing the refresh time, so the refresh was scheduled 60 s after the token had already expired.
  • OverflowLong.MAX_VALUE * 1000L wraps to -1000, same past-expiry path.
  • Null → NPE on unboxing the @NonNull Long.

Now logged and corrected in the SDK's existing log-and-continue style (throwing would be a new crash surface for an init-time setter): null/NaN/negative fall back to the 60 s default; values above ~10 years are clamped. Zero stays valid — it means "refresh once expired" — and is covered by a test so it isn't "fixed" later.

3. Internal naming (Changed)
The core confusion is that one name meant seconds publicly and milliseconds internally. Package-private carriers renamed to ...Millis (IterableConfig, IterableAuthManager, RetryPolicy). All final/package-private — invisible outside the SDK. Public setter names and parameters are untouched.

Follow-ups (not in this PR)

  • React Native — the Android bridge reads the value with optLong, so 12.7 truncates to 12 while iOS keeps 12.7. Fix is committed on feature/SDK-566-rn-fractional-refresh-period but cannot compile until an iterableapi release carries the double overload — RN pins 3.6.2, which publishes only the Long overload (javac: double cannot be converted to Long). Sequenced after this ships.
  • Flutter — unverified; Extensions.kt suggests it truncates the same way.

Android only accepted whole seconds while iOS, React Native and Flutter
accept fractional ones, so the same configuration value could behave
differently per platform. Add a double overload and deprecate the Long
one, which now delegates to it. Existing callers keep compiling.

The setter also accepted any value unguarded. Because the period is
subtracted when computing the refresh time, a negative value scheduled
the refresh after the token had already expired, a very large value
overflowed to a negative period with the same effect, and null threw an
NPE on unboxing. Values carrying no usable intent (null, NaN, negative)
now fall back to the 60s default; an excessive period still expresses an
intent, so it is clamped to a ceiling. Logged rather than thrown.

Also rename the internal carriers to ...Millis so the seconds-in,
milliseconds-stored split is explicit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drop four tests that re-covered behaviour already guarded elsewhere or
never changed: whole-second conversion (covered via the deprecated Long
overload's delegation), 90.25s sub-second precision (covered by 0.5s),
Long.MIN_VALUE (delegates to the double path's negative guard), and the
RetryPolicy interval conversion, which this branch only renamed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@franco-zalamena-iterable
franco-zalamena-iterable requested a review from a team August 11, 2026 15:42
Comment thread iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java Outdated
Comment thread CHANGELOG.md Outdated
Comment thread iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java Outdated
The NaN/negative/null branches return without assigning, so the period
keeps its prior value rather than falling back to 60s. Setting 30s and
then -60s left 30s, not the 60s the javadoc and CHANGELOG claimed.

Docs and log messages now say the value is ignored and report what is
actually kept. Behaviour is unchanged: ignoring bad input preserves an
explicitly configured period instead of discarding it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fig.java

Co-authored-by: Ricardo Silva <rtlsilva@gmail.com>

@rtlsilva rtlsilva left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

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.

2 participants