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 });