Skip to content

Commit 7d06d90

Browse files
authored
chore: kickoff release
2 parents c5a33ca + 6af59d4 commit 7d06d90

File tree

2 files changed

+162
-1
lines changed

2 files changed

+162
-1
lines changed

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AWSCognitoAuthCredentialStore.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@ struct AWSCognitoAuthCredentialStore {
6262
saveStoredAccessGroup()
6363

6464
if !userDefaults.bool(forKey: isKeychainConfiguredKey) {
65-
try? clearAllCredentials()
65+
// We can't reliably clear credentials if the Keychain has a shared access group.
66+
// This is because each app/extension has its own UserDefaults.
67+
// If a user authenticates in an app, the app or extension that shares the keychain would clear the shared credentials.
68+
// We must only clear credentials if a shared Keychain is not being used.
69+
if accessGroup == nil {
70+
try? clearAllCredentials() // clear if not using shared keychain
71+
}
6672
userDefaults.set(true, forKey: isKeychainConfiguredKey)
6773
}
6874

AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/CredentialStore/CredentialStoreConfigurationTests.swift

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,4 +571,159 @@ class CredentialStoreConfigurationTests: AWSAuthBaseTest {
571571
XCTAssertNotNil(retrievedCredentials)
572572
XCTAssertNotEqual(retrievedCredentials, awsCredentials)
573573
}
574+
575+
/// Test that shared keychain credentials are NOT cleared on fresh install when using access group
576+
///
577+
/// - Given: A user has credentials stored in shared keychain
578+
/// - When: The credential store is initialized with fresh UserDefaults but same access group
579+
/// - Then: The shared keychain credentials should NOT be cleared
580+
///
581+
func testSharedKeychainCredentialsNotClearedOnFreshInstall() {
582+
// Given: Save credentials to shared keychain
583+
let identityId = "identityId"
584+
let awsCredentials = AuthAWSCognitoCredentials.testData
585+
let initialCognitoCredentials = AmplifyCredentials.userPoolAndIdentityPool(
586+
signedInData: .testData,
587+
identityID: identityId,
588+
credentials: awsCredentials
589+
)
590+
let authConfig = AuthConfiguration.userPoolsAndIdentityPools(
591+
Defaults.makeDefaultUserPoolConfigData(),
592+
Defaults.makeIdentityConfigData()
593+
)
594+
595+
#if os(watchOS)
596+
let accessGroup = keychainAccessGroupWatch
597+
#else
598+
let accessGroup = keychainAccessGroup
599+
#endif
600+
601+
let credentialStore = AWSCognitoAuthCredentialStore(
602+
authConfiguration: authConfig,
603+
accessGroup: accessGroup
604+
)
605+
606+
do {
607+
try credentialStore.saveCredential(initialCognitoCredentials)
608+
} catch {
609+
XCTFail("Unable to save credentials")
610+
}
611+
612+
// Verify credentials are saved
613+
guard let savedCredentials = try? credentialStore.retrieveCredential() else {
614+
XCTFail("Unable to retrieve saved credentials")
615+
return
616+
}
617+
XCTAssertNotNil(savedCredentials)
618+
619+
// When: Simulate fresh install by clearing UserDefaults flag
620+
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.isKeychainConfigured")
621+
622+
// Initialize new credential store with same access group (simulates app extension scenario)
623+
let newCredentialStore = AWSCognitoAuthCredentialStore(
624+
authConfiguration: authConfig,
625+
accessGroup: accessGroup
626+
)
627+
628+
// Then: Shared keychain credentials should NOT be cleared
629+
guard let retrievedCredentials = try? newCredentialStore.retrieveCredential(),
630+
case .userPoolAndIdentityPool(
631+
let retrievedTokens,
632+
let retrievedIdentityID,
633+
let retrievedAWSCredentials
634+
) = retrievedCredentials else {
635+
XCTFail("Shared keychain credentials should not be cleared")
636+
return
637+
}
638+
639+
XCTAssertNotNil(retrievedCredentials)
640+
XCTAssertNotNil(retrievedTokens)
641+
XCTAssertNotNil(retrievedIdentityID)
642+
XCTAssertNotNil(retrievedAWSCredentials)
643+
XCTAssertEqual(retrievedIdentityID, identityId)
644+
XCTAssertEqual(retrievedAWSCredentials, awsCredentials)
645+
}
646+
647+
/// Test that non-shared keychain credentials ARE cleared on fresh install
648+
///
649+
/// - Given: A user has credentials stored in non-shared keychain
650+
/// - When: The credential store is initialized with fresh UserDefaults and no access group
651+
/// - Then: The keychain credentials should be cleared
652+
///
653+
func testNonSharedKeychainCredentialsClearedOnFreshInstall() {
654+
// Given: Save credentials to non-shared keychain
655+
let identityId = "identityId"
656+
let awsCredentials = AuthAWSCognitoCredentials.testData
657+
let initialCognitoCredentials = AmplifyCredentials.userPoolAndIdentityPool(
658+
signedInData: .testData,
659+
identityID: identityId,
660+
credentials: awsCredentials
661+
)
662+
let authConfig = AuthConfiguration.userPoolsAndIdentityPools(
663+
Defaults.makeDefaultUserPoolConfigData(),
664+
Defaults.makeIdentityConfigData()
665+
)
666+
667+
let credentialStore = AWSCognitoAuthCredentialStore(authConfiguration: authConfig)
668+
669+
do {
670+
try credentialStore.saveCredential(initialCognitoCredentials)
671+
} catch {
672+
XCTFail("Unable to save credentials")
673+
}
674+
675+
// Verify credentials are saved
676+
guard let savedCredentials = try? credentialStore.retrieveCredential() else {
677+
XCTFail("Unable to retrieve saved credentials")
678+
return
679+
}
680+
XCTAssertNotNil(savedCredentials)
681+
682+
// When: Simulate fresh install by clearing UserDefaults flag
683+
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.isKeychainConfigured")
684+
685+
// Initialize new credential store without access group
686+
let newCredentialStore = AWSCognitoAuthCredentialStore(authConfiguration: authConfig)
687+
688+
// Then: Non-shared keychain credentials should be cleared
689+
let retrievedCredentials = try? newCredentialStore.retrieveCredential()
690+
XCTAssertNil(retrievedCredentials, "Non-shared keychain credentials should be cleared on fresh install")
691+
}
692+
693+
/// Test that UserDefaults flag is properly set regardless of access group usage
694+
///
695+
/// - Given: Fresh UserDefaults state
696+
/// - When: Credential store is initialized with or without access group
697+
/// - Then: UserDefaults flag should be set in both cases
698+
///
699+
func testUserDefaultsFlagSetRegardlessOfAccessGroup() {
700+
let authConfig = AuthConfiguration.userPoolsAndIdentityPools(
701+
Defaults.makeDefaultUserPoolConfigData(),
702+
Defaults.makeIdentityConfigData()
703+
)
704+
let userDefaultsKey = "amplify_secure_storage_scopes.awsCognitoAuthPlugin.isKeychainConfigured"
705+
706+
// Test without access group
707+
UserDefaults.standard.removeObject(forKey: userDefaultsKey)
708+
XCTAssertFalse(UserDefaults.standard.bool(forKey: userDefaultsKey))
709+
710+
_ = AWSCognitoAuthCredentialStore(authConfiguration: authConfig)
711+
XCTAssertTrue(UserDefaults.standard.bool(forKey: userDefaultsKey))
712+
713+
// Test with access group
714+
UserDefaults.standard.removeObject(forKey: userDefaultsKey)
715+
XCTAssertFalse(UserDefaults.standard.bool(forKey: userDefaultsKey))
716+
717+
#if os(watchOS)
718+
let accessGroup = keychainAccessGroupWatch
719+
#else
720+
let accessGroup = keychainAccessGroup
721+
#endif
722+
723+
_ = AWSCognitoAuthCredentialStore(
724+
authConfiguration: authConfig,
725+
accessGroup: accessGroup
726+
)
727+
XCTAssertTrue(UserDefaults.standard.bool(forKey: userDefaultsKey))
728+
}
574729
}

0 commit comments

Comments
 (0)