Skip to content

feat: Project ID naming; the JWT provider restores a signed session after a restart; version 19.5.0 - #63

Merged
boehlerlukas merged 1 commit into
masterfrom
feat/project-id-and-provider-restore
Nov 7, 2026
Merged

boehlerlukas merged 1 commit into
masterfrom
feat/project-id-and-provider-restore

Conversation

@boehlerlukas

Copy link
Copy Markdown
Contributor

Implements the Project ID contract (7 Nov 2026) for Android and ports the identity fix from GleapSDK/JavaScript-SDK#208.

1. "SDK key" is now "Project ID"

  • Gleap.initialize(String projectId, Application application): same signature and value, so every existing key keeps working. Only the parameter name and the Javadoc changed. There are no public sdkKey / apiKey setters or config objects (GleapConfig is package-private), so no new methods were needed.
  • README, the sample app (MainApplication) and log lines say "Project ID". The Api-Token header, the apiKey WebSocket query parameter and the sdkKey field sent to the widget are wire names and stay as they are.

2. The JWT provider restores a signed session after an app restart

The gap existed. setUserJwt keeps the token in memory only. After an app restart on a Signed project, the SDK held the session (and maybe a grant the server no longer accepts) but no token. When /sessions answered identity: { mode: "signed", signed: false }, the existing held-identify path only re-sent a stored legacy user hash or an identify the app had already passed in. A provider set with setUserJwtProvider was only asked when a token expired, so the user stayed unsigned.

Fix (GleapSessionController.identifyWithProviderForSignedModeIfNeeded, called after the /sessions answer in GleapBaseSessionService and from setUserJwtProvider). The provider is asked once per app start (signedModeProviderAsked, reset in clearUserSession) when all of these hold:

  • the session answer says Signed mode and the identified session has no grant (signedGrantMissing: identity.mode == "signed", a user id, no grant, no signed identify sent for this session yet)
  • the SDK holds no token and no user hash (no lastIdentify hash, no stored legacy hash)
  • no identify is queued or running
  • a provider is set

The token goes through Gleap.setUserJwt, the normal path. claimSignedGrantRequest makes that identify the one signed identify of the session, so there is never a loop. Other behaviour:

  • The provider's own guards still apply: one call at a time, 10 s timeout, 30 s pause after a failure.
  • A null token sends nothing.
  • A logout, an identify from the app, or a grant that arrives while the provider runs drops its token.
  • A token for another user goes through the existing identity_mismatch recovery.
  • setUserJwtProvider called after the session answer runs the same check, so the provider works whether it is set before or after Gleap.initialize.
  • Javadoc (Gleap, iGleap), README and CHANGELOG describe the new behaviour.

Tests

New SignedModeProviderRestoreTest, 10 tests:

  • the provider is asked once and the identify carries its token, and the grant comes back
  • repeated unsigned answers never loop
  • a null token sends nothing and the provider is not asked again
  • no provider means no identify
  • a provider set after the session answer is asked
  • no provider call for a Classic answer or an anonymous session
  • a token from the app, or a stored legacy hash, is sent instead of asking the provider
  • a logout while the provider runs drops its token

With the fix disabled, the 5 positive tests fail. The other 5 cover the guards and pass either way.

How it was run. There is no JDK 17–23 on this machine, only Android Studio's JBR 25, which Gradle 8.10.2 cannot run on. So ./gradlew test could not run. Instead, the main and test sources were compiled with JBR 25 javac against the SDK's android.jar (android-37) plus:

  • the OkHttp, Okio, Kotlin stdlib and JUnit 4 jars bundled with Android Studio
  • compile-only stubs for the androidx classes and R / BuildConfig

The tests then ran with plain JUnit 4 against:

  • a mockable android.jar (method bodies replaced with default returns, like AGP's unitTests.returnDefaultValues = true)
  • org.json built from the SDK's android-37 sources

Result: all 260 unit tests pass (36 classes), including the 10 new ones. CI (./gradlew :gleap:testDebugUnitTest :gleap:assembleRelease) is the authoritative run.

Versioning

VERSION_NAME is 19.5.0 (VERSION_CODE 195000) and CHANGELOG.md has a ## 19.5.0 section. This is a minor bump to stay in sync with iOS 19.5.0, which adds initialize(withProjectId:). Nothing is tagged or published to Maven Central.

For the wrapper SDKs

  • Android Gleap.initialize(String, Application) is unchanged and works with old and new native versions.
  • The restore logic is native. Wrappers need no logic change; their provider bridge just has to answer within 10 s, pass null on failure, and work whether it is set before or after initialize.

🤖 Generated with Claude Code

…fter a restart; version 19.5.0

Project ID: the key Gleap.initialize takes is now called the Project ID in
the Javadoc, README, sample app and log lines. The signature and the value
are unchanged (Gleap.initialize(String projectId, Application)), so every
existing key keeps working.

Identity: setUserJwt keeps the token in memory only, so after an app restart
on a Signed project the SDK had the session but no token, and a provider set
with setUserJwtProvider was never asked; the user stayed unsigned. When the
session answer says identity.mode "signed" without a grant for an identified
session, no token or user hash is at hand and a provider is set, the
provider is now asked once per app start and its token goes through
setUserJwt (the one signed identify of the session). The provider's guards
apply, a null token sends nothing, a logout or an identify of the app while
it runs drops the token, and setUserJwtProvider after the session answer
runs the same check.

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