Skip to content

Commit f096eaa

Browse files
committed
fix: 2% of pro users have premature trial exit probably due to trust ring init ordering
1 parent f3da6be commit f096eaa

2 files changed

Lines changed: 36 additions & 11 deletions

File tree

src-node/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/phoenix/trust_ring.js

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,13 @@ const SIGNATURE_SALT_KEY = Phoenix.isTestWindow ? "SIGNATURE_SALT_KEY_TEST" : "S
184184
const VERSION_PORTER_KEY = Phoenix.isTestWindow ? "VERSION_PORTER_TEST" : "VERSION_PORTER";
185185
const { key, iv } = _selectKeys();
186186

187+
let _trustRingReadyResolve;
188+
let _trustRingReady = new Promise(resolve => {
189+
_trustRingReadyResolve = resolve;
190+
});
191+
187192
async function setCredential(credKey, secret) {
193+
await _trustRingReady;
188194
if(!window.__IS_NATIVE_SHELL__){
189195
throw new Error("Phoenix API key can only be set in native shell!");
190196
}
@@ -200,6 +206,7 @@ async function setCredential(credKey, secret) {
200206
}
201207

202208
async function getCredential(credKey) {
209+
await _trustRingReady;
203210
if(!window.__IS_NATIVE_SHELL__){
204211
throw new Error("Phoenix API key can only be get in native shell!");
205212
}
@@ -219,6 +226,7 @@ async function getCredential(credKey) {
219226
}
220227

221228
async function removeCredential(credKey) {
229+
await _trustRingReady;
222230
if(!window.__IS_NATIVE_SHELL__){
223231
throw new Error("Phoenix API key can only be removed in native shell!");
224232
}
@@ -235,6 +243,7 @@ async function removeCredential(credKey) {
235243

236244
let _dismatled = false;
237245
async function dismantleKeyring() {
246+
await _trustRingReady;
238247
if(_dismatled){
239248
throw new Error("Keyring can only be dismantled once!");
240249
// and once dismantled, the next line should be reload page. this is a strict security posture requirement to
@@ -249,25 +258,41 @@ async function dismantleKeyring() {
249258
if(!window.__IS_NATIVE_SHELL__){
250259
return;
251260
}
261+
let result;
252262
if(window.__TAURI__) {
253-
return window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", {key, iv});
254-
}
255-
if(window.__ELECTRON__) {
256-
return window.electronAPI.removeTrustWindowAesKey(key, iv);
263+
result = await window.__TAURI__.tauri.invoke("remove_trust_window_aes_key", {key, iv});
264+
} else if(window.__ELECTRON__) {
265+
result = await window.electronAPI.removeTrustWindowAesKey(key, iv);
257266
}
267+
// After dismantling, reset the gate so credential APIs block until a new trust ring is established
268+
_trustRingReady = new Promise(resolve => {
269+
_trustRingReadyResolve = resolve;
270+
});
271+
return result;
258272
}
259273

260274
export async function initTrustRing() {
261275
if(!window.__IS_NATIVE_SHELL__){
276+
_trustRingReadyResolve();
262277
return;
263278
}
264279
// this will only work once in a window unless dismantleKeyring is called. So this is safe as
265280
// a public export as essentially this is a fn that only works in the boot and shutdown phase.
266-
if(window.__TAURI__) {
267-
await window.__TAURI__.tauri.invoke("trust_window_aes_key", {key, iv});
268-
} else if(window.__ELECTRON__) {
269-
await window.electronAPI.trustWindowAesKey(key, iv);
270-
}
281+
try {
282+
if(window.__TAURI__) {
283+
await window.__TAURI__.tauri.invoke("trust_window_aes_key", {key, iv});
284+
} else if(window.__ELECTRON__) {
285+
await window.electronAPI.trustWindowAesKey(key, iv);
286+
}
287+
} catch(e) {
288+
// Trust may already be established for this window (e.g., iframe reusing parent's trust).
289+
// This is expected for tests and not an error - the trust ring is still functional. But for live this is
290+
// a critical error that should never happen.
291+
window.logger && window.logger.reportError(e, "Error establishing trust ring");
292+
const Metrics = window.Metrics;
293+
Metrics && Metrics.countEvent(Metrics.EVENT_TYPE.ERROR, "trustRing", "initFailed");
294+
}
295+
_trustRingReadyResolve();
271296

272297
await _portCredentials();
273298
}

0 commit comments

Comments
 (0)