NSURLCredentialPersistencePermanent lacks built‑in client‑side expiration
0 reputation · 13 Dec 2022, 02:20 UTC
0 reputation · 13 Dec 2022, 02:20 UTC
When using NSURLCredentialPersistencePermanent the credential is written to the user Keychain, but Foundation does not attach an expiration timestamp or automatically purge the entry when the server later rejects it.
The URL Loading System will replay the stored credential for subsequent challenges until a 401 forces the delegate to react, and removal via removeCredential:forProtectionSpace: does not instantly invalidate in‑flight connections. App Sandbox and Keychain Access Groups further limit visibility across processes.
Developers must decide how to track a client‑side TTL, coordinate invalidation across sessions, and whether to rely on Keychain change notifications.
34775 reputation · 13 Dec 2022, 05:19 UTC
Foundation does not add a TTL or automatic purge to a credential saved with NSURLCredentialPersistencePermanent. The credential is written to the user Keychain and remains there until you explicitly remove it, the user deletes it, or you overwrite it. NSURLCredential does not expose creation or modification dates through the URL Loading System API, so client-side expiry cannot be enforced with Foundation alone.
The URL Loading System will replay a stored Permanent credential for matching protection spaces on subsequent challenges. Removal with removeCredential:forProtectionSpace: prevents future reuse but does not abort in-flight connections that already captured the credential.
Confirmed fact: there is no built-in client-side expiration. A server-driven pattern is the only Foundation-native option.
This pattern is reactive, not proactive. In-flight requests may still be sent with the stale credential until they fail.
Confirmed fact: apps in the same Keychain Access Group share the same keychain items. Removal is visible only on the next keychain query.
Steps for this case:
App Sandbox and matching App ID prefix/team ID are required for the group to be readable across processes.
Confirmed fact on macOS: SecKeychainAddCallback and SecKeychainRegisterCallback exist for keychain change notifications. These are macOS-specific and behavior is not guaranteed to be identical on iOS, macOS Catalyst, or future OS versions.
Likely explanation for iOS: there is no documented public cross-process Keychain change notification. Refresh triggering on iOS must rely on app-level signaling or re-challenge on next use.
Verification you can run locally: inspect credentialsForProtectionSpace before and after a 401 to confirm Permanent survives until explicit removal; remove mid-challenge and observe an already started URLSessionDataTask still transmits the old credential on retry; from two apps in a shared group delete the item in one and query in the other to confirm visibility only on next query; on macOS register a SecKeychain callback, delete the item, and confirm the callback fires.
One diagnostic detail that changes the recommendation: are you targeting iOS only or macOS as well? The availability of SecKeychain callbacks and the choice of cross-process signaling depends on that.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.