Post

Replies

Boosts

Views

Activity

Scoping App Attest keys to an appTransactionID
Hi StoreKit team, I posted a question in the App Attest forum yesterday that is related to StoreKit [1]. The engineers there recommended I post here. I'd like to use StoreKit 2's appTransactionID [2] as an ID to tie App Attest keys to anonymous users (that is, users that I don't enforce signup/login of). The design I'm thinking of is basically: Get the appTransactionID at launch Look in keychain to see if I already have an attestation key for this appTransactionID (treating keychain as a k/v store where the key is appTransactionID) If yes, use that key for ongoing assertions as requests are made to my backend Else, go through the initial process of key generation and attestation Do you see any gotchas with using appTransactionID in this way? From the docs, it looks to be exactly what I need: The App Store generates a single, globally unique appTransactionID for each Apple Account that downloads your app and for each family group member for apps that support Family Sharing. This value remains the same for the same Apple Account and app if the customer redownloads the app on any device, receives a refund, repurchases the app, or changes the storefront. For apps that support Family Sharing, the appTransactionID is unique for each family group member. But I'm curious if this application of using appTransactionID as the key identifier to pull values out of keychain raises any flags for you. Thank you, Lou [1] https://developer.apple.com/forums/thread/831468 [2] https://developer.apple.com/documentation/storekit/apptransaction/apptransactionid
1
0
88
1w
How can a compromised device pass attestations
Hi App Attest team, I was nodding along happily in the wwdc session, because it was seeming like an air tight solution to prevent API abuse while allowing "guest" access (e.g. not enforcing that users log in). Then I hit this line, "a compromised device can still pass attestations". How is that possible? Earlier in the session, the presenter said "[AppAttest] gives you the assurance that your app is running on a secure apple device". I'm trying to square these statements and understand the motivation of the 'fraud metric'. Thank you! Lou Ps. I'm so happy that AppAttest is available on Mac now. :D
4
1
136
1w
Blessed pattern for detecting key invalidations on reinstall
The wwdc session mentioned that attestation keys survive app updates but not reinstalls. So it seems like if I try to create an assertion after reinstall from the key I pull from keychain, and include that assertion in my API payload to my backend, my backend will reject the assertion. Is there any mechanism for us to ask the client framework "is this key still valid"? Thanks again, Lou
1
0
65
1w
Is keying off Storekit's AppTransactionID a valid pattern for storing keys?
My understanding from the App Attest wwdc session is that we store attestation keys in keychain on a per-user basis. For apps that don't require user login, I'm thinking of using StoreKit's AppTransactionID [1] as the identifier to discriminate keys. Do you have opinions on whether this is a valid pattern? [1] https://developer.apple.com/documentation/storekit/apptransaction/apptransactionid
1
0
77
1w
When is the unverified branch of AppTransaction.shared entered?
Hi all, I am adding the following StoreKit 2 code to my app, and I don't see anything in Apple's documentation that explains the unverified case. When is that case exercised? Is it when someone has tampered with the app receipt? Or is it for more mundane things like poor network connectivity? // Apple's docstring on `shared` states: // If your app fails to get an AppTransaction by accessing the shared property, see refresh(). // Source: https://developer.apple.com/documentation/storekit/apptransaction/shared var appTransaction: VerificationResult<AppTransaction>? do { appTransaction = try await AppTransaction.shared } catch { appTransaction = try? await AppTransaction.refresh() } guard let appTransaction = appTransaction else { AppLogger.error("Couldn't get the app store transaction") return false } switch appTransaction { case .unverified(appTransaction, verificationError): // For what reasons should I expect this branch to be entered in production? return await inspectAppTransaction(appTransaction, verifiedByApple: false) case .verified(let appTransaction): return await inspectAppTransaction(appTransaction, verifiedByApple: true) } Thank you, Lou
12
1
974
Apr ’25
Drop from URLSession to Network framework for SNI
Hi Dev Forums and Quinn "The Eskimo!", Short version Is there sample NWConnection code available that behaves in a similar way to the higher level URLSession and URLRequest APIs? Long version I have not been able to make this question get past the "sensitive language filter" on the dev forums. I figured it might be 'fool' or 'heck', or the X link, but removing each of those still triggers the sensitive language filter. Please see this gist: https://gist.github.com/lzell/8672c26ecb6ee1bb26d3aa3c7d67dd62 Thank you! Lou Zell
2
0
197
Apr ’25
Change the OSLog level that is written to Xcode console of a dependent library
I'd like to give control to the app developer that uses my library to select which level of logs they'd like to see from my lib (e.g. do they want to see all debug messages or just errors). I know there are filtering controls that Xcode gives us, but I'm wondering if there is a way to pull this off with code. Ideally the user callsite would look like MyLib(logLevel: .info). And then I would pass that info level somehow to OSLog. Today, I create my logger like this: let myLogger = Logger( subsystem: Bundle.main.bundleIdentifier ?? "UnknownApp", category: "MyLibrary" ) As far as I can tell, there is nothing I can then pass to my myLogger instance to configure the threshold level. I'm imagining an interface like: myLogger.logLevel(.warning) // Later... myLogger.debug("You won't see this") myLogger.error("But you will see this") Does OSLog and friends give us any ability to do this out of the box, or are we building little wrappers around OSLog to accomplish this? Thank you, Lou
4
0
520
Mar ’25
Turning on setVoiceProcessingEnabled bumps channel count to 5
Hi all, The use of setVoiceProcessingEnabled increases the channel count of my microphone audio from 1 to 5. This has downstream effects, because when I use AVAudioConverter to convert between PCM buffer types the output buffer contains only silence. Here is a reproduction showing the channel growth from 1 to 5: let avAudioEngine: AVAudioEngine = AVAudioEngine() let inputNode = avAudioEngine.inputNode print(inputNode.inputFormat(forBus: 0)) // Prints <AVAudioFormat 0x600002f7ada0: 1 ch, 48000 Hz, Float32> do { try inputNode.setVoiceProcessingEnabled(true) } catch { print("Could not enable voice processing \(error)") return } print(inputNode.inputFormat(forBus: 0)) // Prints <AVAudioFormat 0x600002f7b020: 5 ch, 44100 Hz, Float32, deinterleaved> If it helps, the reason I'm using setVoiceProcessingEnabled because I don't want the mic to pick up output from the speakers. Per wwdc When enabled, extra signal processing is applied on the incoming audio, and any audio that is coming from the device is taken Here is my conversion logic from the input PCM format (which in the case above is 5ch, 44.1kHZ, Float 32, deinterleaved) to the target format PCM16 with a single channel: let outputFormat = AVAudioFormat( commonFormat: .pcmFormatInt16, sampleRate: inputPCMFormat.sampleRate, channels: 1, interleaved: false ) guard let converter = AVAudioConverter( from: inputPCMFormat, to: outputFormat) else { fatalError("Demonstration") } let newLength = AVAudioFrameCount(outputFormat.sampleRate * 2.0) guard let outputBuffer = AVAudioPCMBuffer( pcmFormat: outputFormat, frameCapacity: newLength) else { fatalError("Demonstration") } outputBuffer.frameLength = newLength try! converter.convert(to: outputBuffer, from: inputBuffer) // Use the PCM16 outputBuffer The outputBuffer contains only silence. But if I comment out inputNode.setVoiceProcessingEnabled(true) in the first snippet, the outputBuffer then plays exactly how I would expect it to. So I have two questions: Why does setVoiceProcessingEnabled increase the channel count to 5? How should I convert the resulting format to a single channel PCM16 format? Thank you, Lou
2
0
668
Dec ’24
How to get in contact with team that manages DeviceCheck
Hi, I run a service that protects API calls from Apple ecosystem apps with several layers of security, one of them being DeviceCheck's server-to-server functionality. All requests arrive with a DeviceCheck token that I send to Apple to validate. Essentially I'm using the functionality listed here: The server-to-server APIs also let you verify that the token you receive comes from your app on an Apple device. https://developer.apple.com/documentation/devicecheck However, occasionally I see huge bursts of traffic that contain valid DeviceCheck tokens from a scripter. I want to understand how they are generating them. It seems like they have identified a way to forge tokens. Here are traffic patterns for my site. The scale of the y-axis is somewhat arbitrary due to how I'm sampling the requests, but you get the gist. You can see the dark green bars at the bottom are general traffic, and the light green is what we rejected (we have other layers besides DeviceCheck that reject traffic). Interestingly, though, all those light green requests contained valid device check tokens! I have thousands of the tokens stored in a file for analysis. Are there known ways that Apple knows of tokens being forged? I wanted to open a TSI for this but the flow requires an Xcode project, and there is no Xcode project to demonstrate this issue. I would really like to get in contact with someone from Apple that either works on DeviceCheck or supports it. Hundreds of apps in the store depend on my service, and DeviceCheck forms a layer of security that I want to rely on. Obviously we can't solely rely on it, and we don't, but it does form an important layer of our defense. So I ask: If you know of a way to forge tokens, please comment and I'll shoot you a DM If you work at Apple and know who I can talk to, please help me work through the process to get in touch with them. Thanks, Lou
5
0
748
Dec ’24
Scoping App Attest keys to an appTransactionID
Hi StoreKit team, I posted a question in the App Attest forum yesterday that is related to StoreKit [1]. The engineers there recommended I post here. I'd like to use StoreKit 2's appTransactionID [2] as an ID to tie App Attest keys to anonymous users (that is, users that I don't enforce signup/login of). The design I'm thinking of is basically: Get the appTransactionID at launch Look in keychain to see if I already have an attestation key for this appTransactionID (treating keychain as a k/v store where the key is appTransactionID) If yes, use that key for ongoing assertions as requests are made to my backend Else, go through the initial process of key generation and attestation Do you see any gotchas with using appTransactionID in this way? From the docs, it looks to be exactly what I need: The App Store generates a single, globally unique appTransactionID for each Apple Account that downloads your app and for each family group member for apps that support Family Sharing. This value remains the same for the same Apple Account and app if the customer redownloads the app on any device, receives a refund, repurchases the app, or changes the storefront. For apps that support Family Sharing, the appTransactionID is unique for each family group member. But I'm curious if this application of using appTransactionID as the key identifier to pull values out of keychain raises any flags for you. Thank you, Lou [1] https://developer.apple.com/forums/thread/831468 [2] https://developer.apple.com/documentation/storekit/apptransaction/apptransactionid
Replies
1
Boosts
0
Views
88
Activity
1w
How can a compromised device pass attestations
Hi App Attest team, I was nodding along happily in the wwdc session, because it was seeming like an air tight solution to prevent API abuse while allowing "guest" access (e.g. not enforcing that users log in). Then I hit this line, "a compromised device can still pass attestations". How is that possible? Earlier in the session, the presenter said "[AppAttest] gives you the assurance that your app is running on a secure apple device". I'm trying to square these statements and understand the motivation of the 'fraud metric'. Thank you! Lou Ps. I'm so happy that AppAttest is available on Mac now. :D
Replies
4
Boosts
1
Views
136
Activity
1w
Blessed pattern for detecting key invalidations on reinstall
The wwdc session mentioned that attestation keys survive app updates but not reinstalls. So it seems like if I try to create an assertion after reinstall from the key I pull from keychain, and include that assertion in my API payload to my backend, my backend will reject the assertion. Is there any mechanism for us to ask the client framework "is this key still valid"? Thanks again, Lou
Replies
1
Boosts
0
Views
65
Activity
1w
Is keying off Storekit's AppTransactionID a valid pattern for storing keys?
My understanding from the App Attest wwdc session is that we store attestation keys in keychain on a per-user basis. For apps that don't require user login, I'm thinking of using StoreKit's AppTransactionID [1] as the identifier to discriminate keys. Do you have opinions on whether this is a valid pattern? [1] https://developer.apple.com/documentation/storekit/apptransaction/apptransactionid
Replies
1
Boosts
0
Views
77
Activity
1w
When is the unverified branch of AppTransaction.shared entered?
Hi all, I am adding the following StoreKit 2 code to my app, and I don't see anything in Apple's documentation that explains the unverified case. When is that case exercised? Is it when someone has tampered with the app receipt? Or is it for more mundane things like poor network connectivity? // Apple's docstring on `shared` states: // If your app fails to get an AppTransaction by accessing the shared property, see refresh(). // Source: https://developer.apple.com/documentation/storekit/apptransaction/shared var appTransaction: VerificationResult<AppTransaction>? do { appTransaction = try await AppTransaction.shared } catch { appTransaction = try? await AppTransaction.refresh() } guard let appTransaction = appTransaction else { AppLogger.error("Couldn't get the app store transaction") return false } switch appTransaction { case .unverified(appTransaction, verificationError): // For what reasons should I expect this branch to be entered in production? return await inspectAppTransaction(appTransaction, verifiedByApple: false) case .verified(let appTransaction): return await inspectAppTransaction(appTransaction, verifiedByApple: true) } Thank you, Lou
Replies
12
Boosts
1
Views
974
Activity
Apr ’25
Drop from URLSession to Network framework for SNI
Hi Dev Forums and Quinn "The Eskimo!", Short version Is there sample NWConnection code available that behaves in a similar way to the higher level URLSession and URLRequest APIs? Long version I have not been able to make this question get past the "sensitive language filter" on the dev forums. I figured it might be 'fool' or 'heck', or the X link, but removing each of those still triggers the sensitive language filter. Please see this gist: https://gist.github.com/lzell/8672c26ecb6ee1bb26d3aa3c7d67dd62 Thank you! Lou Zell
Replies
2
Boosts
0
Views
197
Activity
Apr ’25
Change the OSLog level that is written to Xcode console of a dependent library
I'd like to give control to the app developer that uses my library to select which level of logs they'd like to see from my lib (e.g. do they want to see all debug messages or just errors). I know there are filtering controls that Xcode gives us, but I'm wondering if there is a way to pull this off with code. Ideally the user callsite would look like MyLib(logLevel: .info). And then I would pass that info level somehow to OSLog. Today, I create my logger like this: let myLogger = Logger( subsystem: Bundle.main.bundleIdentifier ?? "UnknownApp", category: "MyLibrary" ) As far as I can tell, there is nothing I can then pass to my myLogger instance to configure the threshold level. I'm imagining an interface like: myLogger.logLevel(.warning) // Later... myLogger.debug("You won't see this") myLogger.error("But you will see this") Does OSLog and friends give us any ability to do this out of the box, or are we building little wrappers around OSLog to accomplish this? Thank you, Lou
Replies
4
Boosts
0
Views
520
Activity
Mar ’25
Turning on setVoiceProcessingEnabled bumps channel count to 5
Hi all, The use of setVoiceProcessingEnabled increases the channel count of my microphone audio from 1 to 5. This has downstream effects, because when I use AVAudioConverter to convert between PCM buffer types the output buffer contains only silence. Here is a reproduction showing the channel growth from 1 to 5: let avAudioEngine: AVAudioEngine = AVAudioEngine() let inputNode = avAudioEngine.inputNode print(inputNode.inputFormat(forBus: 0)) // Prints <AVAudioFormat 0x600002f7ada0: 1 ch, 48000 Hz, Float32> do { try inputNode.setVoiceProcessingEnabled(true) } catch { print("Could not enable voice processing \(error)") return } print(inputNode.inputFormat(forBus: 0)) // Prints <AVAudioFormat 0x600002f7b020: 5 ch, 44100 Hz, Float32, deinterleaved> If it helps, the reason I'm using setVoiceProcessingEnabled because I don't want the mic to pick up output from the speakers. Per wwdc When enabled, extra signal processing is applied on the incoming audio, and any audio that is coming from the device is taken Here is my conversion logic from the input PCM format (which in the case above is 5ch, 44.1kHZ, Float 32, deinterleaved) to the target format PCM16 with a single channel: let outputFormat = AVAudioFormat( commonFormat: .pcmFormatInt16, sampleRate: inputPCMFormat.sampleRate, channels: 1, interleaved: false ) guard let converter = AVAudioConverter( from: inputPCMFormat, to: outputFormat) else { fatalError("Demonstration") } let newLength = AVAudioFrameCount(outputFormat.sampleRate * 2.0) guard let outputBuffer = AVAudioPCMBuffer( pcmFormat: outputFormat, frameCapacity: newLength) else { fatalError("Demonstration") } outputBuffer.frameLength = newLength try! converter.convert(to: outputBuffer, from: inputBuffer) // Use the PCM16 outputBuffer The outputBuffer contains only silence. But if I comment out inputNode.setVoiceProcessingEnabled(true) in the first snippet, the outputBuffer then plays exactly how I would expect it to. So I have two questions: Why does setVoiceProcessingEnabled increase the channel count to 5? How should I convert the resulting format to a single channel PCM16 format? Thank you, Lou
Replies
2
Boosts
0
Views
668
Activity
Dec ’24
How to get in contact with team that manages DeviceCheck
Hi, I run a service that protects API calls from Apple ecosystem apps with several layers of security, one of them being DeviceCheck's server-to-server functionality. All requests arrive with a DeviceCheck token that I send to Apple to validate. Essentially I'm using the functionality listed here: The server-to-server APIs also let you verify that the token you receive comes from your app on an Apple device. https://developer.apple.com/documentation/devicecheck However, occasionally I see huge bursts of traffic that contain valid DeviceCheck tokens from a scripter. I want to understand how they are generating them. It seems like they have identified a way to forge tokens. Here are traffic patterns for my site. The scale of the y-axis is somewhat arbitrary due to how I'm sampling the requests, but you get the gist. You can see the dark green bars at the bottom are general traffic, and the light green is what we rejected (we have other layers besides DeviceCheck that reject traffic). Interestingly, though, all those light green requests contained valid device check tokens! I have thousands of the tokens stored in a file for analysis. Are there known ways that Apple knows of tokens being forged? I wanted to open a TSI for this but the flow requires an Xcode project, and there is no Xcode project to demonstrate this issue. I would really like to get in contact with someone from Apple that either works on DeviceCheck or supports it. Hundreds of apps in the store depend on my service, and DeviceCheck forms a layer of security that I want to rely on. Obviously we can't solely rely on it, and we don't, but it does form an important layer of our defense. So I ask: If you know of a way to forge tokens, please comment and I'll shoot you a DM If you work at Apple and know who I can talk to, please help me work through the process to get in touch with them. Thanks, Lou
Replies
5
Boosts
0
Views
748
Activity
Dec ’24