Skip to content

fix: Deeplink auth when host is already connected#7358

Open
yash-rajpal wants to merge 2 commits into
developfrom
fix/deeplink-auth
Open

fix: Deeplink auth when host is already connected#7358
yash-rajpal wants to merge 2 commits into
developfrom
fix/deeplink-auth

Conversation

@yash-rajpal
Copy link
Copy Markdown
Member

@yash-rajpal yash-rajpal commented May 28, 2026

Proposed changes

During deeplink authentication, the deeplink saga does not check whether the server is already connected. Instead, it always attempts to add the server as a new one.

This triggers the selectServer saga. Since the server is already connected, selectServer redirects the user directly into the app without performing login and dispatches SERVER.SELECT_CANCEL.

However, the deeplink saga is waiting for a SERVER.SELECT_SUCCESS action, which is never dispatched in this scenario, causing the deeplink authentication flow to break.

This PR fixes this by checking if server is already connected in deeplink saga, before attempting to add the server. If server is already connected it continues the login flow without trying to add new server or wait for SERVER.SELECT_SUCCESS

Issue(s)

How to test or reproduce

  • Open App and add a workspace
  • App takes user to login page
  • Now leave the app and use deeplink auth (link having userId and token)
  • The deeplink gets stuck, doesn't log the user in properly

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Further comments

PRM-49

Summary by CodeRabbit

  • Bug Fixes

    • Deep linking preserves http://localhost (with optional port) instead of forcing HTTPS.
    • Avoids unnecessary server reconnections when navigating between links to the same host.
  • Tests

    • Added coverage for deep-link flow when already connected to the target host.

Review Change Stack

@yash-rajpal yash-rajpal temporarily deployed to approve_e2e_testing May 28, 2026 18:05 — with GitHub Actions Inactive
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Walkthrough

This PR preserves http://localhost during host normalization and updates deep-linking to skip server re-initialization and NewServer emission when the SDK is already connected to the deep-linked host; tests cover the already-connected scenario.

Changes

Deep-linking Optimization

Layer / File(s) Summary
Localhost host preservation
app/lib/methods/helpers/normalizeDeepLinkingServerHost.ts
http://localhost (with optional port) is preserved instead of being converted to https://; other non-HTTPS hosts continue to be normalized to HTTPS and trailing slashes trimmed.
Conditional server initialization in deep-link saga
app/sagas/deepLinking.js
The saga inspects sdk.current?.client?.host and, when the target host matches the connected host, skips APP.START(ROOT_OUTSIDE), serverInitAdd(server), delay, and EventEmitter.emit('NewServer'); waiting for types.SERVER.SELECT_SUCCESS is made conditional on initiating a new connection. Token-based login/resume continues after the conditional wait.
Tests for already-connected host flow
app/sagas/__tests__/deepLinking.test.ts
Imports sdk and EventEmitter, sets sdk.current.client.host to the target host in tests, and verifies the saga does not emit NewServer, does not require SERVER.SELECT_SUCCESS, and still completes the deep-link flow calling goRoom.

Sequence Diagram(s)

sequenceDiagram
  participant DeepLinkSaga
  participant SDKClient as sdk.current.client
  participant Redux as ReduxStore
  participant EventEmitter

  DeepLinkSaga->>SDKClient: read host (sdk.current?.client?.host)
  alt hostAlreadyConnected == false
    DeepLinkSaga->>Redux: dispatch APP.START(ROOT_OUTSIDE)
    DeepLinkSaga->>Redux: dispatch serverInitAdd(server)
    DeepLinkSaga->>DeepLinkSaga: delay
    DeepLinkSaga->>EventEmitter: emit NewServer
    DeepLinkSaga->>Redux: wait SERVER.SELECT_SUCCESS
  else hostAlreadyConnected == true
    DeepLinkSaga->>DeepLinkSaga: skip server init and SELECT_SUCCESS wait
  end
  DeepLinkSaga->>Redux: dispatch loginRequest(resumeToken)
  Redux->>DeepLinkSaga: LOGIN.SUCCESS
  DeepLinkSaga->>Redux: dispatch APP.START(ROOT_INSIDE)
  DeepLinkSaga->>DeepLinkSaga: call goRoom
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

area: authentication

Suggested reviewers

  • OtavioStasiak
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main fix: preventing re-initialization of server connection when deeplink host is already connected, which aligns with the core changes across normalizeDeepLinkingServerHost.ts, deepLinking.js saga, and test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • PRM-49: Request failed with status code 401

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
app/sagas/__tests__/deepLinking.test.ts (1)

435-452: 💤 Low value

Restore the emit spy in afterEach so it can't leak on assertion failure.

If the assertion at Line 442 or Line 450 throws, Line 451 (emitSpy.mockRestore()) is never reached, leaving EventEmitter.emit spied for any subsequent test. Restoring in afterEach (or relying on restoreMocks) guarantees cleanup regardless of test outcome.

♻️ Move restore to afterEach
 	afterEach(() => {
 		jest.useRealTimers();
+		jest.restoreAllMocks();
 		// Reset so other describe blocks see the default empty host
 		(sdk.current as any).client.host = '';
 	});
-		expect(jest.mocked(goRoom)).toHaveBeenCalledTimes(1);
-		emitSpy.mockRestore();
+		expect(jest.mocked(goRoom)).toHaveBeenCalledTimes(1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/sagas/__tests__/deepLinking.test.ts` around lines 435 - 452, The test
creates a spy via emitSpy = jest.spyOn(EventEmitter, 'emit') inside a test but
only calls emitSpy.mockRestore() at the end of the test, which can leak if an
assertion fails; move the restore into a global afterEach so cleanup always
runs: declare emitSpy in the test file scope (or ensure access) and in an
afterEach() call emitSpy?.mockRestore() (or use jest.restoreAllMocks()) to unspy
EventEmitter.emit; reference the emitSpy variable and the EventEmitter.emit spy
usage in deepLinking.test.ts to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/sagas/__tests__/deepLinking.test.ts`:
- Around line 435-452: The test creates a spy via emitSpy =
jest.spyOn(EventEmitter, 'emit') inside a test but only calls
emitSpy.mockRestore() at the end of the test, which can leak if an assertion
fails; move the restore into a global afterEach so cleanup always runs: declare
emitSpy in the test file scope (or ensure access) and in an afterEach() call
emitSpy?.mockRestore() (or use jest.restoreAllMocks()) to unspy
EventEmitter.emit; reference the emitSpy variable and the EventEmitter.emit spy
usage in deepLinking.test.ts to apply the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67f1c609-338f-48b9-b49b-7bf0950ef76b

📥 Commits

Reviewing files that changed from the base of the PR and between 4faf40b and b01a518.

📒 Files selected for processing (1)
  • app/sagas/__tests__/deepLinking.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/sagas/__tests__/deepLinking.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Use TypeScript with strict mode and baseUrl set to app/ for import resolution

Files:

  • app/sagas/__tests__/deepLinking.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use Prettier with tabs, single quotes, 130 char width, no trailing commas, arrow parens avoid, bracket same line
Use @rocket.chat/eslint-config base with React, React Native, TypeScript, Jest plugins

Files:

  • app/sagas/__tests__/deepLinking.test.ts
app/sagas/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Sagas for side effects should be placed in app/sagas/ directory (init, login, rooms, messages, encryption, deepLinking, videoConf)

Files:

  • app/sagas/__tests__/deepLinking.test.ts
🧠 Learnings (1)
📚 Learning: 2026-04-30T17:07:51.020Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7274
File: app/lib/services/voip/MediaCallEvents.ts:0-0
Timestamp: 2026-04-30T17:07:51.020Z
Learning: In this Rocket.Chat React Native codebase, the ESLint rule `no-void: error` is enforced. When you see a promise returned from an async call that is not awaited (a “floating promise”), do not silence it with the `void somePromise()` pattern. Instead, handle the promise explicitly by attaching `.catch(...)` (or otherwise awaiting/handling the error) so unhandled-rejection risks are addressed in a way that satisfies the existing ESLint configuration.

Applied to files:

  • app/sagas/__tests__/deepLinking.test.ts
🔇 Additional comments (3)
app/sagas/__tests__/deepLinking.test.ts (3)

100-101: LGTM!


388-396: LGTM!


403-427: LGTM!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants