Repository navigation
feat: an expired signed session continues as a visitor (Intercom parity); version 19.7.0 - #212
Merged
Merged
Conversation
…ty); version 19.7.0
When a session that was signed loses its grant by expiry and no fresh
identity token arrives, the SDK now ends the identified session and
continues as a fresh visitor with no history, like clearIdentity (the JWT
provider stays set). This matches Intercom's web Messenger: "If the session
expires, and no fresh JWT is sent, the user's session will end. The user will
see a fresh Messenger as a logged-out website visitor."
- Records { sid, uid, exp } of the last grant (no credential) in the
signed-session-<projectId> cache entry and cookie, so a reload after the
expiry knows the session was signed. Grants restored from an older SDK's
cookie are recorded too.
- A Signed-mode /sessions answer saying "not signed" for a session whose
signed lifetime passed: one renewal attempt (an identify in flight, the
held token or hash through withFreshIdentify, else the JWT provider), then
the session ends unless a live grant arrived. Network failures keep it.
- A page left open past the expiry: checks at exp, takes a grant another tab
renewed from the cookie, waits while the messenger is open (at most an
hour, mirroring Intercom's one-hour cookie for activity after expiry),
then asks /sessions and decides as above.
- Unchanged: identify(userId, data) without a token or hash (never signed),
a grant only missing before its exp, Classic projects.
- README, typings and CHANGELOG describe the new behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Production probe on the smoke project, confirming the server contract this PR relies on:
🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
On a Signed project, a session that was signed and whose grant expires without a fresh identity token now ends. The SDK drops the identified session and continues as a fresh visitor with no conversation history, like
Gleap.clearIdentity()(the JWT provider stays set). Until now the user stayed identified but unsigned (14 days of history, protected fields read-only).A later
setUserJwt(or a provider token) signs the user in again, and the server's existing guest → user merge (mergeCurrentSessionWithExistingSession) moves the visitor's activity on this device into their contact.Intercom behaviour this matches
Authenticating users in the Messenger with JWTs, "Customer experience":
FAQ, "What if the session expires in the middle of the user doing something?":
Users without a JWT keep the 14-day rule, as in User conversation history visibility.
The visitor fallback is web-only in Intercom. On iOS and Android, Intercom asks
setUserJwtProviderand otherwise lets requests "fail until a new JWT is set" (intercom-react-native README, iOS 19.9.0, Android 18.10.0). So the native SDKs are left unchanged.How
{ sid, uid, exp }(never the grant) in thesigned-session-<projectId>cache entry and cookie. This outlives the grant cookie, so a reload after the expiry knows the session was signed. A grant restored from an older SDK's cookie is recorded too./sessionsin Signed mode withidentity: { mode: 'signed', signed: false }. If this session's signed lifetime has passed, the SDK makes one renewal attempt: an identify already in flight, else the held token or hash (throughwithFreshIdentify, so an expiring token goes to the provider first), else the JWT provider. An expired token with no provider is not sent. If no live grant comes back, the session ends. A network failure keeps the session. The decision waits for in-flight identifies.exp./sessions, and the decision above applies.exp, the existing refresh still renews a minute ahead through the provider.signedSessionExpiryRunning). After the drop, no record is left. No timer is armed for an expiry that already passed. The grace check only reads local state once a minute. The provider backoff and single-flight rules apply unchanged.identify(userId, data)without a token or hash (never signed, so no record).exp(cookie deleted, cookies off, IP host). It is restored as in 19.5.2.identity, so nothing is decided.Server
No change needed. Today a
/sessionsanswer for a session with an expired grant already returns only the session pair,userId,nameandlang. Protected values and token-written fields are stripped (publicSessionData.logic). The answer also saysidentity.signed === false, which is the signal the SDK acts on. Hiding more on the server would also hit the native SDKs and never-signed identified users, which keep today's behaviour.Wrappers
No changes. React Native and Flutter (mobile) use the native SDKs. Capacitor's web build depends on
gleap ^19.5.0and picks up 19.7.0. Flutter web loadssdk.gleap.io/latest.Tests
npm test: 38 suites, 719 tests pass. 19 new tests are inGleapSession.test.js› "signed session expiry". They cover:setUserJwtsigning in again on the visitor sessionsetUserJwtin flight before the answerclearSessionnpm run buildcompiles (build output not committed).Version
19.7.0 (behaviour change). CHANGELOG entry added.
🤖 Generated with Claude Code