Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@ public protocol AppCheckCoreAppAttestKeyIDStorageProtocol: NSObjectProtocol {
@objc func getAppAttestKeyID() async throws -> String?
}

private let kKeyIDStorageDefaultsSuiteName = "com.firebase.AppCheckCoreAppAttestKeyIDStorage"

@objc(GACAppAttestKeyIDStorage)
public class AppCheckCoreAppAttestKeyIDStorage: NSObject,
AppCheckCoreAppAttestKeyIDStorageProtocol {
/// Suite name for the app attest key ID in UserDefaults.
/// Do not rename: retains the "GAC" prefix for compatibility with existing stored data from v11
/// or lower.
static let keyIDStorageDefaultsSuiteName = "com.firebase.GACAppAttestKeyIDStorage"

private let keySuffix: String
private let userDefaults: GULUserDefaults

@objc(initWithKeySuffix:)
public init(keySuffix: String) {
self.keySuffix = keySuffix
userDefaults = GULUserDefaults(suiteName: kKeyIDStorageDefaultsSuiteName)
userDefaults = GULUserDefaults(suiteName: Self.keyIDStorageDefaultsSuiteName)
super.init()
}

Expand Down
18 changes: 13 additions & 5 deletions AppCheckCore/Sources/DebugProvider/AppCheckCoreDebugProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@ import Foundation

private let kDebugTokenEnvKey = "AppCheckDebugToken"
private let kFirebaseDebugTokenEnvKey = "FIRAAppCheckDebugToken"
private let kDebugTokenUserDefaultsKey = "AppCheckCoreDebugToken"
private let kDebugTokenRegisteredUserDefaultsKey = "AppCheckCoreDebugTokenRegistered"

@objc(GACAppCheckDebugProvider)
@objcMembers
public class AppCheckCoreDebugProvider: NSObject, AppCheckCoreProvider {
/// Storage key for the debug token in UserDefaults.
/// Do not rename: retains the "GAC" prefix for compatibility with existing stored data from v11
/// or lower.
static let debugTokenUserDefaultsKey = "GACAppCheckDebugToken"

/// Storage key prefix for registered debug tokens in UserDefaults.
/// Do not rename: retains the "GAC" prefix for compatibility with existing stored data from v11
/// or lower.
static let debugTokenRegisteredUserDefaultsKeyPrefix = "GACAppCheckDebugTokenRegistered"

private let apiService: AppCheckCoreDebugProviderAPIServiceProtocol
private let debugTokenEnvValue: String?
private let registeredUserDefaultsKey: String
Expand Down Expand Up @@ -156,11 +164,11 @@ public class AppCheckCoreDebugProvider: NSObject, AppCheckCoreProvider {
}

private static func localDebugToken() -> String {
if let token = GULUserDefaults.standard().string(forKey: kDebugTokenUserDefaultsKey) {
if let token = GULUserDefaults.standard().string(forKey: debugTokenUserDefaultsKey) {
return token
} else {
let token = UUID().uuidString
GULUserDefaults.standard().setObject(token, forKey: kDebugTokenUserDefaultsKey)
GULUserDefaults.standard().setObject(token, forKey: debugTokenUserDefaultsKey)
return token
}
}
Expand All @@ -172,7 +180,7 @@ public class AppCheckCoreDebugProvider: NSObject, AppCheckCoreProvider {
if safeResourceName.isEmpty {
safeResourceName = "default"
}
return "\(kDebugTokenRegisteredUserDefaultsKey)_\(safeServiceName)_\(safeResourceName)"
return "\(debugTokenRegisteredUserDefaultsKeyPrefix)_\(safeServiceName)_\(safeResourceName)"
}

private static func environmentVariableDebugToken(registeredUserDefaultsKey: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,46 @@ class AppCheckCoreAppAttestKeyIDStorageTests: XCTestCase {
XCTAssertNil(setKeyID)
}

// IMMUTABLE TEST: Do not edit, update, or remove under any circumstance.
// This test locks in an immutable backwards compatibility contract with App Check 11.
// If this test fails, revert changes to the implementation instead of modifying this test.
func testKeyIDStorageDefaultsSuiteName_MatchesContract() {
XCTAssertEqual(
AppCheckCoreAppAttestKeyIDStorage.keyIDStorageDefaultsSuiteName,
"com.firebase.GACAppAttestKeyIDStorage",
"Suite name mismatch breaks App Check 11 migration compatibility."
)
}

func testGetAppAttestKeyID_CompatibilityWithLegacyGACStorage() async throws {
let legacySuiteName = AppCheckCoreAppAttestKeyIDStorage.keyIDStorageDefaultsSuiteName
let legacyDefaults = try XCTUnwrap(UserDefaults(suiteName: legacySuiteName))
let legacyKey = "app_attest_keyID.\(keySuffix!)"
let expectedKeyID = "legacy_test_key_id"
legacyDefaults.set(expectedKeyID, forKey: legacyKey)
defer {
legacyDefaults.removeObject(forKey: legacyKey)
}

let retrievedKeyID = try await storage.getAppAttestKeyID()
XCTAssertEqual(retrievedKeyID, expectedKeyID)
}

func testSetAppAttestKeyID_CompatibilityWithLegacyGACStorage() async throws {
let legacySuiteName = AppCheckCoreAppAttestKeyIDStorage.keyIDStorageDefaultsSuiteName
let legacyDefaults = try XCTUnwrap(UserDefaults(suiteName: legacySuiteName))
let legacyKey = "app_attest_keyID.\(keySuffix!)"
let newKeyID = "new_test_key_id"
defer {
legacyDefaults.removeObject(forKey: legacyKey)
}

_ = try await storage.setAppAttestKeyID(newKeyID)

let storedValue = legacyDefaults.string(forKey: legacyKey)
XCTAssertEqual(storedValue, newKeyID)
}

func testGetAppAttestKeyID_WhenAppAttestKeyIDNotFoundError() async {
do {
_ = try await storage.getAppAttestKeyID()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ class MockAppCheckDebugProviderAPIService: NSObject, AppCheckCoreDebugProviderAP
class AppCheckCoreDebugProviderTests: XCTestCase {
let kDebugTokenEnvKey = "AppCheckDebugToken"
let kFirebaseDebugTokenEnvKey = "FIRAAppCheckDebugToken"
let kDebugTokenUserDefaultsKey = "AppCheckCoreDebugToken"
let kDebugTokenRegisteredUserDefaultsKey = "AppCheckCoreDebugTokenRegistered"
let kDebugTokenUserDefaultsKey = AppCheckCoreDebugProvider.debugTokenUserDefaultsKey
let kDebugTokenRegisteredUserDefaultsKey = AppCheckCoreDebugProvider
.debugTokenRegisteredUserDefaultsKeyPrefix

var provider: AppCheckCoreDebugProvider!
var fakeAPIService: MockAppCheckDebugProviderAPIService!
Expand All @@ -71,6 +72,67 @@ class AppCheckCoreDebugProviderTests: XCTestCase {

// MARK: - Debug token generating/storing

// IMMUTABLE TEST: Do not edit, update, or remove under any circumstance.
// This test locks in an immutable backwards compatibility contract with App Check 11.
// If this test fails, revert changes to the implementation instead of modifying this test.
func testDebugTokenUserDefaultsKeys_MatchContract() {
XCTAssertEqual(
AppCheckCoreDebugProvider.debugTokenUserDefaultsKey,
"GACAppCheckDebugToken",
"Debug token key mismatch breaks App Check 11 migration compatibility."
)
XCTAssertEqual(
AppCheckCoreDebugProvider.debugTokenRegisteredUserDefaultsKeyPrefix,
"GACAppCheckDebugTokenRegistered",
"Registered token key prefix mismatch breaks App Check 11 migration compatibility."
)
}

func testLegacyDebugTokenCompatibility() {
let legacyKey = kDebugTokenUserDefaultsKey
let legacyToken = "legacy_stored_debug_token"
UserDefaults.standard.set(legacyToken, forKey: legacyKey)
defer {
UserDefaults.standard.removeObject(forKey: legacyKey)
}

let debugProvider = AppCheckCoreDebugProvider(
apiService: fakeAPIService,
serviceName: "test-service",
resourceName: "projects/test-project/apps/test-app",
environment: [:]
)

XCTAssertEqual(
debugProvider.currentDebugToken(),
legacyToken,
"Debug token mismatch breaks App Check 11 migration compatibility."
)
}

func testSetDebugToken_CompatibilityWithLegacyGACStorage() {
let legacyKey = kDebugTokenUserDefaultsKey
UserDefaults.standard.removeObject(forKey: legacyKey)
defer {
UserDefaults.standard.removeObject(forKey: legacyKey)
}

let debugProvider = AppCheckCoreDebugProvider(
apiService: fakeAPIService,
serviceName: "test-service",
resourceName: "projects/test-project/apps/test-app",
environment: [:]
)

let generatedToken = debugProvider.currentDebugToken()
let storedLegacyValue = UserDefaults.standard.string(forKey: legacyKey)
XCTAssertEqual(
storedLegacyValue,
generatedToken,
"Generated debug token was not stored under legacy key."
)
}

func testCurrentTokenWhenEnvironmentVariableSetAndTokenStored() {
UserDefaults.standard.set("stored token", forKey: kDebugTokenUserDefaultsKey)
let envToken = "env token"
Expand Down
Loading