Prioritize user privacy and data security in your app. Discuss best practices for data handling, user consent, and security measures to protect user information.

All subtopics

Post

Replies

Boosts

Views

Activity

REST API for checking "Transferred" Apple ID Credential State?
After an app transfer, I'm reached this point in this article, and I need to check if the Sign In with Apple ID users I've migrated have the "transferred" status. This articles provides you with Swift code to check this status; does anyone know if there's a way to access this info using the Apple ID REST API, preferably with a curl command? Or do I specifically need to check this info on my app? Here's the swift code I'm talking about: let request = ASAuthorizationAppleIDProvider().createRequest() // Specify the current user identifier here. request.user = "User Identifier" let controller = ASAuthorizationController(authorizationRequests: [request]) controller.delegate = self controller.presentationContextProvider = self controller.performRequests()
1
0
203
Aug ’24
"Sign in with Apple" on iOS
Hello, I have a JS website that uses "Sign in with Apple" with AppleID.auth.signIn : It redirects to appleid.apple.com It redirects to my backend with a form POST Then I redirect the user to my frontend I bundled my website as a webview in an app for iOS. When using the functionality on iOS : A faceId popup appears over my app The user is redirected to appleid.apple.com A second faceId popup appears It redirects to my backend with a form POST it gets redirected to my frontend in an embeded Safari. When closing the embeded browser, the user is not connected on my app I would like my user to be redirected to my app instead of staying in the embeded browser. I would like to eliminate the double faceId popups The functionality works in the web (Safari desktop and mobile), as a PWA, and on Android. So I think I have properly configured the Domains and Redirect URLs in my Sign in with Apple service. When I click on a link that points to my website from Safari, it opens my iOS app. So I think I have properly configured the associated domains. When testing the functionality from an iPhone emulator in XCode, it works (but uses a simple appleId/password form instead of faceId) I could not find an answer on the forums that solved my problem. I can provide code and screen captures. Thank you
1
0
204
Aug ’24
MDM and App identities management
We are working on an application that allows to open and share encrypted containers. These containers can be protected by a variety of access types : most users choose to use password accesses, as those are easy to setup and don't require any supplementary enrollment done by the user. To enhance app functionnality, especially in managed environments, we would like to find a way to distribute more efficiently certificates that may be used to access the containers. Preliminary research led me to believe that something akin to what the Android Keystore does is not doable here : the general Keychain can't be accessed by applications, which can only access their own, specific "sub-keychain". https://developer.apple.com/library/archive/qa/qa1745/_index.html I was wondering if there were any plans to change that, especially in the case of managed environments managed by an MDM. If there is not, is there any way the identities supplied by an MDM can be supplied directly to a managed application's keychain ? Are there any recommended usages for this use case ?
0
0
144
Aug ’24
Trouble Generating Client Secret for Apple Sign-In Migration
We're preparing to migrate our Apple Sign-In users for an upcoming app transfer. Following this guide, we're currently stuck on the first curl command: curl -v POST "https://appleid.apple.com/auth/token" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials' \ -d 'scope=user.migration' \ -d 'client_id=CLIENT_ID' \ -d 'client_secret=CLIENT_SECRET_ISSUED_BY_TEAM_A' Specifically, we're having issues generating the client secret, specified here. We're using a Node.js script to generate the script; initially I realized that the private key I was signing the JWT with was wrong (I was using the App Store Connect API team key instead of a private key for use with Account & Organization Data Sharing). Every time we try entering this curl command: curl -v POST "https://appleid.apple.com/auth/token" \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials' \ -d 'scope=user.migration' \ -d 'client_id=com.jumbofungames.platMaker' \ -d 'client_secret=$(node clientsecret_jwt2.js)' Where $(node clientsecret_jwt2.js) is the command to generate the client secret; we get this error: < HTTP/1.1 400 Bad Request < Server: Apple < Date: Mon, 19 Aug 2024 15:41:31 GMT < Content-Type: application/json;charset=UTF-8 < Content-Length: 49 < Connection: keep-alive < Pragma: no-cache < Cache-Control: no-store < * Connection #1 to host appleid.apple.com left intact {"error":"invalid_client","email_verified":false}% Here is the script we are using to generate the Client Secret (JWT), with some variables using placeholders for privacy: const fs = require('fs'); const jwt = require('jsonwebtoken'); // npm i jsonwebtoken // You get privateKey, keyId, and teamId from your Apple Developer account const privateKey = fs.readFileSync("./AuthKey_ABCDEFG123.p8") // ENTER THE PATH TO THE TEAM KEY HERE (private key file) const keyId = "API_KEY"; // ENTER YOUR API KEY ID HERE const teamId = "TEAM_ID"; // ENTER YOUR TEAM ID HERE const clientId = "com.companyname.appname"; // ENTER YOUR APP ID OR SERVICES ID HERE (This is the client_id) // Time settings (in seconds) let now = Math.round((new Date()).getTime() / 1000); // Current time (seconds since epoch) let nowPlus6Months = now + 15776999; // 6 months from now (maximum expiration time) let payload = { "iss": teamId, // The Team ID associated with your developer account "iat": now, // Issued at time "exp": nowPlus6Months, // Expiration time (up to 6 months) "aud": "https://appleid.apple.com", // Audience "sub": clientId // The App ID or Services ID } let signOptions = { "algorithm": "ES256", // Algorithm for signing (ECDSA with P-256 curve and SHA-256 hash algorithm) header : { "alg": "ES256", // Algorithm in the header "kid": keyId, // Key ID in the header "typ": "JWT" // JWT type } }; // Generate the JWT (client_secret) let clientSecret = jwt.sign(payload, privateKey, signOptions); console.log(clientSecret); module.exports = clientSecret; If anyone has run into similar issues using this API or could shed some light on what could be going wrong, please let us know — we're at a bit of a loss here.
1
0
372
Aug ’24
Biometric authentication, Face ID doesn't get triggered
When a user swipes up to see the app switcher, I put a blocking view over my app so the data inside cannot be seen if you flick through the app switcher. I do this by checking if the scenePhase goes from .active to .inactive. If the app goes into the background, scenePhase == .background so I trigger something that would force the user to authenticate with Face ID/Touch ID when the app is next brought to the foreground or launched. However, this doesn't seem to work. The biometrics authentication is executed, but it just lets the user in without showing the Face ID animation. I put my finger over the sensors so it couldn't possibly be authenticating, but it just lets them in. Here's a quick set of logs: scenePhase == .inactive - User showed app switcher scenePhase == .background - User swiped up fully, went to Home Screen scenePhase == .inactive - User has tapped the app icon scenePhase == .active - App is now active authenticate() - Method called authenticate(), authenticateViaBiometrics() == true - User is going to be authenticated via Face ID // Face ID did not appear! success = true - Result of calling `context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics` means user was authenticated successfully error = nil - No error in the authentication policy authenticate(), success - Method finished, user was authenticated Here's the code: print("authenticate(), authenticateViaBiometrics() == true - User is going to be authenticated via Face ID") var error: NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { // Handle permission denied or error print("authenticate(), no permission, or error") authenticated = false defaultsUpdateAuthenticated(false) defaultsUpdateAuthenticateViaBiometrics(false) return } context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate with biometrics") { (success, error) in DispatchQueue.main.async { print("success = \(success)") print("error = \(String(describing: error?.localizedDescription))") if(success) { print("authenticate(), success") authenticated = true } else { print("authenticate(), failure") authenticated = false } } } This happens with or without the DispatchQueue... call.
2
0
378
Aug ’24
Spawn Constraint example
I downloaded the sample code given in: https://developer.apple.com/documentation/servicemanagement/updating-your-app-package-installer-to-use-the-new-service-management-api?language=objc Made necessary changes and I was able to install and test successfully. Next, I watched: https://developer.apple.com/videos/play/wwdc2023/10266/ I noted the example given at: 14:52 Example launchd plist constraint. Applied the KeepAlive, RunAtLoad and SpawnConstraints parameters to the sample code downloaded earlier. I got the log in the console and agent was not allowed: default 11:35:26.885483+0300 kernel AMFI: Launch Constraint Violation (enforcing), error info: c[5]p[1]m[1]e[0], (Constraint not matched) launching proc[vc: 10 pid: 19439]: /Library/Application Support/X/SMAppServiceSampleCode.app/Contents/Resources/SampleLaunchAgent, launch type 0, failure proc [vc: 10 pid: 19439]: /Library/Application Support/X/SMAppServiceSampleCode.app/Contents/Resources/SampleLaunchAgent Is SpawnConstraint not applicable for launch agents? Since launchd is the only parent process that can spawn the launch agent based on the plist, is the example given at 14:52 still valid?
4
1
345
Aug ’24
Local Network Privacy and MDM.
Starting with macOS 15, the Local Network Privacy (LNP) feature has been introduced, which has been available on iOS for some time. We are developing an enterprise application for remote control and protection of corporate devices. The management is carried out using a dedicated server, usually located on the internal LAN of the enterprise. In order to interact with this server, devices require access to the local network, which is managed by the new TCC LNP. The UX of our application involves minimal user interaction. To address this issue, we use Apple's MDM to automatically grant various permissions. Additionally, we have scripts for remotely installing the application and configuring it. However, for the new TCC LNP, we have not found a way to do this through the MDM profile. Does Apple intend to incorporate the relevant functionality into the MDM? There are several posts on the dev forum about this topic, but they are all about iOS.
1
1
437
Aug ’24
Inquiry Regarding Secure Enclave Storage Capacity and Key Generation
Dear Apple Support, I hope this message finds you well. I am writing to inquire about the storage capacity and key generation limitations of the Secure Enclave on iOS devices. My specific questions are as follows: Does the Secure Enclave utilize its own independent memory for storing keys and other sensitive data? Is this storage area shared among all apps on the device? If the storage is indeed shared among all apps, is it possible that my app could fail to generate and store even a single key due to a lack of available memory in the Secure Enclave? Understanding these details is crucial for the secure development of our application. We need to ensure that we can reliably generate and store keys within the Secure Enclave without encountering unexpected limitations. Thank you for your assistance and I look forward to your response. Best regards,
2
0
266
Aug ’24
App transfer - exchange identifiers 'invalid_request' error for most users
We recently did an app transfer of our app from one account to a parent company's account. Because we have Sign In With Apple, we need to transfer the users. We're following this doc: https://developer.apple.com/documentation/sign_in_with_apple/bringing_new_apps_and_users_into_your_team We have about 118,000 users, and were successful in generating the transfer identifier for the majority. We assume some users have revoked access. When we try to call to exchange identifiers, we are successful for about 50k users, but for the remaining 68k users we are receiving 'invalid_request' with no other information I don't understand why we would have a transfer identifier, and then receive 'invalid_request.' If it was a configuration problem I would expect that error for all users, or invalid_client. I've already gone through this doc: https://developer.apple.com/documentation/technotes/tn3107-resolving-sign-in-with-apple-response-errors We've tried using a Node.js script, bash, Postman, with the same result. If an API call to exchange identifiers has an error, that user has an error every time. If a call is successful, it's successful every time. Other forum posts are dead ends with no resolution. Has anybody seen this behavior and found a fix for it?
7
2
512
Aug ’24
App's Location, Camera, and Microphone Permissions Missing from Settings
Hello, I am currently developing an app using React Native. The app requires the following permissions: location sharing, camera access, photo library access (write and usage), and microphone access. I have listed all the necessary permissions in the Info.plist file. Until two days ago, everything was working perfectly. However, since then, the location, camera, and microphone permissions have disappeared from the App's Settings page. The photo library permission remains intact. I have not encountered any errors in the Xcode build, and there are no notable differences in the logs. I have tried the following troubleshooting steps: Deleted and reinstalled the app. Removed and re-added the permissions in the Info.plist file, including testing the photo library permission which works as expected. Cleared the Xcode derived data cache using rm -rf ~/Library/Developer/Xcode/DerivedData/*. Deleted and reinstalled CocoaPods by removing the Pod and Podfile.lock files. Reverted to versions from more than two days ago, but the permissions are still missing. Tested on the simulator, but the issue persists. Interestingly, a team member located in a different country (South Korea) reports that all permissions except the microphone are working fine. Has anyone else encountered a similar issue? What could be causing this problem? Are there any suggested solutions or inspired approaches to resolve this? Thank you in advance for any assistance or insights.
0
0
218
Aug ’24
Local Network privacy alert not triggering with iOS 18 beta
Hello, I have been testing my app with iOS 18 beta and noticing an issue with the triggering of Local Network privacy prompt. My app uses this permission to make a request to a local network address. Prior to iOS upgrade to 18 beta, the privacy prompt used to get triggered upon making the request and only after tapping on 'Allow', the subsequent requests used to succeed. If the user turned off the toggle for 'Local Network' in the app settings, then this functionality used to break as expected. Issues observed with 18 beta: The privacy prompt is not getting triggered upon making the request to local network and the request is succeeding. The app already seems to have this access granted but I do not see the permission toggle in the app settings. Upon device restart, the prompt got triggered but even on disallowing the access (tapping Don't Allow), the app is able to make requests to the local network. The permission toggle appears in the app settings, but its state does not impact the app's functionality. Has something changes in this flow? Can someone please help with what might be causing this behaviour?
2
3
575
Aug ’24
Clarity on iCloud Private Relay behaviour with HTTP traffic
While analyzing iCloud Private Relay traffic using my App's content filter (based on NEFilterDataProvider), I noticed a couple of items that piqued my curiosity and wanted to see if I could get more information here. Namely, when accessing a HTTP site via iCloud Private Relay, there is an initial flow established to UDP port 80 and an unspecified IPv6 address (i.e, '::'). This seemed odd for a few reasons: IPv6 is disabled on the system and the site I'm connecting with only has an IPv4 address. Also, the unspecified IPv6 address seems strange in general. In the documentation, iCloud Private Relay claims to only use UDP port 443 [1][2]. Could you provide more clarity on how this works in the background? Would it also be possible to confirm whether UDP port 80 should be included in any filtering logic concerning iCloud Private Relay? Note: in case this isn't the general behavior for iCloud Private Relay, I wanted to further clarify that this is the behavior I've seen in Safari 17 on Sonoma and Sequoia, when accessing websites via HTTP, with Safari's "Use advanced tracking and fingerprinting protection" enabled. Thanks! [1] https://developer.apple.com/support/prepare-your-network-for-icloud-private-relay [2] https://support.apple.com/en-ca/101555
0
0
260
Aug ’24
macOS PSSO extension development
Hey guys, I have recently started with developing an extension to support PSSO, I am at a very initial stage and trying out device registration. I am trying to fetch the registration token in my MDM profile but when running in debug mode I don't see the token , and also when I see the console log I see errors like error 14:44:00.465847+0530 AppSSODaemon Error Domain=com.apple.PlatformSSO Code=-1004 "no device configuration data to load" UserInfo={NSLocalizedDescription=no device configuration data to load} error 14:44:00.466434+0530 AppSSOAgent Error Domain=com.apple.PlatformSSO Code=-1004 "no device configuration" UserInfo={NSLocalizedDescription=no device configuration}, user default 14:44:00.466145+0530 AppSSODaemon -[PODaemonProcess deviceConfigurationForIdentifer:completion:] identifer = 96DBA2E4-6DB8-4937-85A8-69F7632B8717 on <private> error 14:44:00.466773+0530 SSO extension Error Domain=com.apple.PlatformSSO Code=-1001 "failed to retrieve SecKeyProxyEndpoint for key" UserInfo={NSLocalizedDescription=failed to retrieve SecKeyProxyEndpoint for key, NSUnderlyingError=0x14b608820 {Error Domain=com.apple.PlatformSSO Code=-1001 "Failed to receive key proxy endpoint." UserInfo={NSLocalizedDescription=Failed to receive key proxy endpoint.}}} I think due to some reason the PSSO process is not able to get the token from my configuration. And this is how my configuration profile looks like <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>PayloadContent</key> <array> <dict> <key>ExtensionIdentifier</key> <string>com.test.PSSO.SSO-extension</string> <key>PayloadDisplayName</key> <string>ingle Sign-On Extensions</string> <key>PayloadIdentifier</key> <string>com.apple.extensiblesso.96DBA2E4-6DB8-4937-85A8-69F7632B8717</string> <key>PayloadType</key> <string>com.apple.extensiblesso</string> <key>PayloadUUID</key> <string>CDC67F3E-0687-4796-95B0-A61EF6F3F9A7</string> <key>PayloadVersion</key> <integer>1</integer> <key>TeamIdentifier</key> <string>my_team_identifier</string> <key>Type</key> <string>Redirect</string> <key>RegistrationToken</key> <string>dummy_token_123</string> <key>PlatformSSO</key> <dict> <key>AuthenticationMethod</key> <string>Password</string> <key>EnableAuthorization</key> <true/> <key>EnableCreateUserAtLogin</key> <true/> <key>NewUserAuthorizationMode</key> <string>Standard</string> <key>UseSharedDeviceKeys</key> <true/> <key>UserAuthorizationMode</key> <string>Standard</string> </dict> <key>URLs</key> <array> <string>my_url</string> </array> </dict> </array> <key>PayloadDisplayName</key> <string>SSOE</string> <key>PayloadIdentifier</key> <string>com.test.psso.configuration</string> <key>PayloadScope</key> <string>System</string> <key>PayloadType</key> <string>Configuration</string> <key>PayloadUUID</key> <string>0DC6670F-F853-49CB-91B3-1C5ECB5D3F46</string> <key>PayloadVersion</key> <integer>1</integer> </dict> </plist>
0
0
282
Aug ’24
Way of handling important data temporarily in swift
Hello! I have been making an app where a user is able to create an account, as well as provide other important data like their name, address, weight, etc. When it comes to managing the data between view controllers, what do you recommend as a way to save the data for account creation purposes temporarily such as stage 1 (enter your username) and stage 2 (enter your password)(as moving into the next view controller through a segue removes all data?) As I am using firebase to process the data (not store it) I considered using user defaults with a temporary system where after 5 minutes, all data is wiped under a "session timeout", however I am skeptical of this. What are your thoughts as I am new to this?
0
0
142
Aug ’24
Create Identity from certificate and key
I have x509 certificate in pem format. CertificatePem -----BEGIN CERTIFICATE----- MIIC3jCCAcYCAQAw...9gBFNQUdahSccXF2bnZkv2Kh -----END CERTIFICATE----- PrivatekeyPem: -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQE...ooxp1Nyl17zfP -----END RSA PRIVATE KEY----- And I convert it to base64 using this JS code const pemHeader = type === 'certificate'? '-----BEGIN CERTIFICATE-----' : '-----BEGIN RSA PRIVATE KEY-----'; const pemFooter = type === 'certificate'? '-----END CERTIFICATE-----':'-----END RSA PRIVATE KEY-----'; let base64Key = pemKey.replace(pemHeader, '').replace(pemFooter, ''); // Remove any newline characters base64Key = base64Key.replace(/\r?\n|\r/g, ''); return base64Key; }; And my CertificateBase64 look like: MIIC3jCCAcYCAQAw...9gBFNQUdahSccXF2bnZkv2Kh PrivateBase64: MIIEowIBAAKCAQE...ooxp1Nyl17zfP I want to create identity to use in https request. I am getting error: Unable to create identity: -25300 My loadIdentity function look like this: func loadIdentity(certificate: String, privateKey: String) -> SecIdentity? { print("privateKey: \(privateKey)") guard let certData = Data(base64Encoded: certificate) else { print("Unable to decode certificate PEM") return nil } print("certData: \(certData)") // Create certificate object guard let cert = SecCertificateCreateWithData(nil, certData as CFData) else { print("Unable to create certificate") return nil } // Add certificate to the keychain let certAddQuery: [NSString: Any] = [ kSecClass: kSecClassCertificate, kSecValueRef: cert, kSecAttrLabel: "myCertificate" ] var status = SecItemAdd(certAddQuery as CFDictionary, nil) if status != errSecSuccess && status != errSecDuplicateItem { print("Failed to add certificate to keychain: \(status)") return nil } guard let keyData = Data(base64Encoded: privateKey) else { print("Unable to decode private key PEM") return nil } print("keyData: \(keyData)") // Define attributes for the private key let keyDict: [NSString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits: 2048, kSecReturnPersistentRef: true ] // Create private key object var error: Unmanaged<CFError>? guard let privateKeyData = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else { // print("Unable to create private key: \(error?.takeRetainedValue() ?? "Unknown error" as CFError)") print("Unable to create private key") return nil } // Add private key to the keychain let keyAddQuery: [NSString: Any] = [ kSecClass: kSecClassKey, kSecValueRef: privateKeyData, kSecAttrLabel: "myPrivateKey", kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked ] status = SecItemAdd(keyAddQuery as CFDictionary, nil) if status != errSecSuccess && status != errSecDuplicateItem { print("Failed to add private key to keychain: \(status)") return nil } // Query to retrieve the identity from the keychain let identityQuery: [NSString: Any] = [ kSecClass: kSecClassIdentity, kSecReturnRef: true, kSecAttrLabel: "myCertificate", kSecMatchItemList: [cert, privateKeyData] ] var identity: CFTypeRef? status = SecItemCopyMatching(identityQuery as CFDictionary, &identity) guard status == errSecSuccess else { print("Unable to create identity") return nil } return (identity as! SecIdentity) }
1
1
219
Aug ’24
ATT
hi, In the Apple documentation, it says we can still track the customers who do not consent on ATT if we are just using the first party data. Could you please elaborate on first party and third-party data from IOS perspective? We referred to following documentation from Apple. https://www.apple.com/privacy/docs/Mobile_Advertising_and_the_Impact_of_Apples_App_Tracking_Transparency_Policy_April_2022.pdf And as we are just using our customer data and are not using any external data, can we go ahead and track our customers traffic? Thank you
0
0
168
Aug ’24
Xcode 16 thinks iPhone has Dev mode disabled, but it doesn't
I'm attempting to do something I've done many times—built to device. However, since updating my mac OS to 15, downloading Xcode 16 beta, and updating my device to iOS 18 beta, I am unable to do so. Xcode is giving me an error telling me that Developer mode is disabled with instructions to enable. But when I check the device, Developer mode is already enabled. I've tried disabling and re-enabling (which requires a restart and confirmation). I've tried shutting down the device and restarting it. I've tried quitting and re-opening Xcode. Anyone else running into this issue? Any thoughts?
0
1
229
Aug ’24