tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_oauth_token_storage.js (4796B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { FxAccounts } = ChromeUtils.importESModule(
      7  "resource://gre/modules/FxAccounts.sys.mjs"
      8 );
      9 const { FxAccountsClient } = ChromeUtils.importESModule(
     10  "resource://gre/modules/FxAccountsClient.sys.mjs"
     11 );
     12 
     13 // We grab some additional stuff via the system global.
     14 var { AccountState } = ChromeUtils.importESModule(
     15  "resource://gre/modules/FxAccounts.sys.mjs"
     16 );
     17 
     18 function promiseNotification(topic) {
     19  return new Promise(resolve => {
     20    let observe = () => {
     21      Services.obs.removeObserver(observe, topic);
     22      resolve();
     23    };
     24    Services.obs.addObserver(observe, topic);
     25  });
     26 }
     27 
     28 // A storage manager that doesn't actually write anywhere.
     29 function MockStorageManager() {}
     30 
     31 MockStorageManager.prototype = {
     32  promiseInitialized: Promise.resolve(),
     33 
     34  initialize(accountData) {
     35    this.accountData = accountData;
     36  },
     37 
     38  finalize() {
     39    return Promise.resolve();
     40  },
     41 
     42  getAccountData() {
     43    return Promise.resolve(this.accountData);
     44  },
     45 
     46  updateAccountData(updatedFields) {
     47    for (let [name, value] of Object.entries(updatedFields)) {
     48      if (value == null) {
     49        delete this.accountData[name];
     50      } else {
     51        this.accountData[name] = value;
     52      }
     53    }
     54    return Promise.resolve();
     55  },
     56 
     57  deleteAccountData() {
     58    this.accountData = null;
     59    return Promise.resolve();
     60  },
     61 };
     62 
     63 // Just enough mocks so we can avoid hawk etc.
     64 function MockFxAccountsClient() {
     65  this._email = "nobody@example.com";
     66  this._verified = false;
     67 
     68  this.accountStatus = function (uid) {
     69    return Promise.resolve(!!uid && !this._deletedOnServer);
     70  };
     71 
     72  this.signOut = function () {
     73    return Promise.resolve();
     74  };
     75  this.registerDevice = function () {
     76    return Promise.resolve();
     77  };
     78  this.updateDevice = function () {
     79    return Promise.resolve();
     80  };
     81  this.signOutAndDestroyDevice = function () {
     82    return Promise.resolve();
     83  };
     84  this.getDeviceList = function () {
     85    return Promise.resolve();
     86  };
     87 
     88  FxAccountsClient.apply(this);
     89 }
     90 
     91 MockFxAccountsClient.prototype = {};
     92 Object.setPrototypeOf(
     93  MockFxAccountsClient.prototype,
     94  FxAccountsClient.prototype
     95 );
     96 
     97 function MockFxAccounts() {
     98  return new FxAccounts({
     99    fxAccountsClient: new MockFxAccountsClient(),
    100    newAccountState(credentials) {
    101      // we use a real accountState but mocked storage.
    102      let storage = new MockStorageManager();
    103      storage.initialize(credentials);
    104      return new AccountState(storage);
    105    },
    106    _getDeviceName() {
    107      return "mock device name";
    108    },
    109    fxaPushService: {
    110      registerPushEndpoint() {
    111        return new Promise(resolve => {
    112          resolve({
    113            endpoint: "http://mochi.test:8888",
    114          });
    115        });
    116      },
    117    },
    118  });
    119 }
    120 
    121 async function createMockFxA() {
    122  let fxa = new MockFxAccounts();
    123  let credentials = {
    124    email: "foo@example.com",
    125    uid: "1234@lcip.org",
    126    sessionToken: "dead",
    127    scopedKeys: {
    128      [SCOPE_OLD_SYNC]: {
    129        kid: "key id for sync key",
    130        k: "key material for sync key",
    131        kty: "oct",
    132      },
    133    },
    134    verified: true,
    135  };
    136  await fxa._internal.setSignedInUser(credentials);
    137  return fxa;
    138 }
    139 
    140 // The tests.
    141 
    142 add_task(async function testCacheStorage() {
    143  let fxa = await createMockFxA();
    144 
    145  // Hook what the impl calls to save to disk.
    146  let cas = fxa._internal.currentAccountState;
    147  let origPersistCached = cas._persistCachedTokens.bind(cas);
    148  cas._persistCachedTokens = function () {
    149    return origPersistCached().then(() => {
    150      Services.obs.notifyObservers(null, "testhelper-fxa-cache-persist-done");
    151    });
    152  };
    153 
    154  let promiseWritten = promiseNotification("testhelper-fxa-cache-persist-done");
    155  let tokenData = { token: "token1", somethingelse: "something else" };
    156  let scopeArray = ["foo", "bar"];
    157  cas.setCachedToken(scopeArray, tokenData);
    158  deepEqual(cas.getCachedToken(scopeArray), tokenData);
    159 
    160  deepEqual(cas.oauthTokens, { "bar|foo": tokenData });
    161  // wait for background write to complete.
    162  await promiseWritten;
    163 
    164  // Check the token cache made it to our mocked storage.
    165  deepEqual(cas.storageManager.accountData.oauthTokens, {
    166    "bar|foo": tokenData,
    167  });
    168 
    169  // Drop the token from the cache and ensure it is removed from the json.
    170  promiseWritten = promiseNotification("testhelper-fxa-cache-persist-done");
    171  await cas.removeCachedToken("token1");
    172  deepEqual(cas.oauthTokens, {});
    173  await promiseWritten;
    174  deepEqual(cas.storageManager.accountData.oauthTokens, {});
    175 
    176  // sign out and the token storage should end up with null.
    177  let storageManager = cas.storageManager; // .signOut() removes the attribute.
    178  await fxa.signOut(/* localOnly = */ true);
    179  deepEqual(storageManager.accountData, null);
    180 });