@@ -184,7 +184,13 @@ const SIGNATURE_SALT_KEY = Phoenix.isTestWindow ? "SIGNATURE_SALT_KEY_TEST" : "S
184184const VERSION_PORTER_KEY = Phoenix . isTestWindow ? "VERSION_PORTER_TEST" : "VERSION_PORTER" ;
185185const { key, iv } = _selectKeys ( ) ;
186186
187+ let _trustRingReadyResolve ;
188+ let _trustRingReady = new Promise ( resolve => {
189+ _trustRingReadyResolve = resolve ;
190+ } ) ;
191+
187192async 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
202208async 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
221228async 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
236244let _dismatled = false ;
237245async 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
260274export 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