@@ -181,6 +181,7 @@ function _selectKeys() {
181181const CRED_KEY_API = Phoenix . isTestWindow ? "API_KEY_TEST" : "API_KEY" ;
182182const CRED_KEY_PROMO = Phoenix . isTestWindow ? "PROMO_GRANT_KEY_TEST" : "PROMO_GRANT_KEY" ;
183183const SIGNATURE_SALT_KEY = Phoenix . isTestWindow ? "SIGNATURE_SALT_KEY_TEST" : "SIGNATURE_SALT_KEY" ;
184+ const VERSION_PORTER_KEY = Phoenix . isTestWindow ? "VERSION_PORTER_TEST" : "VERSION_PORTER" ;
184185const { key, iv } = _selectKeys ( ) ;
185186
186187async function setCredential ( credKey , secret ) {
@@ -243,6 +244,80 @@ export async function initTrustRing() {
243244 // this will only work once in a window unless dismantleKeyring is called. So this is safe as
244245 // a public export as essentially this is a fn that only works in the boot and shutdown phase.
245246 await window . __TAURI__ . tauri . invoke ( "trust_window_aes_key" , { key, iv} ) ;
247+
248+ _portCredentials ( ) ;
249+ }
250+ async function reinstallCreds ( ) {
251+ if ( ! window . __TAURI__ ) {
252+ throw new Error ( "reinstallCreds can only be called in tauri shell!" ) ;
253+ }
254+ // Read current credential values
255+ const apiKey = await getCredential ( CRED_KEY_API ) ;
256+ const promoKey = await getCredential ( CRED_KEY_PROMO ) ;
257+ const saltKey = await getCredential ( SIGNATURE_SALT_KEY ) ;
258+
259+ // Remove credentials from keychain
260+ if ( apiKey ) {
261+ await removeCredential ( CRED_KEY_API ) ;
262+ }
263+ if ( promoKey ) {
264+ await removeCredential ( CRED_KEY_PROMO ) ;
265+ }
266+ if ( saltKey ) {
267+ await removeCredential ( SIGNATURE_SALT_KEY ) ;
268+ }
269+
270+ // Re-set credentials to refresh keychain access
271+ if ( apiKey ) {
272+ await setCredential ( CRED_KEY_API , apiKey ) ;
273+ }
274+ if ( promoKey ) {
275+ await setCredential ( CRED_KEY_PROMO , promoKey ) ;
276+ }
277+ if ( saltKey ) {
278+ await setCredential ( SIGNATURE_SALT_KEY , saltKey ) ;
279+ }
280+
281+ const currentVersion = Phoenix . metadata . version ;
282+ await setCredential ( VERSION_PORTER_KEY , currentVersion ) ;
283+ }
284+
285+ /**
286+ * Handles keychain credential portability across app versions on macOS.
287+ * not a problem in windows/linux.
288+ *
289+ * On macOS, the system keychain ties stored credentials to the app’s code signature.
290+ * If the signature changes (for example: running a debug build, unsigned dev build,
291+ * or re-signed binary), macOS will repeatedly prompt the user for their password
292+ * every time credentials are accessed. This does not usually happen in official
293+ * signed release builds, but it can be disruptive during development.
294+ *
295+ * To reduce this annoyance, we track the app version in the keychain. If the
296+ * stored version and the current version don’t match, we reinstall credentials
297+ * under the new signature so that future keychain access works without constant
298+ * prompts.
299+ */
300+ async function _portCredentials ( ) {
301+ if ( ! Phoenix . isNativeApp || Phoenix . platform === "win" || Phoenix . platform === "linux" ) {
302+ return ;
303+ }
304+ try {
305+ const storedVersion = await getCredential ( VERSION_PORTER_KEY ) ;
306+ const currentVersion = Phoenix . metadata . version ;
307+
308+ if ( ! storedVersion && currentVersion ) {
309+ // First boot or version key doesn't exist, set it
310+ await setCredential ( VERSION_PORTER_KEY , currentVersion ) ;
311+ } else if ( storedVersion && currentVersion && storedVersion !== currentVersion ) {
312+ // Version changed, reinstall credentials
313+ console . log ( `Version changed from ${ storedVersion } to ${ currentVersion } , reinstalling credentials` ) ;
314+ // Update stored version first to prevent races with multi phoenix windows
315+ await setCredential ( VERSION_PORTER_KEY , currentVersion ) ;
316+ await reinstallCreds ( ) ;
317+ }
318+ } catch ( error ) {
319+ console . error ( "Error during version-based credential check:" , error ) ;
320+ }
246321}
247322
248323/**
@@ -293,7 +368,8 @@ window.KernalModeTrust = {
293368 generateRandomKeyAndIV,
294369 dismantleKeyring,
295370 generateDataSignature,
296- validateDataSignature
371+ validateDataSignature,
372+ reinstallCreds
297373} ;
298374if ( Phoenix . isSpecRunnerWindow ) {
299375 window . specRunnerTestKernalModeTrust = window . KernalModeTrust ;
0 commit comments