Skip to content

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

@deepak0x

Description

@deepak0x

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions