This post is an extension to Importing Cryptographic Keys that covers one specific common case: importing a PEM-based RSA private key and its certificate to form a digital identity.
If you have questions or comments, start a new thread in Privacy & Security > General. Tag your thread with Security so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Importing a PEM-based RSA Private Key and its Certificate
I regularly see folks struggle to import an RSA private key and its corresponding certificate. Importing Cryptographic Keys outlines various options for importing keys, but in this post I want to cover one specific case, namely, a PEM-based RSA private key and its corresponding certificate. Together these form a digital identity, represented as a SecIdentity object.
IMPORTANT If you can repackage your digital identity as a PKCS#12, please do. It’s easy to import that using SecPKCS12Import. If you can switch to an elliptic curve (EC) private key, please do. It’s generally better and Apple CryptoKit has direct support for importing an EC PEM.
Assuming that’s not the case, let’s explore how to import a PEM-base RSA private key and its corresponding certificate to form a digital identity.
Note The code below was built with Xcode 16.2 and tested on the iOS 18.2 simulator. It uses the helper routines from Calling Security Framework from Swift.
This code assumes the data protection keychain. If you’re targeting macOS, add kSecUseDataProtectionKeychain to all the keychain calls. See TN3137 On Mac keychain APIs and implementations for more background to that.
Unwrap the PEM
To start, you need to get the data out of the PEM:
/// Extracts the data from a PEM.
///
/// As PEM files can contain a large range of data types, you must supply the
/// expected prefix and suffix strings. For example, for a certificate these
/// are `"-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
///
/// - important: This assumes the simplest possible PEM format. It does not
/// handle metadata at the top of the PEM or PEMs with multiple items in them.
func dataFromPEM(_ pem: String, _ expectedPrefix: String, _ expectedSuffix: String) -> Data? {
let lines = pem.split(separator: "\n")
guard
let first = lines.first,
first == expectedPrefix,
let last = lines.last,
last == expectedSuffix
else { return nil }
let base64 = lines.dropFirst().dropLast().joined()
guard let data = Data(base64Encoded: base64) else { return nil }
return data
}
IMPORTANT Read the doc comment to learn about some important limitations with this code.
Import a Certificate
When adding a digital identity to the keychain, it’s best to import the certificate and the key separately and then add them to the keychain. That makes it easier to track down problems you encounter.
To import a PEM-based certificate, extract the data from the PEM and call SecCertificateCreateWithData:
/// Import a certificate in PEM format.
///
/// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations.
func importCertificatePEM(_ pem: String) throws -> SecCertificate {
guard
let data = dataFromPEM(pem, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----"),
let cert = SecCertificateCreateWithData(nil, data as NSData)
else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil) }
return cert
}
Here’s an example that shows this in action:
let benjyCertificatePEM = """
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIBCzANBgkqhkiG9w0BAQsFADAfMRAwDgYDVQQDDAdNb3Vz
ZUNBMQswCQYDVQQGEwJHQjAeFw0xOTA5MzAxNDI0NDFaFw0yOTA5MjcxNDI0NDFa
MB0xDjAMBgNVBAMMBUJlbmp5MQswCQYDVQQGEwJHQjCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAOQe5ai68FQhTVIgpsDK+UOPIrgKzqJcW+wwLnJRp6GV
V9EmifJq7wjrXeqmP1XgcNtu7cVhDx+/ONKl/8hscak54HTQrgwE6mK628RThld9
BmZoOjaWWCkoU5bH7ZIYgrKF1tAO5uTAmVJB9v7DQQvKERwjQ10ZbFOW6v8j2gDL
esZQbFIC7f/viDXLsPq8dUZuyyb9BXrpEJpXpFDi/wzCV3C1wmtOUrU27xz4gBzi
3o9O6U4QmaF91xxaTk0Ot+/RLI70mR7TYa+u6q7UW/KK9q1+8LeTVs1x24VA5csx
HCAQf+xvMoKlocmUxCDBYkTFkmtyhmGRN52XucHgu0kCAwEAAaMqMCgwDgYDVR0P
AQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUA
A4IBAQAyrArH7+IyHTyEOrv/kZr3s3h4HWczSVeiO9qWD03/fVew84J524DiSBK4
mtAy3V/hqXrzrQEbsfyT7ZhQ6EqB/W0flpVYbku10cSVgoeSfjgBJLqgJRZKFonv
OQPjTf9HEDo5A1bQdnUF1y6SwdFaY16lH9mZ5B8AI57mduSg90c6Ao1GvtbAciNk
W8y4OTQp4drh18hpHegrgTIbuoWwgy8V4MX6W39XhkCUNhrQUUJk3mEfbC/yqfIG
YNds0NRI3QCTJCUbuXvDrLEn4iqRfbzq5cbulQBxBCUtLZFFjKE4M42fJh6D6oRR
yZSx4Ac3c+xYqTCjf0UdcUGxaxF/
-----END CERTIFICATE-----
"""
print(try? importCertificatePEM(benjyCertificatePEM))
If you run this it prints:
Optional(<cert(0x11e304c10) s: Benjy i: MouseCA>)
Import a Private Key
To import a PEM-base RSA private key, extract the data from the PEM and call SecKeyCreateWithData:
/// Import an 2048-bit RSA private key in PEM format.
///
/// Don’t use this code if:
///
/// * If you can switch to an EC key. EC keys are generally better and, for
/// this specific case, there’s support for importing them in Apple CryptoKit.
///
/// * You can switch to using a PKCS#12. In that case, use the system’s
/// `SecPKCS12Import` routine instead.
///
/// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations.
func importRSA2048PrivateKeyPEM(_ pem: String) throws -> SecKey {
// Most private key PEMs are in PKCS#8 format. There’s no way to import
// that directly. Instead you need to strip the header to get to the
// `RSAPrivateKey` data structure encapsulated within the PKCS#8. Doing that
// in the general case is hard. In the specific case of an 2048-bit RSA
// key, the following hack works.
let rsaPrefix: [UInt8] = [
0x30, 0x82, 0x04, 0xBE, 0x02, 0x01, 0x00, 0x30,
0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
0x04, 0xA8,
]
guard
let pkcs8 = dataFromPEM(pem, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----"),
pkcs8.starts(with: rsaPrefix)
else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil) }
let rsaPrivateKey = pkcs8.dropFirst(rsaPrefix.count)
return try secCall { SecKeyCreateWithData(rsaPrivateKey as NSData, [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, $0) }
}
IMPORTANT This code only works with 2048-bit RSA private keys. The comments explain more about that limitation.
Here’s an example that shows this in action:
let benjyPrivateKeyPEM = """
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkHuWouvBUIU1S
IKbAyvlDjyK4Cs6iXFvsMC5yUaehlVfRJonyau8I613qpj9V4HDbbu3FYQ8fvzjS
pf/IbHGpOeB00K4MBOpiutvEU4ZXfQZmaDo2llgpKFOWx+2SGIKyhdbQDubkwJlS
Qfb+w0ELyhEcI0NdGWxTlur/I9oAy3rGUGxSAu3/74g1y7D6vHVGbssm/QV66RCa
V6RQ4v8MwldwtcJrTlK1Nu8c+IAc4t6PTulOEJmhfdccWk5NDrfv0SyO9Jke02Gv
ruqu1FvyivatfvC3k1bNcduFQOXLMRwgEH/sbzKCpaHJlMQgwWJExZJrcoZhkTed
l7nB4LtJAgMBAAECggEBAKOPF6ED776SZgrliEog/dmXrhABB6jXybytyw+CRkuP
dXhrRmr+isZ9Y0gTzMN4+dILVgW4EozzoP0/sgZ04oWwDqQS30eU2qzRRzMbo+3k
oYsZXeu3nhxcYppwXIDsfAEd/ygMFzaadRPKYhrFykR2rA/dpLYCvW2tfm5SuULp
RxnKykFlVi8yVT64AovVm0XGOy/QTO5BBbUdftvZY9QCjGn/IEL8QFEz0rxZsb2L
s0HgVMUcB1My38RksZQRKLMWCtqLqWnez3oCnPka+dxFQj5RU//vNtRoVh1ExbmW
txHz48v00AKQvaudC4ujIspZlY8+UPdYQT0TNjhsfoUCgYEA+7yEvyCgRtYwUNm6
jHTg67LoSldHwENOry63qGZp3rCkWBkPXle7ulgRtuw+e11g4MoMMAgkIGyIGB/Z
6YvnQGmJCTMw+HHIyw3k/OvL1iz4DM+QlxDuD79Zu2j2UIL4maDG0ZDskiJujVAf
sFOy4r36TvYedmd7qgh9pgpsFl8CgYEA5/v8PZDs2I1wSDGllGfTr6aeQcxvw98I
p8l/8EV/lYpdKQMFndeFZI+dnJCcTeBbeXMmPNTAdL5gOTwDReXamIAdr93k7/x6
iKMHzBrpQZUMEhepSd8zdR1+vLvyszvUU6lvNXcfjwbu7gJQkwbA6kSoXRN+C1Cv
i5/w66t0f1cCgYBt02FWwTUrsmaB33uzq4o1SmhthoaXKsY5R3h4z7WAojAQ/13l
GwGb2rBfzdG0oJiTeZK3odWhD7iQTdUUPyU0xNY0XVEQExQ3AmjUr0rOte/CJww9
2/UAicrsKG7N0VYEMFCNPVz4pGz22e35T4rLwXZi3J2NqrgZBntK5WEioQKBgEyx
L4ii+sn0qGQVlankUUVGjhcuoNxeRZxCrzsdnrovTfEbAKZX88908yQpYqMUQul5
ufBuXVm6/lCtmF9pR8UWxbm4X9E+5Lt7Oj6tvuNhhOYOUHcNhRN4tsdqUygR5XXr
E8rXIOXF4wNoXH7ewrQwEoECyq6u8/ny3FDtE8xtAoGBALNFxRGikbQMXhUXj7FA
lLwWlNydCxCc7/YwlHfmekDaJRv59+z7SWAR15azhbjqS9oXWJUQ9uvpKF75opE7
MT0GzblkKAYu/3uhTENCjQg+9RFfu5w37E5RTWHD2hANV0YqXUlmH3d+f5uO0xN7
7bpqwYuYzSv1hBfU/yprDco6
-----END PRIVATE KEY-----
"""
print(try? importRSA2048PrivateKeyPEM(benjyPrivateKeyPEM))
If you run this it prints:
Optional(<SecKeyRef algorithm id: 1, key type: RSAPrivateKey, version: 4, 2048 bits (block size: 256), addr: 0x600000c5ce50>)
Form a Digital Identity
There are two common ways to form a digital identity:
SecPKCSImport
SecItemCopyMatching
SecPKCSImport is the most flexible because it gives you an in-memory digital identity. You can then choose to add it to the keychain or not. However, it requires a PKCS#12 as input. If you’re starting out with separate private key and certificate PEMs, you have to use SecItemCopyMatching.
Note macOS also has SecIdentityCreateWithCertificate, but it has some seriously limitations. First, it’s only available on macOS. Second, it requires the key to be in the keychain. If you’re going to add the key to the keychain anyway, you might as well use SecItemCopyMatching.
To form a digital identity from a separate private key and certificate:
Add the certificate to the keychain.
Add the private key to the keychain.
Call SecItemCopyMatching to get back a digital identity.
Here’s an example of that in action:
/// Imports a digital identity composed of separate certificate and private key PEMs.
///
/// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations.
/// See ``importRSA2048PrivateKeyPEM(_:)`` for alternative strategies that are
/// much easier to deploy.
func addRSA2048DigitalIdentityPEMToKeychain(certificate: String, privateKey: String) throws -> SecIdentity {
// First import the certificate and private key. This has the advantage in
// that it triggers an early failure if the data is in the wrong format.
let certificate = try importCertificatePEM(certificate)
let privateKey = try importRSA2048PrivateKeyPEM(privateKey)
// Check that the private key matches the public key in the certificate. If
// not, someone has given you bogus credentials.
let certificatePublicKey = try secCall { SecCertificateCopyKey(certificate) }
let publicKey = try secCall { SecKeyCopyPublicKey(privateKey) }
guard CFEqual(certificatePublicKey, publicKey) else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecPublicKeyInconsistent))
}
// Add the certificate first. If that fails — and the most likely error is
// `errSecDuplicateItem` — we want to stop immediately.
try secCall { SecItemAdd([
kSecValueRef: certificate,
] as NSDictionary, nil) }
// The add the private key.
do {
try secCall { SecItemAdd([
kSecValueRef: privateKey,
] as NSDictionary, nil) }
} catch let error as NSError {
// We ignore a `errSecDuplicateItem` error when adding the key. It’s
// possible to have multiple digital identities that share the same key,
// so if you try to add the key and it’s already in the keychain then
// that’s fine.
guard error.domain == NSOSStatusErrorDomain, error.code == errSecDuplicateItem else {
throw error
}
}
// Finally, search for the resulting identity.
//
// I originally tried querying for the identity based on the certificate’s
// attributes — the ones that contribute to uniqueness, namely
// `kSecAttrCertificateType`, `kSecAttrIssuer`, and `kSecAttrSerialNumber` —
// but that failed for reasons I don't fully understand (r. 144152660). So
// now I get all digital identities and find the one with our certificate.
let identities = try secCall { SecItemCopyMatching([
kSecClass: kSecClassIdentity,
kSecMatchLimit: kSecMatchLimitAll,
kSecReturnRef: true,
] as NSDictionary, $0) } as! [SecIdentity]
let identityQ = try identities.first { i in
try secCall { SecIdentityCopyCertificate(i, $0) } == certificate
}
return try secCall(Int(errSecItemNotFound)) { identityQ }
}
IMPORTANT This code is quite subtle. Read the comments for an explanation as to why it works the way it does.
Further reading
For more information about the APIs and techniques used above, see:
Importing Cryptographic Keys
On Cryptographic Keys Formats
SecItem: Fundamentals
SecItem: Pitfalls and Best Practices
Calling Security Framework from Swift
TN3137 On Mac keychain APIs and implementations
Finally, for links to documentation and other resources, see Security Resources.
Revision History
2025-02-13 Added code to check for mismatched private key and certificate.
2025-02-04 First posted.
General
RSS for tagPrioritize 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
Activity
I have configured DKIM and amazon's default spf. but can't get emails using Amazon Send, do I have to configure a custom domain name here for this to work, I'd like to get a definitive conclusion!
Hey there, I used our team's account to configure sign in with Apple, the mode is pop up, my clientId scope redirectUrl state are both correct. I got Failed to verify your identity. Try again., actually my account is valid because I can login to my mac and every apple website. I have tried many apple accounts and still got this error. That was so weird, I didn't find a solution online. Pls help me thanks.
I've come across strange behavior with the userID property on the returned credential from a passkey attestation.
When performing a cross-device passkey assertion between iOS and Android by scanning the generated QR code on my iPhone with an Android device the returned credential object contains an empty userID.
This does not happen when performing an on device or cross-device assertion using two iPhones.
Is this expected behavior, or is there something I'm missing here? I couldn't find any more information on this in the documentation.
iOS Version: 26.0.1, Android Version: 13
I am running a service available on both an app and a web platform with "Sign In with Apple."
Should I store the tokens separately, or should I overwrite them in a single storage location?
When a user requests to sign out, should I revoke both the app and web tokens, or will revoking the app token automatically cover the web token as well?
I added a feature to my app that retrieves only app settings (no personal data) from my API hosted on Cloudflare Workers. The app does not send, collect, track, or share any user data, and I do not store or process any personal information.
Technical details such as IP address, user agent, and device information may be automatically transmitted as part of the internet protocol when the request is made, but my app does not log or use them. Cloudflare may collect this information.
Question: Does this count as “data collection” for App Store Connect purposes, or can I select “No Data Collected”?
Without developer mode, I was able to get Password AutoFill to work in my SwiftUI app with my local Vapor server using ngrok and adding the Associated Domains capability with the value webcredentials:....ngrok-free.app and the respective apple-app-site-association file on my local server in /.well-known/. (works on device, but not in the simulator).
However, if I use the developer mode (webcredentials:....ngrok-free.app?mode=developer) it only works halfway when running from Xcode: I get asked to save the password, but the saved passwords are not picked up, when I try to login again. Neither on device, nor in the simulator. If I remove the ?mode=developer it seems to work as expected.
Is this by design, or am I missing something?
var body: some View {
...
Section(header: Text("Email")) {
TextField("Email", text: $viewModel.credentials.username)
.textContentType(.username)
.autocapitalization(.none)
.keyboardType(.emailAddress)
}
Section(header: Text("Passwort")) {
SecureField("Passwort", text: $viewModel.credentials.password)
.textContentType(.password)
}
...
}
Topic:
Privacy & Security
SubTopic:
General
Tags:
SwiftUI
Universal Links
Authentication Services
Autofill
News link: https://developer.apple.com/news/?id=12m75xbj
If your app offers Sign in with Apple, you’ll need to use the Sign in with Apple REST API to revoke user tokens when deleting an account.
I'm not good English. I'm confused about the above sentence
Do I have to use REST API unconditionally or can I just delete to the account data?
Topic:
Privacy & Security
SubTopic:
General
Tags:
App Review
Sign in with Apple REST API
Sign in with Apple
completeRequestWithTextToInsert is used to return text into an arbitrary textfield via the context menu AutoFill/Passwords from a 3rd party password manager (or presumably the Passwords App) in iOS 18.
While testing this feature in the debugger, it would often fail on the first invocation. It also appears to happen intermittently in the released app extension. Subsequent testing using the Passwords App shows it too may fail to return a value.
I have confirmed this behaviour is repeatable with the Passwords App on an iPhone running iOS 18.3.1
Reboot the iPhone.
Show the App Library, and right click Autofill.
Select Passwords
Select Passwords (App)
Select a password.
Nothing will be inserted (intermittently).
Feedback assistant report: FB16788563
I am currently unable to enable passkey in my app so I am having to tell my users to skip the prompts for using passkey. We have noticed that after a few times of this the OS will stop asking the user to register their passkey. The question is, how long does this last before the OS asks you to use passkey again? Is it permanent until you re-install the app? Just looking for a time frame if anyone knows.
Topic:
Privacy & Security
SubTopic:
General
Tags:
Passkeys in iCloud Keychain
Authentication Services
Hello everyone,
I’ve been working on ways to implement stricter accountability systems for personal use, especially to prevent access to NSFW content in apps like Reddit and Twitter. The main challenge is that iOS sandboxing and privacy policies block apps from monitoring or interacting with other apps on the system.
While Apple’s focus on privacy is important, there’s a clear need for an opt-in exception for accountability tools. These tools could be allowed enhanced permissions under stricter oversight to help users maintain accountability and integrity without compromising safety.
Here are a few ideas I’ve been thinking about:
1. Vetted Apps with Enhanced Permissions: Allow trusted applications to bypass sandbox restrictions with user consent and close monitoring by Apple.
2. Improved Parental Controls: Add options to send notifications to moderators (like accountability partners) when restrictions are bypassed or disabled.
3. Custom Keyboard or API Access: Provide a framework for limited system-wide text monitoring for specific use cases, again with user consent.
If anyone has ideas for how to address this within current policies—or suggestions for advocating for more flexibility—I’d appreciate the input. I’m curious how others have handled similar challenges or if there are better approaches I haven’t considered.
Hi Guys I try to use smart card reader with my ipad project and after trying to make init session with the card got error from cryptotokenkit
Message from debug log :
[smartcard] slot refused to allocate exclusive session for the card
"Session error: Error Domain=CryptoTokenKit Code=-2 "(null)""
// connect to the card
mngr?.getSlot(withName: slotName, reply: {
(slot:TKSmartCardSlot?) in
let card = slot?.makeSmartCard()
if card != nil {
// begin session
card?.beginSession{
( success:Bool,error:Error?) in
if success {
// Send 1st APDU
card?.send(ins: 0x84, p1: 0x00, p2: 0x00,le:8){
(data:Data?,sw:UInt16,error:Error?) in
if error != nil {
print("sendIns error:",error!)
}else{
print("Response:",data!,String(sw,radix: 16))
//String(format: "%02X", data! as CVarArg)
}
}
}else{
print("Session error:",error!)
}
}
}else{
print("No card found")
}
})
The Passwords App is accessing websites found in the ASCredentialIdentityStore associated with a 3rd Party password management app (SamuraiSafe). This behaviour appears to be associated with looking up website favicons in order to display in Passwords. However the websites contacted are not stored in the Passwords App/iCloud KeyChain - only the 3rd Party password management app (SamuraiSafe). This is effectively leaking website information stored in the 3rd Party password management app.
I first noticed this behaviour on macOS, and it appears to happen every 8 days. Today it was seen on iOS.
The behaviour is revealed through the App Privacy Report on iOS (and LittleSnitch on macOS).
I would not be surprised to see the Passwords App do this for websites saved in the Passwords App/iCloud KeyChain, however I believe it should not be arbitrarily testing every website found in the ASCredentialIdentityStore as reference to that website url should be entirely under the control of the end user.
See attached screenshots from App Privacy Report.
Filed bug with Apple: FB16682423
I try to send apdu command via smartcard with Cryptotokenkit on IOS application but got some error
in debug log
[smartcard] slot refused to allocate exclusive session for the card
"error domain=cryptotokenkit code=-2 "(null)""
for my code please see below code
// connect to the card
mngr?.getSlot(withName: slotName, reply: {
(slot:TKSmartCardSlot?) in
let card = slot?.makeSmartCard()
if card != nil {
// begin session
card?.beginSession{
( success:Bool,error:Error?) in
if success {
// Send 1st APDU
card?.send(ins: 0x84, p1: 0x00, p2: 0x00,le:8){
(data:Data?,sw:UInt16,error:Error?) in
if error != nil {
print("sendIns error:",error!)
}else{
print("Response:",data!,String(sw,radix: 16))
//String(format: "%02X", data! as CVarArg)
}
}
}else{
print("Session error:",error!)
}
}
}else{
print("No card found")
}
})
So i'm not sure what i do wrong on this
Hi,
I need to make a "Sign with apple" button.
I followed all the instructions and I managed to get apple's response with id_token.
I have to continue testing the application but I no longer get the "user" object (name, lastname).
How can I get the user object back to continue development?
Topic:
Privacy & Security
SubTopic:
General
Tags:
Sign in with Apple
Sign in with Apple JS
Sign in with Apple REST API
Hi,
I'm currently in the middle of debugging between my macOS and server for implementing Platform SSO.
As part of the debug process, I sometimes want to restart the whole process, which means to get into the beginDeviceRegisteration method again.
I noticed that even if I push the Repair button in the user (under settings) it will go again and again into the beginUserRegistration, but it will not go again to the device registration.
Is there an option to reset the Platform SSO device registration? (already tried Repair, remove MDM profile of the PSSO etc.)
I would like to be able to reset an user password through some automated scripts, would need help regarding that.
Topic:
Privacy & Security
SubTopic:
General
I'm working on a Password Manager app that integrates with the AutoFill Credential Provider to provide stored passwords and OTPs to the user within Safari and other apps.
Password AutoFill works perfectly.
I'm unable to get iOS to register that the app supports OTPs though.
I've followed the Apple documentation here: https://developer.apple.com/documentation/authenticationservices/providing-one-time-passcodes-to-autofill and added "ProvidesOneTimeCodes" to the AutoFill extension's Info.plist, but iOS just doesn't seem to notice the OTP support.
<key>ASCredentialProviderExtensionCapabilities</key>
<dict>
<key>ProvidesOneTimeCodes</key>
<true/>
<key>ProvidesPasswords</key>
<true/>
</dict>
Any help would be greatly appreicated!
Topic:
Privacy & Security
SubTopic:
General
Tags:
Extensions
Entitlements
Autofill
Authentication Services
I'm trying to create an "Extensible Enterprise SSO" extension as described in the Introducing Extensible Enterprise SSO tech talk.
My SSO extension works fine, but I want to be able to access the camera (via AVFoundation) from within the SSO extension.
According to this thread (which I can't seem to be able to reply to) - it should be possible to access the camera from within an SSO extension, however this doesn't work for me.
When I try to access the camera, I get the permission dialog, but after accepting, the camera preview is empty and no camera frames are produced.
I don't get any errors/warnings in the logs, but it immediately fires AVCaptureSession.wasInterruptedNotification notification with AVCaptureSessionInterruptionReasonKey = 1 which corresponds to videoDeviceNotAvailableInBackground.
However, the SSO extension view controller is clearly not in the background, so is this a bug - or are there special rules for requesting camera permission in an SSO extension? The same camera access works fine in the host app, just not inside the extension.
Interestingly, accessing the camera in a WKWebView using various webcam test pages, doesn't work either.
All of these tests have been on iPadOS 18.
Private relay emails are ending up in the spam folder, even though SPF and DKIM are set up correctly.