Could you tell me about account security and passkeys? Our service is considering implementing passkeys, and these questions are to understand how Apple protects accounts from third parties.
① Apple website states that two-factor authentication is mandatory for newly created Apple Accounts. When did this requirement come into effect? What are the conditions for users who do not have two-factor authentication enabled?
② Apple website mentions that a verification code may be required when signing into an Apple Account from a new device or browser. Is my understanding of the situations where a verification code is requested accurate, as listed below? Are there any other situations?
Completely signing out of the Apple Account on that device.
Erasing the device.
Needing to change the password for security reasons.
③ If a user is already using a passkey on an Apple device, and then upgrades to a new device, will additional authentication, such as entering a PIN code, be required to use the passkey on the new device?
Prioritize user privacy and data security in your app. Discuss best practices for data handling, user consent, and security measures to protect user information.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
I am not very well versed in this area, so I would appreciate some guidance on what should be enabled or disabled. My app is an AppKit app. I have read the documentation and watched the video, but I find it hard to understand.
When I added the Enhanced Security capability in Xcode, the following options were enabled automatically:
Memory Safety
Enable Enhanced Security Typed Allocator
Runtime Protections
Enable Additional Runtime Platform Restrictions
Authenticate Pointers
Enable Read-only Platform Memory
The following options were disabled by default:
Memory Safety
Enable Hardware Memory Tagging
Memory Tag Pure Data
Prevent Receiving Tagged Memory
Enable Soft Mode for Memory Tagging
Should I enable these options? Is there anything I should consider disabling?
While working with Platform SSO on macOS, I’m trying to better understand how the system handles cases where a user’s local account password becomes unsynchronized with their Identity Provider (IdP) password—for example, when the device is offline during a password change.
My assumption is that macOS may store some form of persistent token during the Platform SSO user registration process (such as a certificate or similar credential), and that this token could allow the system to unlock the user’s login keychain even if the local password no longer matches the IdP password.
I’m hoping to get clarification on the following:
Does macOS actually use a persistent token to unlock the login keychain when the local account password is out of sync with the IdP password? If so, how is that mechanism designed to work?
If such a capability exists, is it something developers can leverage to enable a true passwordless authentication experience at the login window and lock screen (i.e., avoiding the need for a local password fallback)?
I’m trying to confirm what macOS officially supports so I can understand whether passwordless login is achievable using the persistent-token approach.
Thanks in advance for any clarification.
I am trying to integrate those into my app, stuck on it would not transfer to view that inside app, can someone help?
Scott
I'm building a macOS app that registers itself for HTTP(S) url handling and would like it to participate in the ASWebAuthenticationSession fow.
I did:
update the plist to register as a handler for URL shemes (http, https, file)
use NSWorkspace setDefaultApplication API to set this app as a default handler for urls in question
wrote custom ASWebAuthenticationSessionWebBrowserSessionHandling implementation and set it as SessionManager's sessionHandler
I launched this app from Xcode, then I triggered authentication flow from a third-party app.
When the sign in flow is initiated, I can see that my app is activeated (willBecomeActive and didBecomeActive callbacks are both called), but there is no call for sessionHandler's begin() method.
With some additional debugging I see that my app receives an apple event when the flow is started:
{sfri,auth target=SafariLaunchAgent {qntp=90/$627......},aapd=TRUE
If I switch system default browser back to Safari and then start the login flow, it correctly displays a sign in web page. What do I miss?
PS. I'm on Tahoe 26.2
[Q] When is the kTCCServiceEndpointSecurityClient set by macOS and in which conditions?
From what I'm gathering, the kTCCServiceEndpointSecurityClient can not be set by a configuration profile and the end user can only grant full disk access.
I searched for documentation on Apple's develop website (with the "kTCCServiceEndpointSecurityClient" search) and did not get any useful result.
Using a more complete search engine, or the forum search engine, only points to the old annoying big bug in macOS Ventura.
The problem I'm investigating is showing a process being listed as getting granted kTCCServiceEndpointSecurityClient permissions in the TCC database when:
it's not an Endpoint Security client.
it does not have the ES Client entitlement.
the bundle of the process includes another process that is an ES Client and is spawn-ed by this process but I don't see why this should have an impact.
This process is supposed to have been granted kTCCServiceSystemPolicyAllFiles via end user interaction or configuration profile.
AFAIK, the kTCCServiceEndpointSecurityClient permission can only be set by macOS itself.
So this looks like to be either a bug in macOS, an undocumented behavior or I'm missing something. Hence the initial question.
macOS 15.7.3 / Apple Silicon
Hi, I’m seeing a production issue on iOS 26+ that only affects some users.
symptoms:
It does NOT happen for all users.
It happens for a subset of users on iOS 26+.
If we write a value to Keychain and read it immediately in the same session, it succeeds.
However, after terminating the app and relaunching, the value appears to be gone:
SecItemCopyMatching returns errSecItemNotFound (-25300).
Repro (as observed on affected devices):
Launch app (iOS 26+).
Save PIN data to Keychain using SecItemAdd (GenericPassword).
Immediately read it using SecItemCopyMatching -> success.
Terminate the app (swipe up / kill).
Relaunch the app and read again using the same service -> returns -25300.
Expected:
The Keychain item should persist across app relaunch and remain readable (while the device is unlocked).
Actual:
After app relaunch, SecItemCopyMatching returns errSecItemNotFound (-25300) as if the item does not exist.
Implementation details (ObjC):
We store a “PIN” item like this (simplified):
addItem:
kSecClass: kSecClassGenericPassword
kSecAttrService: <FIXED_STRING>
kSecValueData:
kSecAttrAccessControl: SecAccessControlCreateWithFlags(..., kSecAttrAccessibleWhenUnlockedThisDeviceOnly, 0, ...)
readItem (SecItemCopyMatching):
kSecClass: kSecClassGenericPassword
kSecAttrService: <FIXED_STRING>
kSecReturnData: YES
(uses kSecUseOperationPrompt in our async method)
Question:
On iOS 26+, is there any known issue or new behavior where a successfully added GenericPassword item could later return errSecItemNotFound after app termination/relaunch for only some users/devices?
What should we check to distinguish:
OS behavior change/bug vs.
entitlement/access-group differences (app vs extension, provisioning/team changes),
device state/policies (MDM, passcode/biometrics changes),
query attributes we should include to make the item stable across relaunch?
Build / Dev Environment:
macOS: 15.6.1 (24G90)
Xcode: 26.2
Our Goal: We are implementing a workflow for derived credentials. Our objective is to have a PIV/CAC derived credential (from Entrust), installed via the Intune MDM Company Portal app, and then use it within our (managed) app to generate digital signatures.
Challenge: The Intune Company Portal installs these identities into the System Keychain. Because third-party apps are restricted from accessing private keys in the System Keychain, we are running into a roadblock.
Our Question: 1) Is there an API that allows us to create a signature without us having to pass the private key itself, but instead just pass a handle/some reference to the private key and then the API can access the private key in the system keychain and create the signature under the hood. SecKeyCreateSignature is the API method that creates a signature but requires passing a private key. 2) If #1 is not feasible, is there a way to get access to system keychain to retrieve certs + private key for managed apps
Critical Privacy and Security Issue: Spotlight disregards explicit exclusions and exposes user files
Apple has repeatedly ignored my reports about a critical privacy issue in Spotlight on macOS 26, and the problem persists in version 26.3 RC. This is not a minor glitch, it is a fundamental breach of user trust and privacy.
Several aspects of Spotlight fail to respect user settings:
• Hidden apps still exposed: In the Apps section (Cmd+1), Spotlight continues to display apps marked with the hidden flag, even though they should remain invisible.
• Clipboard reactivation: The clipboard feature repeatedly turns itself back on after logout or restart, despite being explicitly disabled by the user.
• Excluded files revealed: Most concerning, Spotlight exposes files in Suggestions and Recents (Cmd+3) even when those files are explicitly excluded under System Settings > Spotlight > Search Privacy.
This behavior directly violates user expectations and system settings. It is not only a major privacy issue but also a security risk, since sensitive files can be surfaced without consent.
Apple must address this immediately. Users rely on Spotlight to respect their privacy configurations, and the current behavior undermines both trust and security.
a
I am submitting this appeal because we believe our app was misunderstood and the review outcome and follow-up communication have been unfair and mechanically handled.
1) What happened / Outcome we disagree with
Our submission was rejected under Guideline 4.8 – Design – Login Services, with the reviewer stating that our app uses a third-party login service but does not provide an equivalent login option that meets Apple’s requirements (limited data collection, private email option, no advertising tracking without consent).
However, our game does not require or force any third-party login. The feature being treated as “login” is not a login service at all—it is Mainland China real-name / anti-addiction compliance verification.
2) Why we believe we comply with the App Review Guidelines
A. The feature in question is compliance verification, not login
Players do not need to create or log into any in-game account to play.
The flow exists solely to satisfy Mainland China real-name/anti-addiction compliance requirements.
Verification can be completed by either:
Using TapTap only as a real-name verification authorization option, or
Manually entering a Chinese ID number + legal name to pass verification and play.
Because this is verification, not an account login, Guideline 4.8 “Login Services” should not apply in the way the rejection message assumes.
B. There is no “playable account” to provide
After we clarified the above, we continued to receive repeated, template-like requests to provide a “playable account.” This request does not match our product design: there is no account system required for gameplay, so there is no “review account” to provide.
We have already provided the information needed to complete the verification path (ID + name for the compliance flow), yet the responses remained repetitive and did not reflect that the reviewer checked our explanation.
3) Why we believe the handling was unfair
Even after clearly explaining that this is not a login system, the review communication continued with mechanical responses that did not address the clarification. This caused significant delays to our release timeline and appears to be unfair treatment compared with many existing App Store apps that use similar compliance verification flows.
4) What we are requesting from the Appeals Team
Please investigate and correct the misclassification of our real-name compliance verification as a “login service” under Guideline 4.8.
If the team still believes Guideline 4.8 applies, please provide:
The specific guideline rationale, and
The exact screen/step in our app that is being interpreted as “login.”
Please advise what specific materials you need to proceed efficiently (e.g., screen recording of the verification flow, step-by-step review instructions, configuration notes). We are ready to provide them immediately.
I have filed bug reports on this to no avail, so I am bringing it up here hoping someone at Apple will address this. Since the first beta of 26.3, with voice control enabled there are now two icons in the menu bar (*plus an orange dot in full screen) that never go away. That orange microphone isn't serving its intended purpose to notify me that something is accessing my microphone if it is always displayed. I use voice control extensively, so it is nearly always on. In every prior version of macOS, the orange icon was not on for voice control. Even if voice control is not listening but simply enabled in system settings, the orange icon will be there. And there is no need for this icon to be on for a system service that is always listening. This orange icon in the menu bar at all times is incredibly irritating, as it takes up valuable space to the right of the notch, and causes other actual useful menu bar items to be hidden. As well, if some other application on my system were to turn on the mic and start recording me I would never know since that orange icon is always on. It also places an orange dot next to the control center icon taking up even more of the precious little menu bar real estate. Please fix this! Either exempt voice control (as Siri is always listening and it doesn't get the orange icon) or exempt all system services, or give me a way to turn this off. If you cannot tell, I find this incredibly annoying and frustrating.
Topic:
Privacy & Security
SubTopic:
General
% curl -v https://app-site-association.cdn-apple.com/a/v1/zfcs.bankts.cn
Host app-site-association.cdn-apple.com:443 was resolved.
IPv6: (none)
IPv4: 218.92.226.151, 119.101.148.193, 218.92.226.6, 115.152.217.3
Trying 218.92.226.151:443...
Connected to app-site-association.cdn-apple.com (218.92.226.151) port 443
ALPN: curl offers h2,http/1.1
(304) (OUT), TLS handshake, Client hello (1):
CAfile: /etc/ssl/cert.pem
CApath: none
(304) (IN), TLS handshake, Server hello (2):
(304) (IN), TLS handshake, Unknown (8):
(304) (IN), TLS handshake, Certificate (11):
(304) (IN), TLS handshake, CERT verify (15):
(304) (IN), TLS handshake, Finished (20):
(304) (OUT), TLS handshake, Finished (20):
SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
ALPN: server accepted http/1.1
Server certificate:
subject: C=US; ST=California; O=Apple Inc.; CN=app-site-association.cdn-apple.com
start date: Sep 25 13:58:08 2025 GMT
expire date: Mar 31 17:44:25 2026 GMT
subjectAltName: host "app-site-association.cdn-apple.com" matched cert's "app-site-association.cdn-apple.com"
issuer: CN=Apple Public Server RSA CA 11 - G1; O=Apple Inc.; ST=California; C=US
SSL certificate verify ok.
using HTTP/1.x
GET /a/v1/zfcs.bankts.cn HTTP/1.1
Host: app-site-association.cdn-apple.com
User-Agent: curl/8.7.1
Accept: /
Request completely sent off
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< Content-Length: 10
< Connection: keep-alive
< Server: nginx
< Date: Wed, 04 Feb 2026 02:26:00 GMT
< Expires: Wed, 04 Feb 2026 02:26:10 GMT
< Age: 24
< Apple-Failure-Details: {"cause":"context deadline exceeded (Client.Timeout exceeded while awaiting headers)"}
< Apple-Failure-Reason: SWCERR00301 Timeout
< Apple-From: https://zfcs.bankts.cn/.well-known/apple-app-site-association
< Apple-Try-Direct: true
< Vary: Accept-Encoding
< Via: https/1.1 jptyo12-3p-pst-003.ts.apple.com (acdn/3.16363), http/1.1 jptyo12-3p-pac-043.ts.apple.com (acdn/3.16363), https/1.1 jptyo12-3p-pfe-002.ts.apple.com (acdn/3.16363)
< X-Cache: MISS KS-CLOUD
< CDNUUID: 736dc646-57fb-43c9-aa0d-eedad3a534f8-1154605242
< x-link-via: yancmp83:443;xmmp02:443;fzct321:443;
< x-b2f-cs-cache: no-cache
< X-Cache-Status: MISS from KS-CLOUD-FZ-CT-321-35
< X-Cache-Status: MISS from KS-CLOUD-XM-MP-02-16
< X-Cache-Status: MISS from KS-CLOUD-YANC-MP-83-15
< X-KSC-Request-ID: c4a640c815640ee93c263a357ee919d6
< CDN-Server: KSFTF
< X-Cdn-Request-ID: c4a640c815640ee93c263a357ee919d6
<
Not Found
Connection #0 to host app-site-association.cdn-apple.com left intact
I'm developing a passkey manager using ASCredentialProviderViewController. I've set a custom AAGUID in the attestation object during registration:
let aaguid = Data([
0xec, 0x78, 0xfa, 0xe8, 0xb2, 0xe0, 0x56, 0x97,
0x8e, 0x94, 0x7c, 0x77, 0x28, 0xc3, 0x95, 0x00
])
However, when I test on webauthn.io, the relying party receives:
AAGUID: 00000000-0000-0000-0000-000000000000
Provider Name: "iCloud Keychain"
It appears that macOS overwrites the AAGUID to all zeros for third-party Credential Provider Extensions.
This makes it impossible for relying parties to distinguish between different passkey providers, which is one of the key purposes of AAGUID in the WebAuthn specification.
Is this expected behavior? Is there a way for third-party Credential Provider Extensions to use their own registered AAGUID?
Environment:
macOS 26.2
Xcode 26.2
Topic:
Privacy & Security
SubTopic:
General
Tags:
Extensions
macOS
Authentication Services
Passkeys in iCloud Keychain
I'm testing app transferring, before, I have migrate user from teamA to teamB, including subA->transferSub->subB process, now I'm transfer the app from teamB to teamC, after the transfer requested, I can't get transfer_id by /usermigrationinfo api, which response 400 invalid request.
the question is I can still get transfer sub by the auth/token api(grant_type: authorization_code) with teamB parameters(teamIdB/clientIdB/appSecretB/redirectUrlB/subB),but the value is same as first time transfer_id which get during teamA to teamB.
when use parameters above with target(teamIdC) to request /usermigrationinfo, invalid request was responsed.
im sure that all parameters is correct, dose it cause by teamB still in 60-days first transferring(sure already accepted)?
Hello,
I am currently process of migrating an app from Team A to Team B and attempting to generate transfer identifiers using the migration endpoint:
POST https://appleid.apple.com/auth/usermigrationinfo.
Content-Type: application/x-www-form-urlencoded
However, I am consistently receiving an
{
"error": "access_denied"
} response.
[Current Configuration]
Team A (Source):
Primary App ID: com.example.primary
Grouped App IDs:
com.example.service (Services ID for Web)
com.example.app (App ID for iOS - The one being transferred)
All identifiers are under the same App Group.
Team B (Destination):
New App ID and Key created.
[Steps Taken]
Created a Client Secret (JWT) using Team A's Key ID and Team ID.
The sub (subject) in the JWT is set to the Primary App ID of Team A.
Requesting with client_id (Primary App ID), client_secret (JWT), and user_token.
[Questions]
1. App Group Impact: Does the fact that the App being transferred is a Grouped App ID (not the Primary) affect the usermigrationinfo request? Should I use the Primary App ID or the specific Grouped App ID as the client_id?
2. Ungrouping Safety: If I need to ungroup the App ID from the Primary App ID to resolve this:
Will existing users still be able to sign in without issues?
Is there any risk of changing the sub (user identifier) that the app receives from Apple?
Will this cause any immediate service interruption for the live app?
Any insights on why access_denied occurs in this Primary-Grouped configuration would be greatly appreciated.
Feedback ticket ID: FB21797397
Summary
When using posix_spawn() with posix_spawnattr_set_uid_np() to spawn a child process with a different UID, the eslogger incorrectly reports a setuid event as an event originating from the parent process instead of the child process.
Steps to Reproduce
Create a binary that do the following:
Configure posix_spawnattr_t that set the process UIDs to some other user ID (I'll use 501 in this example).
Uses posix_spawn() to spawn a child process
Run eslogger with the event types setuid, fork, exec
Execute the binary as root process using sudo or from root owned shell
Terminate the launched eslogger
Observe the process field in the setuid event
Expected behavior
The eslogger will report events indicating a process launch and uid changes so the child process is set to 501. i.e.:
fork
setuid - Done by child process
exec
Actual behavior
The process field in the setuid event is reported as the parent process (that called posix_spawn) - indicating UID change to the parent process.
Attachments
I'm attaching source code for a small project with a 2 binaries:
I'll add the source code for the project at the end of the file + attach filtered eslogger JSONs
One that runs the descirbed posix_spawn flow
One that produces the exact same sequence of events by doing different operation and reaching a different process state:
Parent calls fork()
Parent process calls setuid(501)
Child process calls exec()
Why this is problematic
Both binaries in my attachment do different operations, achieving different process state (1 is parent with UID=0 and child with UID=501 while the other is parent UID=501 and child UID=0), but report the same sequence of events.
Code
#include <cstdio>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
// environ contains the current environment variables
extern char **environ;
extern "C" {
int posix_spawnattr_set_uid_np(posix_spawnattr_t *attr, uid_t uid);
int posix_spawnattr_set_gid_np(posix_spawnattr_t *attr, gid_t gid);
}
int main() {
pid_t pid;
int status;
posix_spawnattr_t attr;
// 1. Define the executable path and arguments
const char *path = "/bin/sleep";
char *const argv[] = {(char *)"sleep", (char *)"1", NULL};
// 2. Initialize spawn attributes
if ((status = posix_spawnattr_init(&attr)) != 0) {
fprintf(stderr, "posix_spawnattr_init: %s\n", strerror(status));
return EXIT_FAILURE;
}
// 3. Set the UID for the child process (e.g., UID 501)
// Note: Parent must be root to change to a different user
uid_t target_uid = 501;
if ((status = posix_spawnattr_set_uid_np(&attr, target_uid)) != 0) {
fprintf(stderr, "posix_spawnattr_set_uid_np: %s\n", strerror(status));
posix_spawnattr_destroy(&attr);
return EXIT_FAILURE;
}
// 4. Spawn the process
printf("Spawning /bin/sleep 1 as UID %d...\n", target_uid);
status = posix_spawn(&pid, path, NULL, &attr, argv, environ);
if (status == 0) {
printf("Successfully spawned child with PID: %d\n", pid);
// Wait for the child to finish (will take 63 seconds)
if (waitpid(pid, &status, 0) != -1) {
printf("Child process exited with status %d\n", WEXITSTATUS(status));
} else {
perror("waitpid");
}
} else {
fprintf(stderr, "posix_spawn: %s\n", strerror(status));
}
// 5. Clean up
posix_spawnattr_destroy(&attr);
return (status == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
// This program demonstrates fork + setuid + exec behavior for ES framework bug report
// 1. Parent forks
// 2. Parent does setuid(501)
// 3. Child waits with sleep syscall
// 4. Child performs exec
int main() {
printf("Parent PID: %d, UID: %d, EUID: %d\n", getpid(), getuid(), geteuid());
pid_t pid = fork();
if (pid < 0) {
// Fork failed
perror("fork");
return EXIT_FAILURE;
}
if (pid == 0) {
// Child process
printf("Child PID: %d, UID: %d, EUID: %d\n", getpid(), getuid(), geteuid());
// Child waits for a bit with sleep syscall
printf("Child sleeping for 2 seconds...\n");
sleep(2);
// Child performs exec
printf("Child executing child_exec...\n");
// Get the path to child_exec (same directory as this executable)
char *const argv[] = {(char *)"/bin/sleep", (char *)"2", NULL};
// Try to exec child_exec from current directory first
execv("/bin/sleep", argv);
// If exec fails
perror("execv");
return EXIT_FAILURE;
} else {
// Parent process
printf("Parent forked child with PID: %d\n", pid);
// Parent does setuid(501)
printf("Parent calling setuid(501)...\n");
if (setuid(501) != 0) {
perror("setuid");
// Continue anyway to observe behavior
}
printf("Parent after setuid - UID: %d, EUID: %d\n", getuid(), geteuid());
// Wait for child to finish
int status;
if (waitpid(pid, &status, 0) != -1) {
if (WIFEXITED(status)) {
printf("Child exited with status %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("Child killed by signal %d\n", WTERMSIG(status));
}
} else {
perror("waitpid");
}
}
return EXIT_SUCCESS;
}
posix_spawn.json
fork_exec.json
I have a user (myself, during development) who originally signed in with Apple successfully. I attempted to revoke
access via Settings > Apple ID > Sign-In & Security > Sign in with Apple, but the app appears stuck in the list and
cannot be fully removed. Now when attempting to sign in again, the identity token contains the correct sub but email is
undefined. According to Apple's documentation, "Apple provides the user's email address in the identity token on all
subsequent API responses." I've tried programmatically revoking via the /auth/revoke endpoint (received 200 OK), and
I've implemented the server-to-server notification endpoint to handle consent-revoked events, but subsequent sign-in
attempts still return no email. The same Apple ID works fine with other apps. Is there a way to fully reset the
credential state for a specific app, or is this a known issue with partially-revoked authorizations?
I have a user (myself, during development) who originally signed in with Apple successfully. I attempted to revoke
access via Settings > Apple ID > Sign-In & Security > Sign in with Apple, but the app appears stuck in the list and
cannot be fully removed. Now when attempting to sign in again, the identity token contains the correct sub but email is
undefined. According to Apple's documentation, "Apple provides the user's email address in the identity token on all
subsequent API responses." I've tried programmatically revoking via the /auth/revoke endpoint (received 200 OK), and
I've implemented the server-to-server notification endpoint to handle consent-revoked events, but subsequent sign-in
attempts still return no email. The same Apple ID works fine with other apps. Is there a way to fully reset the
credential state for a specific app, or is this a known issue with partially-revoked authorizations?
We are using SecItemCopyMatching from LocalAuthentication to access the private key to sign a challenge in our native iOS app twice in a few seconds from user interactions.
This was working as expected up until about a week ago where we started getting reports of it hanging on the biometrics screen (see screenshot below).
From our investigation we've found the following:
It impacts newer iPhones using iOS 26.1 and later. We have replicated on these devices:
iPhone 17 Pro max
iPhone 16 Pro
iPhone 15 Pro max
iPhone 15
Only reproducible if the app tries to access the private key twice in quick succession after granting access to face ID.
Looks like a race condition between the biometrics permission prompt and Keychain private key access
We were able to make it work by waiting 10 seconds between private key actions, but this is terrible UX.
We tried adding adding retries over the span of 10 seconds which fixed it on some devices, but not all.
We checked the release notes for iOS 26.1, but there is nothing related to this.
Screenshot:
Topic:
Privacy & Security
SubTopic:
General
Tags:
Face ID
Entitlements
Touch ID
Local Authentication
In these threads, it was clarified that Credential Provider Extensions must set both Backup Eligible (BE) and Backup State (BS) flags to 1 in authenticator data:
https://developer.apple.com/forums/thread/745605
https://developer.apple.com/forums/thread/787629
However, I'm developing a passkey manager that intentionally stores credentials only on the local device. My implementation uses:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly for keychain items
kSecAttrTokenIDSecureEnclave for private keys
No iCloud sync or backup
These credentials are, by definition, single-device credentials. According to the WebAuthn specification, they should be represented with BE=0, BS=0.
Currently, I'm forced to set BE=1, BS=1 to make the extension work, which misrepresents the actual backup status to relying parties. This is problematic because:
Servers using BE/BS flags for security policies will incorrectly classify these as synced passkeys
Users who specifically want device-bound credentials for higher security cannot get accurate flag representation
Request: Please allow Credential Provider Extensions to return credentials with BE=0, BS=0 for legitimate device-bound passkey implementations.
Environment: macOS 26.2 (25C56), Xcode 26.2 (17C52)
Topic:
Privacy & Security
SubTopic:
General
Tags:
Extensions
macOS
Authentication Services
Passkeys in iCloud Keychain