Skip to content

Commit aa25476

Browse files
committed
update after code review.
1 parent d575f64 commit aa25476

4 files changed

Lines changed: 24 additions & 13 deletions

File tree

.github/workflows/pr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
# pull_request_target is used so secrets are available to fork PRs.
55
# Mitigated by per-job Member Check (see "Check Write Permission" / "Validate Write Permission").
66
pull_request_target: # zizmor: ignore[dangerous-triggers]
7-
branches: [dev, master, app-attestation]
7+
branches: [dev, master]
88
paths-ignore:
99
- '**/*.md'
1010
- 'LICENSE'

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/AppAttestation.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ public class AppAttestation: NSObject {
5050
/// Returns an attestation string if attestation is enabled and the domain is a My Domain,
5151
/// otherwise returns nil. Callers do not need to check gating conditions themselves.
5252
@objc
53-
public static func attestationIfEnabled(for domain: String?, consumerKey: String) async -> String? {
53+
public static func attestationIfEnabled(for domain: String?, consumerKey: String?) async -> String? {
5454
guard let domain,
55+
let consumerKey,
5556
shouldAttemptAttestation(for: domain, consumerKey: consumerKey, isDeviceSupported: DCAppAttestService.shared.isSupported) else {
5657
return nil
5758
}
@@ -66,7 +67,7 @@ public class AppAttestation: NSObject {
6667
}
6768

6869
// Internal for unit tests
69-
static func shouldAttemptAttestation(for domain: String?, consumerKey: String, isDeviceSupported: Bool) -> Bool {
70+
static func shouldAttemptAttestation(for domain: String?, consumerKey: String?, isDeviceSupported: Bool) -> Bool {
7071
guard let domain, !domain.isEmpty else {
7172
return false
7273
}
@@ -78,7 +79,7 @@ public class AppAttestation: NSObject {
7879
guard UserAccountManager.shared.appAttestationEnabled,
7980
isDeviceSupported,
8081
!isLoginPool,
81-
!consumerKey.isEmpty else {
82+
let consumerKey, !consumerKey.isEmpty else {
8283
return false
8384
}
8485

@@ -110,10 +111,7 @@ public class AppAttestation: NSObject {
110111
let keyId = try await keyId(for: attestationId, domain: domain, consumerKey: consumerKey)
111112

112113
let challenge = try await requestChallengeFromSalesforce(domain: domain, consumerKey: consumerKey, attestationId: attestationId)
113-
guard let challengeData = challenge.data(using: .utf8) else {
114-
throw AppAttestationError.challengeEncodingFailed
115-
}
116-
let hash = Data(SHA256.hash(data: challengeData))
114+
let hash = try sha256(of: challenge)
117115

118116
let assertion = try await performAppAttestOperation {
119117
try await DCAppAttestService.shared.generateAssertion(keyId, clientDataHash: hash)
@@ -127,17 +125,21 @@ public class AppAttestation: NSObject {
127125

128126

129127
private static func generateAttestation(keyId: String, challenge: String) async throws -> String {
130-
guard let challengeData = challenge.data(using: .utf8) else {
131-
throw AppAttestationError.challengeEncodingFailed
132-
}
133-
let hash = Data(SHA256.hash(data: challengeData))
128+
let hash = try sha256(of: challenge)
134129

135130
let attestation = try await performAppAttestOperation {
136131
try await DCAppAttestService.shared.attestKey(keyId, clientDataHash: hash)
137132
}
138133
return attestation.base64EncodedString()
139134
}
140135

136+
private static func sha256(of challenge: String) throws -> Data {
137+
guard let challengeData = challenge.data(using: .utf8) else {
138+
throw AppAttestationError.challengeEncodingFailed
139+
}
140+
return Data(SHA256.hash(data: challengeData))
141+
}
142+
141143
// 1. Client creates a key pair
142144
// 2. Client makes request to get challenge - /mobile/attest/challenge
143145
// 3. Client creates an Apple attestation object with obtained challenge

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthSessionRefresher.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ - (void)refreshSessionWithCompletion:(void (^)(SFOAuthCredentials *))completionB
8484
return;
8585
}
8686

87+
__weak typeof(self) weakSelf = self;
8788
[SFSDKAppAttestation attestationIfEnabledFor:self.credentials.domain consumerKey:self.credentials.clientId completionHandler:^(NSString * _Nullable attestation) {
88-
[self executeRefreshWithAttestation:attestation];
89+
__strong typeof(weakSelf) strongSelf = weakSelf;
90+
if (!strongSelf) return;
91+
[strongSelf executeRefreshWithAttestation:attestation];
8992
}];
9093
}
9194

libs/SalesforceSDKCore/SalesforceSDKCoreTests/AppAttestationTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,12 @@ class AppAttestationTests: XCTestCase {
270270
XCTAssertFalse(result, "Should return false when consumer key is empty")
271271
}
272272

273+
func test_givenNilConsumerKey_whenCheckingShouldAttempt_thenReturnsFalse() {
274+
UserAccountManager.shared.appAttestationEnabled = true
275+
let result = AppAttestation.shouldAttemptAttestation(for: "mydomain.my.salesforce.com", consumerKey: nil, isDeviceSupported: true)
276+
XCTAssertFalse(result, "Should return false when consumer key is nil")
277+
}
278+
273279
// MARK: - existingKeyId Tests
274280

275281
func test_givenNoKeychainData_whenCallingExistingKeyId_thenReturnsNil() throws {

0 commit comments

Comments
 (0)