Skip to content

fix: JWT provider restores the signed session after a reload; host-only cookies on IP hosts; version 19.5.2 - #208

Merged
boehlerlukas merged 1 commit into
masterfrom
fix-identity-provider-and-ip-cookies
Nov 7, 2026
Merged

boehlerlukas merged 1 commit into
masterfrom
fix-identity-provider-and-ip-cookies

Conversation

@boehlerlukas

Copy link
Copy Markdown
Contributor

Fixes two defects in the identity-token release (19.5.1) that showed up in today's production test.

1. A JWT provider on its own never restores a signed session after a reload (LOW–MEDIUM)

Problem. Identify with setUserJwt, then reload with only setUserJwtProvider set and no grant cookie (IP host, or cookie deleted). /sessions answers identity: { mode: 'signed', signed: false }, but the SDK never asks the provider and never identifies. The user stays unsigned: the server limits them to 14 days of history, and Kai treats them as unverified. The cause: needsSignedModeIdentify / identifyForSignedModeIfNeeded need lastIdentify.userHash, which lives in memory only, and withFreshIdentify returns early when there is no lastIdentify.

Fix (src/GleapSession.js). Two new methods, needsSignedModeProviderIdentify and identifyWithProviderForSignedModeIfNeeded. identifyForSignedModeIfNeeded uses them when it has no held identify to send. Once per session load (signedModeProviderAsked, reset in clearSession), the SDK asks the provider when all of these hold:

  • the answer says Signed mode for an identified session (session.userId set)
  • there is no live grant
  • the SDK holds no token
  • a provider is set
  • no identify is in flight, and signedModeIdentifySent and the one-minute GRANT_REFRESH_MIN_INTERVAL_MS guard allow it

The token goes through setUserJwt → identifySession, the same path as an app-supplied token. That path marks signedModeIdentifySent, so at most one identify goes out per load. A token for another user goes through the normal identity_mismatch recovery.

  • The provider's own guards still apply: one call at a time, 10 s timeout, 30 s backoff.
  • A null token sends nothing.
  • A logout, an app-supplied token, or a grant that arrives while the provider runs drops the provider's token.
  • setUserJwtProvider also runs the check when the session is already loaded, so a provider set after the /sessions answer still works.
  • Docs: index.d.ts, the Gleap.js JSDoc and the README now say the provider also restores the signed session after a reload.

2. Cookies are never stored on IP-address hosts (LOW)

Problem. setGleapCookie wrote domain=removeSubDomain(host). On 127.0.0.1 that is domain=0.1, and on [::1]:8080 it is domain=[. The browser rejects these cookies, so the session cookie and the grant cookie were lost.

Fix (src/GleapHelper.js). A new cookieDomainFor(host) returns null for IPv4 and for IPv6 (bracketed or bare). setGleapCookie and eraseGleapCookie then leave out the domain attribute, which makes the cookie host-only, and erasing it still works. Domain hosts are unchanged. localhost also keeps domain=localhost, which worked in testing.

Tests

  • src/GleapSession.test.js, new describe block "signed mode restored by the JWT provider alone":

    • the provider is asked once, the identify carries its token, and the grant and cookie come back
    • no loop across repeated unsigned answers
    • one provider call at a time
    • no provider → no identify
    • provider returns null → nothing sent, nothing logged, not asked again
    • a token for another user → identity_mismatch recovery
    • a provider set after the /sessions answer is asked
    • no provider call for a Classic answer, an anonymous session, a live grant or a held token
    • a logout while the provider runs drops its token

    Without the fix, all 8 provider tests fail.

  • src/GleapHelper.test.js: a cookieDomainFor table (domains, .co.uk, localhost, IPv4, IPv6 with and without brackets or port), host-only write and erase on 127.0.0.1:8080 and [::1]:8080, and an unchanged Domain on example.com and localhost.

Results

  • npm test -- --ci: 37 suites, 695 tests, all pass.
  • npm run build: compiles. The only warnings are the existing webpack bundle-size warnings.
  • npm pack --dry-run: OK (gleap-19.5.2.tgz).
  • The repo has no lint script or ESLint config.

Versioning

package.json, package-lock.json and CHANGELOG.md move to 19.5.2, following the repo convention. Not released: no tag, no npm publish. Build output is not committed. Note that the CDN deploys on every push to master, so merging this ships it to sdk.gleap.io.

🤖 Generated with Claude Code

…ly cookies on IP hosts; version 19.5.2

- On a Signed-mode /sessions answer for an identified session without a
  live grant, with no token held and a JWT provider set, ask the provider
  once per session load and identify with its token through setUserJwt
  (identity_mismatch recovery included). Respects signedModeIdentifySent,
  the one-minute signed-mode interval, the provider backoff and the single
  in-flight provider call. A null token sends nothing. A provider set after
  the session loaded is asked as well.
- setGleapCookie / eraseGleapCookie omit the domain attribute on IPv4 and
  IPv6 hosts (the browser rejected domain=0.1 for 127.0.0.1), so the session
  and grant cookies are stored there. Domain hosts and localhost unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant