RocketChat / RocketChat/EmbeddedChat

[Bug]: Cross-Instance Authentication Corruption via Hardcoded `ec_token` Key

Open
#1,105 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
JavaScript
Stars
165
Forks
381
Avg merge
1d 2h
Merged PRs (30d)
1

Description

The EmbeddedChat authentication logic hardcodes the localStorage key to ec_token in packages/react/src/lib/auth.js. This prevents multiple instances of EmbeddedChat from co-existing on the same domain with independent sessions.

If two EmbeddedChat instances (for example, a Sales Bot and a Support Bot) are embedded on the same page or domain, logging into one instance overwrites the authentication token of the other.


Reproduction Steps

  1. Embed two instances of EmbeddedChat on the same page.
  2. Log in to Instance A.
  3. Log in to Instance B.
  4. Refresh the page.

Result: Instance A is logged in as Instance B’s user (or ends up with an invalid token), because the shared ec_token value was overwritten.


Jest Test

const localStorageMock = (function () {
  let store = {};
  return {
    getItem: (key) => store[key] || null,
    setItem: (key, value) => {
      store[key] = value.toString();
    },
    removeItem: (key) => {
      delete store[key];
    },
    clear: () => {
      store = {};
    },
  };
})();

Object.defineProperty(global, 'localStorage', {
  value: localStorageMock,
});

import { getTokenStorage } from './auth';

describe('Cross-Instance Authentication Corruption', () => {
  beforeEach(() => {
    localStorage.clear();
    jest.clearAllMocks();
  });

  const getLocalStorageKey = () => 'ec_token';

  test('Two seemingly independent instances overwrite each other\'s tokens', async () => {
    const auth = getTokenStorage(false);
    const { saveToken, getToken } = auth;

    // Simulate Instance A login
    const instanceAToken = 'token_for_instance_A';
    await saveToken(instanceAToken);

    expect(localStorage.getItem(getLocalStorageKey())).toBe(instanceAToken);
    expect(await getToken()).toBe(instanceAToken);

    // Simulate Instance B login
    const instanceBToken = 'token_for_instance_B';
    await saveToken(instanceBToken);

    // Instance A token is overwritten
    expect(localStorage.getItem(getLocalStorageKey())).toBe(instanceBToken);

    const currentToken = await getToken();
    expect(currentToken).not.toBe(instanceAToken);
    expect(currentToken).toBe(instanceBToken);
  });
});

Expected Behavior

The token storage key should be configurable or automatically namespaced to allow multiple isolated EmbeddedChat instances on the same domain.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/react/src/lib/auth.js, especially getTokenStorage(false), and reproduce the two-instance scenario with the Jest test described in the issue. Trace how saveToken and getToken derive their localStorage key. Done means separate EmbeddedChat instances retain independent tokens, with coverage for saving and retrieving both tokens.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react
Domain
authentication, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.