Hello, I'm currently integrating a feature in our app that allows customers to set up a passkey. Once set up, users are prompted to use their passkey at the sign-in page. For users without a registered passkey, we ensure that the passkey assertion request fails silently to maintain a smooth login experience, using preferImmediatelyAvailableCredentials effectively for this purpose.
However, we've noticed that when users are employing third-party password managers like 1Password or Bitwarden, they encounter a QR code fallback. Discussions with 1Password have revealed that iOS does not currently extend preferImmediatelyAvailableCredentials to these services.
I would appreciate any advice on how to harmonize the behavior between iCloud Keychain and third-party password providers to ensure a consistent user experience.
Passkeys in iCloud Keychain
RSS for tagUse public-key-based credentials using the WebAuthn standard that are synced with iCloud Keychain.
Posts under Passkeys in iCloud Keychain tag
90 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
We are implementing a 3rd party Passkeys Manager app for ios. In the ios app in the CredentialProviderViewController I've implemented:
func prepareCredentialList(
for serviceIdentifiers: [ASCredentialServiceIdentifier]
)
func provideCredentialWithoutUserInteraction(
for credentialRequest: ASCredentialRequest
)
func prepareInterfaceToProvideCredential(
for credentialRequest: ASCredentialRequest
)
func prepareInterface(
forPasskeyRegistration registrationRequest: ASCredentialRequest
)
When testing on webpages like webauthn.io and webauthn.me , our app shows up as one of the options for creating a passkey.
We are getting the calls in prepareInterface() and handling it as advised here
https://developer.apple.com/documentation/authenticationservices/ascredentialproviderviewcontroller/4172626-prepareinterface/
However the registration is failing. I understand that in this function, we need to create a passkey using a crypto library and then call completeRegistrationRequest(using:completionHandler:)
The documentation on this is scant so it is hard to debug for this reason.
Need help fixing this issue. What could we be missing?
Is there any sample code for overriding these functions?
Any recommendations on the crypto library for generating passkeys
When the passkeys have been generated, how do we pass it back to the system?
Thank you,
Jaydip.
We’ve observed a couple of concerning alterations in the passkey registration and authentication behaviour in iOS 17.4.1:
During passkey registration, “excludeCredentials” property is ignored. Existing passkey is silently overwritten and no error is reported from navigator.credentials.create (both, Safari and Chrome).
However, according to W3 spec when “excludeCredentials” is present in the PublicKeyCredentialCreationOptions -
“The client is requested to return an error if the new credential would be created on an authenticator that also contains one of the credentials enumerated in this parameter.”
PublicKeyCredentialCreationOptions we use:
"credCreateOptions": {
"rp": {
"name": "RP name"
},
"user": {
"name": "username",
"id": "abcd"
},
"challenge": "56elsKE5pKgEECg-fJpLl3gF33ACRSVBl00Mn03JAIk",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"excludeCredentials": [
{
"type": "public-key",
"id": “abcd”
}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"userVerification": "required"
},
"hints": [
"client-device"
],
"attestation": "direct",
"extensions": null
}
}
This behaviour is different from what is observed on iOS 17.3.1 where the registration call to navigator.credentials.create with the same options produces the following error: “At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator”
During passkey login flow, iOS 17.4.1 ignores
"hints": [ "client-device" ]
According to https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create#client-device, hints specify what authentication UI the user-agent should provide for the user. “client-device” requests the user authenticates using their own device, such as a phone.
Here’s our PublicKeyCredentialRequestOption:
"credRequestOptions": {
"challenge": "xk_wd1BaVue7mOZ-UM_KVj6Z4AmGxf12_7H1Gilq01I",
"timeout": 300000,
"allowCredentials": [
{
"type": "public-key",
"id": “abcd”,
"transports": [
"internal"
]
}
],
"userVerification": "required",
"hints": [
"client-device"
],
"extensions": null
}
}
However, iOS 17.4.1 gives user an option to sign in with another device .
On iOS 17.3.1, the request to navigator.credentials.get with the same same PublicKeyCredentialRequestOption presents the UI screen without the “Sign In with Another Device” option.
Is that a bug on iOS 17.4.1 or intended behaviour? This does not follow the official specs and different from the other platforms like Android.
Is there any documentation around this change on iOS 17.4.1? That would be helpful.
I have registered and created passkey with credentials.create function in apple device with software 17.4.1 in Safari browser. When I clean the cache in safari and try to log in, it force me to register again and after that I had two passkeys on my device. It should be like this ? Why Safari is related to Passkeys ?
Hello,
we are planning to migrate an app that uses iCloud documents to store some documents that the user can upload/download and modify.
In the official overview of App Transfer here, there are information related to CloudKit Containers and KVS but nothing related to iCloud Documents.
The content of iCloud Documents is accessed using FileManager.default.url(forUbiquityContainerIdentifier: containerID) where the containerID has the form iCloud.com.things.things, even if in the official documentation it is written that the identifier must be prefixed with the Team ID.
With this ID the storage works properly and we never experienced problems.
Do you know if migrating an app with this functionality can be problematic? Do you know if the Team ID is somehow used even if not explicitly passed in the call to the function?
Thank you and have a nice day!
Hi,
Does anyone have a good reference list of all capabilities that are unavailable in an iMessage App vs a regular App? In particular, I'm trying to figure out whether I can 1) request a signature via passkey, and 2) send a POST request to an HTTP endpoint.
Asking because I haven't been able to find code references or live apps for either. Any pointers would be greatly appreciated!
Regards,
Lyron
In the new macOS and iOS updates (14.4 and 17.4 respectively), something has changed in regards to passkey creation:
Any passkey created from Safari doesn't have any transports + the authenticatorAttachment is always set to platform, irrespective of whether a cross-platform authentication method is utilized, such as a hardware security key.
All passkeys saved in iCloud Keychain created from any browser have an authenticatorAttachment always set to platform + empty authenticator transports.
authenticatorAttachment always set to platform
According to the WebAuthn specification (Section 5.4.5), the authenticatorAttachment descriptor plays a crucial role in guiding the client (browser or platform) to create or use an authenticator of a specific type. The options are platform for a built-in authenticator or cross-platform for a roaming authenticator.
Some relying parties mandate a cross-platform method for the first passkey or as second authentication factor. This is to ensure users do not find themselves locked out when they try to sign in from a device that doesn't have access to the non-roaming webauthn credential. Unfortunately, the current implementation in Sonoma 14.4 forces the authenticatorAttachment to platform, thus preventing the creation of passkeys that comply with such policies on websites.
For comparison, browsers like Chrome correctly return a cross-platform authenticatorAttachment when a hardware security key is used, and the same used to happen on previous macOS and iOS versions from Safari.
Authenticator transports missing
The absence of transport data (WebAuthn Section 5.8.4) for all passkeys created via Safari and iCloud Keychain passkeys created from all browsers further complicates the scenario. The transport hint is crucial for informing relying parties about the preferred transport method for the authenticator, be it USB, NFC, BLE, HYBRID or internal. This omission could lead to inefficiencies and a diminished user experience, as the system cannot optimize the authentication process based on the authenticators available to the user.
These issues jeopardize the utility and adoption of passkeys across various platforms and browsers, a primary goal of WebAuthn and FIDO2 for widespread secure authentication practices. What is the rationale behind this choice and is there any workaround to be considered?
Thanks for all the help and clarification!
Safari is allowing users to create multiple passkeys despite setting the excludeCredentials in PublicKeyCredentialCreationOptions (https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create#excludecredentials). I also included appidExclude(https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions#appidexclude) but it was still allowing me to create multiple passkeys on the same iCloud account.
This does not happen in other browsers.
Can anyone point me to any documentation regarding this? Much appreciated
https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions#appidexclude
https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create#excludecredentials
It seems cross-device passkey authentication isn't supported by the Simulator, is that right? I can't tell if it's the simulator not supporting it, or my app/server failing.
My app, running in the Simulator, presents a QR code during sign-in flow. I scan that with my phone, and it just spins "connecting…".
Can anyone confirm that the Simulator just doesn't support this?
What are my options for testing this flow? I guess I need another device not sign in to my AppleID.
I'm working my way through adding passkey support to my app. At app launch, I'd like to test to see if the user has already created a passkey for the service, and if not, immediately present the account creation UI.
Is there an API call I can make to see if the user already has a credential? From the examples I’ve found, it seems I should just try to sign in, and I’ll get an error callback if there are no stored credentials. Is that right?
The below code used to compile for iOS 16.0 and above when using Xcode 15.2. Now it seems that ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest.excludeCredentials is only available on iOS 17.4 an above in Xcode 15.3? Is there any reason that's the case?
let request = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: id).createCredentialRegistrationRequest(challenge: challengeData, name: name, userID: userIDData)
// ERROR: 'excludedCredentials' is only available in iOS 17.4 or newer
request.excludedCredentials = registrationOptions.excludeCredentials
How do we find such info as attestationPreference through the prepareInterface() API? Is there a way to access ASAuthorizationPublicKeyCredentialRegistrationRequest? I don't seem to see how we can achieve this through ASPasskeyCredentialRequest in prepareInterface(forPasskeyRegistration:) for iOS.
A more broader question is: do we even have access to the WebAuthn extensions in third-party passkey manager?
Thanks,
Joshua
I am integrating Apple's iOS implementation of WebAuthN using the ASAuthorization APIS provided by Apple. I am integrating with the Django py_webauthn host backend. I have the entire registration & account creations process working. And I almost have the account authentication process working. But there seems to be an issue between the version of authenticationData generated by iOS subsequent to the client (iPhone) authentication process and what py_webauthn is expecting. It results in a consistent
"Leftover bytes detected while parsing authentication data" error
on the host. According to the documentation, this error is generated when all the authenticatorData has been parsed but there are leftover bytes in the data.
I believe if I get past this issue everything will be working.
I could use some help here and provide code/logs below. But in the hopes that someone from the Authentication development team at Apple is watching for these posts, I have a related question.
Can we assume that the generated private key AND sign_count data is shared across an iOS user's devices through KeyChain cloud? Do we have to advise our users that they should enable this??
Here is code (OBJ C) and logs, in the hopes that someone can advise... and hoping that the code can help others trying to implement this capability.
Thanks.....
iPhone Client
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization
//Check to make sure that we are authenticating user after receiving authorization challenge from host
NSLog(@"%s retrieve public/private key",__FUNCTION__);
NSDictionary *pubKeyDict = [NSDictionary dictionary];
ASAuthorizationPlatformPublicKeyCredentialAssertion *appleCred = (ASAuthorizationPlatformPublicKeyCredentialAssertion *)authorization.credential;
NSDictionary *clientJSON = [NSJSONSerialization JSONObjectWithData:appleCred.rawClientDataJSON options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%s clientJSON = %@",__FUNCTION__,clientJSON);
NSString *clientJSONString = [ServerUtilities encodeBase64URLData:appleCred.rawClientDataJSON];
NSString *authenticatorString = [ServerUtilities encodeBase64URLData:appleCred.rawAuthenticatorData];
NSString *signatureString = [ServerUtilities encodeBase64URLData:appleCred.signature];
NSDictionary *respDict = [NSDictionary dictionaryWithObjectsAndKeys clientJSONString, @"clientDataJSON",
authenticatorString, @"signature",
signatureString, @"authenticatorData", nil];
NSLog(@"%s respDict - %@",__FUNCTION__,respDict);
NSString *credIDString = [ServerUtilities encodeBase64URLData:appleCred.credentialID];
NSDictionary *regDict = [NSDictionary dictionaryWithObjectsAndKeys: credIDString, @"id",
respDict, @"response",
@"public-key",@"type", nil];
pubKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: credentialEmail, kEMAIL,
regDict, @"auth_cred", nil];
NSLog(@"%s pubKeyDict - %@",__FUNCTION__,pubKeyDict);
//Post JSON serialized version of pubKeyDict to host
Xcode log
pubKeyDict - {
"auth_cred" = {
id = "JNMBbZF_PQM7O64RY_MXQqhAyKk";
response = {
authenticatorData = "MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l";
clientDataJSON = eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9;
signature = "WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA";
};
type = "public-key";
};
email = "my@email.com";
}
Django Python Host
auth_cred_json - {'id': 'JNMBbZF_PQM7O64RY_MXQqhAyKk', 'response': {'clientDataJSON': 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9', 'signature': 'WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA', 'authenticatorData': 'MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l'}, 'type': 'public-key', 'rawId': 'JNMBbZF_PQM7O64RY_MXQqhAyKk'}
AuthorizationCredential generated by 'parse_authentication_credential_json'
id='JNMBbZF_PQM7O64RY_MXQqhAyKk'
raw_id=b'$\\xd3\\x01m\\x91\\x7f=\\x03;;\\xae\\x11c\\xf3\\x17B\\xa8@\\xc8\\xa9' response=AuthenticatorAssertionResponse(
client_data_json=b'{
"type":"webauthn.get",
"challenge":"vHXq7JLm9ggMSPbzsie8EYBlUmzWcwYyo8PgOAJ2TuoEAleAnD44aMT87DowbvZeCq-lSt-n51dQbiW815XYFQ",
"origin":"https://anchor-away.com"
}',
authenticator_data=b"0F\\x02!\\x00\\xe0TR\\r\\x8a\\x1c\\xbd\\x9c\\xfb\\xaeF\\xb6V\\x90\\xb3\\xda\\x84G\\xcb\\xcbxn\\t_\\x7f\\xb1\\xe3\\xae3\\x98\\x12\\x96\\x02!\\x00\\xff\\xfd\\xf4EP\\x8dxr'-\\xfbq\\xc4qA\\xe9\\xd2\\xc6\\xde\\xe8<\\xda\\xd0\\xe1X\\xd5\\r%\\xb9\\x12?%",
signature=b'X\\x9d\\xed$\\xd3\\x18|\\xd4\\x93\\xf7\\xdc~\\x10\\x07M\\x0e\\xb9\\xecP\\xe0\\xb1\\xa8\\\\\\xef\\x0e\\x05\\xc2\\xc97\\x13\\x83\\xab\\x1d\\x00\\x00\\x00\\x00',
user_handle=None
)
We have an App that displays a WKWebWiew. The page being loaded in the WebView loads a snippet with a button that when clicked, initiates WebAuthN Flow. The App throws he following
[WebAuthn] Request cancelled after error: The operation couldn’t be completed. Application with identifier 123ABCDEF4.com.exmaple.app.staging is not associated with domain example.co.za.
In the above quote, 123ABCDEF4 would represent the TeamID, com.exmaple.app.staging is the bundle identifier and example.co.za is the main/root domain while the WKWebView actually loads a URL at staging.example.co.za.
in Xcode, the App's Associated Domains contains the following
webcredentials:staging.example.co.za
The AASA file hosted at https://staging.example.co.za/.well-known/apple-app-site-association returns the following
{
"applinks": {
"details": [
{
"appIDs": [
"123ABCDEF4.com.example.app",
"123ABCDEF4.com.example.app.staging"
],
"paths": [
"*"
]
}
]
},
"webcredentials": {
"apps": [
"123ABCDEF4.com.example.app",
"123ABCDEF4.com.example.app.staging"
]
},
"appclips": {
"apps": []
}
}
May you kindly advise what we may be doing wrong? The message being thrown refers to the fact that the Staging app is not associated with the root/main domain. If it's any consolation, we've updated both example.co.za and staging.exmaple.co.za to return the exact same AASA file where they both registers both Prod & Staging AppIds
I'm trying to implement an AutoFill extension for passkeys.
I need the extension to communicate with the containing app even when the containing app is terminated.
Is there any (and I mean ANY) way to do it?
P.S. I already tried the MMWormhole package and also tried to write to a file from the extension using NSFileCoordinator and observe this file in the containing app using NSFilePresenter.
Both only work when the containing app is already running.
In the context of a passkey authentication where a non apple desktop system relies on the ios device as an authenticator, je ios device requires the user to scan the qrcode of the desktop device every time an authentication on is required.
in the same context, and Android device will allow the user to « remember » the established link to allow subsequent authentications to reuse the link without having to scan the qrcode each time.
The android device behaviour is specified by FIDO at https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html
So my question is: when will ios support CTAP 2.2 hybrid transport state assisted transaction?
The absence of it is a major obstacle to the general adoption of passkeys among ios users.
I am creating an app using flutter. When the application displays the passkey authentication dialog box, after the face id authentication is complete (the authentication dialog box has not yet closed), I press the power button to lock the screen. After that, when I unlock the screen, my application is still displayed but it is in inactive state. This means the interface is still visible but the user cannot interact.
Hi folks, I have a couple questions relating to the user gesture requirement on iOS, specifically regarding WebKit.
From my testing, it looks like only one webauthn invocation without user gesture is allowed even if there's intervening user gestures, e.g. navigating to another page, clicking buttons, etc. I have to close Safari and reopen in order to reset the count. Is this expected behavior? It seems like it was originally supposed to be one per user navigation [1].
I see the user gesture requirement was removed recently [2]. I agree with the decision, but am curious what was the context behind this move as it seems like a reversal of https://webkit.org/blog/11312/meet-face-id-and-touch-id-for-the-web/.
[1] https://bugs.webkit.org/show_bug.cgi?id=220897
[2] https://bugs.webkit.org/show_bug.cgi?id=264444
I understand that the passkey is stored on iCloud Keychain. That means you need an Apple ID account. Can I use passkey without Apple ID account? I checked on the simulator and even though I don't need to log in to my Apple ID account, I can still use the passkey. Please help me. Thank you.
I have a demo AppClip which does Passkey registration and assertion in a wizard style page collection. Very often, the assertion would fail with the error "Couldn't communicate with a helper application". Retrying it few times will eventually work.
Full error:
Connection to agent service interrupted with error: Error Domain=NSCocoaErrorDomain Code=4097 "Couldn’t communicate with a helper application."
I don't know exactly what's causing this and how to prevent it? It only seems to happen during assertion, not registration.