Skip to content

Fix a crash when initialising StreamVideoClient with a UserType.Guest#1637

Merged
aleksandar-apostolov merged 6 commits intodevelopfrom
bugfix/rahullohra/guest-user
Apr 1, 2026
Merged

Fix a crash when initialising StreamVideoClient with a UserType.Guest#1637
aleksandar-apostolov merged 6 commits intodevelopfrom
bugfix/rahullohra/guest-user

Conversation

@rahul-lohra
Copy link
Copy Markdown
Contributor

@rahul-lohra rahul-lohra commented Mar 31, 2026

Goal

Fix a crash in the Android SDK when initialising StreamVideoClient with a UserType.Guest user and no token. On iOS and React, the SDK silently calls /video/guest to obtain a JWT automatically — Android should behave the same way.

Additionally, the demo app incorrectly treated Guest and Anonymous as the same login path. This PR separates the two user types with correct labels and distinct entry points in the login UI.

Implementation

SDK — two bug fixes:

  1. StreamVideoBuilder — token blank validation was applied unconditionally to all user types. Scoped the check to UserType.Authenticated only. Guest users pass no token; the SDK internally calls POST /video/guest to fetch a JWT via guestUserJob.

  2. CoordinatorAuthInterceptor — when token was blank (guest/anonymous), the interceptor still appended an Authorization: header with an empty string, causing the server to return HTTP 403. Added a guard so the Authorization header is only added when the token is non-blank.

🎨 UI Changes

image image

Testing

  1. Sign out
  2. Choose Sign in as guest
  3. The guest user do not have the role of creating a call or joining an ended call.
  4. Use another authenticated user to join a video with new ID
  5. Join that room with the guest user
  6. The guest user should be able to join the video room

Summary by CodeRabbit

  • New Features
    • Added guest user sign-in option to the login interface, providing an alternative way to access video calls
    • Guest users can now connect and participate in calls

@rahul-lohra rahul-lohra self-assigned this Mar 31, 2026
@rahul-lohra rahul-lohra requested a review from a team as a code owner March 31, 2026 10:18
@rahul-lohra rahul-lohra added the pr:bug Fixes a bug label Mar 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@rahul-lohra rahul-lohra changed the title Improve guest authentication on video sdk [AND-1134] Improve guest authentication on video sdk Mar 31, 2026
@rahul-lohra rahul-lohra changed the title [AND-1134] Improve guest authentication on video sdk Improve guest authentication on video sdk Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Walkthrough

This PR introduces guest sign-in support to the Stream Video Android SDK. Changes span the demo app's login UI, SDK initialization helpers, token validation logic, and network authentication handling. Key additions include new guest/anonymous initialization methods, adjusted WebSocket connection behavior for different user types, and updated tests validating guest user flows.

Changes

Cohort / File(s) Summary
Login UI & Resources
demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginScreen.kt, demo-app/src/main/res/values/strings.xml
Updated login options to distinguish between anonymous and new guest_user sign-in paths; added guest sign-in button and navigation handling for GuestSignInComplete state.
Login ViewModel
demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt
Added SignInAsGuest event and GuestSignInComplete UI state; implemented signInAsGuest() flow that generates random guest ID, creates guest User, persists via datastore, and initializes SDK via new helper.
SDK Initialization
demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt
Added loadSdkForGuest() and loadSdkForAnonymous() suspend functions for guest/anonymous user initialization; both manage SDK state, user persistence, and differ in WebSocket connection behavior.
Token & Connection Management
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoBuilder.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorAuthInterceptor.kt
Relaxed token validation to permit blank tokens for non-authenticated user types; updated WebSocket connection eligibility to authenticated users only; added coordinatorConnectionModule.updateToken("") call for guest setup; modified auth interceptor to conditionally set Authorization header only when token is non-blank.
Build Configuration
build.gradle.kts
Re-indented Gradle blocks and added directory existence check for git hooks before executing hook-copy commands.
Tests
stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/ClientAndAuthTest.kt
Updated anonymous user test to omit token; modified guest user test with time-based guest ID and connectOnInit = false; added guest user can connect and ignored guest user can join a call test cases.
Minor Updates
stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt
Added inline comments to local variable assignments (non-functional).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LoginScreen
    participant LoginViewModel
    participant DataStore
    participant StreamVideoInitHelper
    participant StreamVideoBuilder
    participant StreamVideo as StreamVideo SDK

    User->>LoginScreen: Tap "Sign in as Guest"
    LoginScreen->>LoginViewModel: emit SignInAsGuest event
    LoginViewModel->>LoginViewModel: signInAsGuest() flow triggered
    LoginViewModel->>LoginViewModel: Generate random guest ID
    LoginViewModel->>DataStore: updateUser(guestUser with UserType.Guest)
    DataStore-->>LoginViewModel: User persisted
    LoginViewModel->>StreamVideoInitHelper: loadSdkForGuest(dataStore, guestUser)
    StreamVideoInitHelper->>StreamVideoBuilder: Initialize SDK with blank token
    StreamVideoBuilder->>StreamVideoBuilder: Skip token validation for Guest type
    StreamVideoBuilder->>StreamVideo: initializeStreamVideo(token="", user=guestUser)
    StreamVideo-->>StreamVideoBuilder: SDK initialized
    StreamVideoInitHelper->>StreamVideo: connect() via instanceOrNull()
    StreamVideo-->>StreamVideoInitHelper: WebSocket connected
    StreamVideoInitHelper->>LoginViewModel: emit GuestSignInComplete
    LoginViewModel->>LoginScreen: Update UI state
    LoginScreen->>User: Navigate to call join screen
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Poem

🐰 A guest hops in with borrowed paws,
No token needed, breaking all the laws,
Anonymous sibling hides their name,
Stream's doors now open, both play the same game!
Authentication blooms in many ways,
Guest and anon brighten all our days!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides Goal, Implementation, UI Changes with screenshots, and Testing sections. However, it is missing the Contributor Checklist section from the template, which is required. Add the completed Contributor Checklist to confirm CLA signing, issue linkage, changelog updates, test coverage, and other required items before merge.
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve guest authentication on video sdk' accurately summarizes the main change: adding/fixing guest user authentication support in the SDK and separating guest from anonymous user flows.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/rahullohra/guest-user

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt (1)

178-198: ⚠️ Potential issue | 🟠 Major

Guest users fail to re-login on app restart.

signInIfValidUserExist() always emits LoginEvent.SignInSuccess for any stored user, which routes to signInSuccess() and fetches a server-side token via StreamService.instance.getAuthData(). For guest users (stored with type = UserType.Guest), this is incorrect—they should go through signInAsGuest() instead, which uses StreamVideoInitHelper.loadSdkForGuest() with an empty token.

This breaks the re-login flow: if a guest signs in, kills the app, and restarts, the app will attempt to fetch a server token for the guest user, causing sign-in to fail.

Proposed fix
 fun signInIfValidUserExist() {
     viewModelScope.launch {
         val user = dataStore.user.firstOrNull()
         if (user != null) {
             handleUiEvent(LoginEvent.Loading)
             if (BuildConfig.BUILD_TYPE != "benchmark") {
                 delay(10)
-                handleUiEvent(LoginEvent.SignInSuccess(userId = user.id))
+                if (user.type == UserType.Guest) {
+                    handleUiEvent(LoginEvent.SignInAsGuest)
+                } else {
+                    handleUiEvent(LoginEvent.SignInSuccess(userId = user.id))
+                }
             }
         } else {
             if (autoLogIn) {
                 handleUiEvent(LoginEvent.Loading)
                 handleUiEvent(
                     LoginEvent.SignInSuccess(
                         UserHelper.generateRandomString(upperCaseOnly = true),
                     ),
                 )
             }
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt`
around lines 178 - 198, signInIfValidUserExist incorrectly treats stored guest
users as normal users; update LoginViewModel.signInIfValidUserExist so that
after retrieving user = dataStore.user.firstOrNull() it checks if user.type ==
UserType.Guest and, if so, emits the guest path (e.g.
handleUiEvent(LoginEvent.SignInAsGuest(userId = user.id)) or otherwise trigger
the signInAsGuest flow) instead of LoginEvent.SignInSuccess; keep the existing
BUILD_TYPE benchmark delay and current non-guest behavior unchanged for
non-guest users.
🧹 Nitpick comments (1)
demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt (1)

360-360: Hardcoded HttpLoggingLevel.BODY may expose sensitive data.

Forcing HttpLoggingLevel.BODY overrides the caller's loggingLevel and logs full HTTP request/response bodies. This could expose JWTs and other sensitive tokens in logs, which conflicts with the coding guideline to "Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs."

Consider making this conditional on debug builds or removing the override to respect the caller's logging preferences.

♻️ Suggested fix for debug-only body logging
-            loggingLevel = loggingLevel.copy(httpLoggingLevel = HttpLoggingLevel.BODY),
+            loggingLevel = if (BuildConfig.DEBUG) {
+                loggingLevel.copy(httpLoggingLevel = HttpLoggingLevel.BODY)
+            } else {
+                loggingLevel
+            },

As per coding guidelines: "Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt`
at line 360, The code currently forces loggingLevel =
loggingLevel.copy(httpLoggingLevel = HttpLoggingLevel.BODY), which can leak
sensitive data; change this to respect the caller or enable BODY only for debug
builds (e.g. use BuildConfig.DEBUG or a passed-in debug flag) — update the
assignment in StreamVideoInitHelper (the loggingLevel variable) so that
HttpLoggingLevel.BODY is applied only when in debug mode, otherwise leave
loggingLevel.httpLoggingLevel unchanged or set a safer default (e.g. HEADERS or
NONE).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt`:
- Around line 185-230: The isInitialising flag in loadSdkForGuest is not reset
when an exception is thrown because isInitialising = false is after the thrown
exception; wrap the init logic in a try { ... } finally { isInitialising = false
} (or move the flag reset into a finally block) so isInitialising is always set
to false regardless of success or failure; update loadSdkForGuest to set
isInitialising = true before the try and reset it in finally, keeping the
existing catch behavior for setting _initState and rethrowing the exception.
- Around line 232-275: loadSdkForAnonymous has the same missing start guard and
non-guaranteed reset of isInitialising as loadSdkForGuest; add a guard at the
top of loadSdkForAnonymous that returns early if isInitialising is true (same
behavior as loadSdkForGuest), and move the isInitialising = false assignment
into a finally block so it always executes after the try/catch around
initializeStreamVideo / dataStore.updateUser; ensure _initState updates remain
in the try/catch as they are and reference the existing symbols
loadSdkForAnonymous, isInitialising, _initState, initializeStreamVideo, and
dataStore.updateUser when making the change.
- Around line 194-200: Add the same isInitialising guard used in loadSdk to
loadSdkForGuest to prevent concurrent inits: check isInitialising at the start
of loadSdkForGuest (alongside the existing StreamVideo.isInstalled check) and
return early if true, so you only set isInitialising = true and _initState.value
= InitializedState.RUNNING when no initialization is already in progress;
reference the loadSdk method's guard pattern and the symbols isInitialising,
loadSdkForGuest, StreamVideo.isInstalled, and _initState/InitializedState.

In
`@stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt`:
- Line 889: Remove the debug/placeholder inline comments "// noob" left next to
the EXTRA_JOIN_AND_RING usage; specifically edit the code that reads val
joinAndRing = intent.getBooleanExtra(EXTRA_JOIN_AND_RING, false) (and the other
occurrence near the second EXTRA_JOIN_AND_RING usage) and delete the trailing
comment so only the declaration remains, keeping behavior unchanged.

---

Outside diff comments:
In
`@demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt`:
- Around line 178-198: signInIfValidUserExist incorrectly treats stored guest
users as normal users; update LoginViewModel.signInIfValidUserExist so that
after retrieving user = dataStore.user.firstOrNull() it checks if user.type ==
UserType.Guest and, if so, emits the guest path (e.g.
handleUiEvent(LoginEvent.SignInAsGuest(userId = user.id)) or otherwise trigger
the signInAsGuest flow) instead of LoginEvent.SignInSuccess; keep the existing
BUILD_TYPE benchmark delay and current non-guest behavior unchanged for
non-guest users.

---

Nitpick comments:
In
`@demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt`:
- Line 360: The code currently forces loggingLevel =
loggingLevel.copy(httpLoggingLevel = HttpLoggingLevel.BODY), which can leak
sensitive data; change this to respect the caller or enable BODY only for debug
builds (e.g. use BuildConfig.DEBUG or a passed-in debug flag) — update the
assignment in StreamVideoInitHelper (the loggingLevel variable) so that
HttpLoggingLevel.BODY is applied only when in debug mode, otherwise leave
loggingLevel.httpLoggingLevel unchanged or set a safer default (e.g. HEADERS or
NONE).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dd4b3f1d-ac7c-40af-8a25-e21900441e27

📥 Commits

Reviewing files that changed from the base of the PR and between 9128cd3 and 063b82b.

📒 Files selected for processing (10)
  • build.gradle.kts
  • demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginScreen.kt
  • demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt
  • demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt
  • demo-app/src/main/res/values/strings.xml
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoBuilder.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorAuthInterceptor.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/ClientAndAuthTest.kt
  • stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 12.02 MB 12.02 MB 0.00 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.68 MB 0.00 MB 🟢
stream-video-android-ui-compose 6.28 MB 6.28 MB 0.00 MB 🟢

Copy link
Copy Markdown
Contributor

@aleksandar-apostolov aleksandar-apostolov left a comment

Choose a reason for hiding this comment

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

SDK fixes look good. LGTM.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 1, 2026

@aleksandar-apostolov aleksandar-apostolov merged commit 2ca0f86 into develop Apr 1, 2026
12 checks passed
@aleksandar-apostolov aleksandar-apostolov deleted the bugfix/rahullohra/guest-user branch April 1, 2026 10:00
@aleksandar-apostolov aleksandar-apostolov changed the title Improve guest authentication on video sdk Fix a crash when initialising StreamVideoClient with a UserType.Guest Apr 1, 2026
@stream-public-bot stream-public-bot added the released Included in a release label Apr 1, 2026
@stream-public-bot
Copy link
Copy Markdown
Collaborator

🚀 Available in v1.21.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:bug Fixes a bug released Included in a release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants