Skip to content

Commit dae5171

Browse files
authored
chore: kickoff release
2 parents 67be15d + 93310c9 commit dae5171

6 files changed

Lines changed: 80 additions & 90 deletions

File tree

.github/workflows/dependency-review.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ jobs:
2020
persist-credentials: false
2121

2222
- name: Dependency Review
23-
uses: actions/dependency-review-action@7d90b4f05fea31dde1c4a1fb3fa787e197ea93ab # v3.0.7
23+
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
2424
with:
2525
config-file: amazon-ospo/dependency-review-config/default/dependency-review-config.yml@main

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

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ struct AWSCognitoAuthCredentialStore {
2121

2222
// User defaults constants
2323
private let userDefaultsNameSpace = "amplify_secure_storage_scopes.awsCognitoAuthPlugin"
24-
/// This UserDefault Key is used to check if Keychain already has items stored on a fresh install
25-
/// If this flag doesn't exist, previous keychain values for Amplify would be wiped out
26-
private var isKeychainConfiguredKey: String {
27-
"\(userDefaultsNameSpace).isKeychainConfigured"
28-
}
2924
/// This UserDefaults Key is use to retrieve the stored access group to determine
3025
/// which access group the migration should happen from
3126
/// If none is found, the unshared service is used for migration and all items
@@ -67,16 +62,16 @@ struct AWSCognitoAuthCredentialStore {
6762

6863
saveStoredAccessGroup()
6964

70-
if !userDefaults.bool(forKey: isKeychainConfiguredKey) {
71-
// We can't reliably clear credentials if the Keychain has a shared access group.
72-
// This is because each app/extension has its own UserDefaults.
73-
// If a user authenticates in an app, the app or extension that shares the keychain would clear the shared credentials.
74-
// We must only clear credentials if a shared Keychain is not being used.
75-
if accessGroup == nil {
76-
try? clearAllCredentials() // clear if not using shared keychain
77-
}
78-
userDefaults.set(true, forKey: isKeychainConfiguredKey)
79-
}
65+
// NOTE: We intentionally do NOT clear keychain credentials on app reinstall.
66+
// Previously, this code checked a UserDefaults flag (isKeychainConfiguredKey) to detect
67+
// fresh installs and clear orphaned keychain items. However, this approach was unreliable
68+
// because UserDefaults can return false during iOS prewarming (background app launch after
69+
// device reboot) when protected data is not yet available. This caused valid credentials
70+
// to be incorrectly cleared, resulting in random user logouts.
71+
//
72+
// Keychain items persisting across app reinstalls is iOS's default behavior. Any stale
73+
// credentials will naturally fail authentication and trigger a proper sign-out flow.
74+
// See: https://github.com/aws-amplify/amplify-swift/issues/3972
8075

8176
restoreCredentialsOnConfigurationChanges(currentAuthConfig: authConfiguration)
8277
// Save the current configuration

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

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,44 @@
77

88
import XCTest
99
@testable import AWSCognitoAuthPlugin
10+
@_spi(KeychainStore) import AWSPluginsCore
1011

1112
class CredentialStoreConfigurationTests: AWSAuthBaseTest {
1213

14+
private let service = "com.amplify.awsCognitoAuthPlugin"
15+
private let sharedService = "com.amplify.awsCognitoAuthPluginShared"
16+
1317
override func setUp() async throws {
1418
try await super.setUp()
15-
AuthSessionHelper.clearSession()
19+
clearAllKeychains()
20+
// Clear access group UserDefaults to ensure clean state for migration tests
21+
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.accessGroup")
1622
}
1723

1824
override func tearDown() async throws {
1925
try await super.tearDown()
20-
AuthSessionHelper.clearSession()
26+
clearAllKeychains()
27+
// Clear access group UserDefaults
28+
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.accessGroup")
29+
}
30+
31+
/// Clears all keychain items (both shared and non-shared) to ensure clean test state
32+
private func clearAllKeychains() {
33+
// Clear non-shared keychain
34+
let nonSharedKeychain = KeychainStore(service: service)
35+
try? nonSharedKeychain._removeAll()
36+
37+
// Clear shared keychains for all access groups used in tests
38+
#if os(watchOS)
39+
let accessGroups = [keychainAccessGroupWatch, keychainAccessGroupWatch2]
40+
#else
41+
let accessGroups = [keychainAccessGroup, keychainAccessGroup2]
42+
#endif
43+
44+
for accessGroup in accessGroups {
45+
let sharedKeychain = KeychainStore(service: sharedService, accessGroup: accessGroup)
46+
try? sharedKeychain._removeAll()
47+
}
2148
}
2249

2350
/// Test successful migration of credentials when auth configuration changes
@@ -229,12 +256,11 @@ class CredentialStoreConfigurationTests: AWSAuthBaseTest {
229256
}
230257

231258
// When configuration don't change changed
232-
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.isKeychainConfigured")
233259
let newCredentialStore = AWSCognitoAuthCredentialStore(authConfiguration: initialAuthConfig)
234260

235261
// Then credentials should be nil
236262
let credentials = try? newCredentialStore.retrieveCredential()
237-
XCTAssertNil(credentials)
263+
XCTAssertNotNil(credentials)
238264
}
239265

240266
/// Test migrating to a shared access group keeps credentials
@@ -679,51 +705,11 @@ class CredentialStoreConfigurationTests: AWSAuthBaseTest {
679705
}
680706
XCTAssertNotNil(savedCredentials)
681707

682-
// When: Simulate fresh install by clearing UserDefaults flag
683-
UserDefaults.standard.removeObject(forKey: "amplify_secure_storage_scopes.awsCognitoAuthPlugin.isKeychainConfigured")
684-
685708
// Initialize new credential store without access group
686709
let newCredentialStore = AWSCognitoAuthCredentialStore(authConfiguration: authConfig)
687710

688711
// Then: Non-shared keychain credentials should be cleared
689712
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))
713+
XCTAssertNotNil(retrievedCredentials, "Non-shared keychain credentials should NOT be cleared on fresh install")
728714
}
729715
}

AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/Persistence/LogFile.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ final class LogFile {
3030
self.sizeLimitInBytes = sizeLimitInBytes
3131
self.handle = try FileHandle(forUpdating: fileURL)
3232
if #available(macOS 12.0, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
33-
self.count = try self.handle.offset()
33+
self.count = try handle.offset()
3434
} else {
3535
self.count = handle.offsetInFile
3636
}
@@ -68,7 +68,7 @@ final class LogFile {
6868
/// Data to the underlying log file.
6969
func write(data: Data) throws {
7070
if #available(macOS 12.0, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
71-
try self.handle.write(contentsOf: data)
71+
try handle.write(contentsOf: data)
7272
} else {
7373
handle.write(data)
7474
}

Gemfile.lock

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,24 @@ GEM
3030
json (>= 1.5.1)
3131
artifactory (3.0.15)
3232
atomos (0.1.3)
33-
aws-eventstream (1.2.0)
34-
aws-partitions (1.709.0)
35-
aws-sdk-core (3.170.0)
36-
aws-eventstream (~> 1, >= 1.0.2)
37-
aws-partitions (~> 1, >= 1.651.0)
38-
aws-sigv4 (~> 1.5)
33+
aws-eventstream (1.4.0)
34+
aws-partitions (1.1196.0)
35+
aws-sdk-core (3.240.0)
36+
aws-eventstream (~> 1, >= 1.3.0)
37+
aws-partitions (~> 1, >= 1.992.0)
38+
aws-sigv4 (~> 1.9)
39+
base64
40+
bigdecimal
3941
jmespath (~> 1, >= 1.6.1)
40-
aws-sdk-kms (1.62.0)
41-
aws-sdk-core (~> 3, >= 3.165.0)
42-
aws-sigv4 (~> 1.1)
43-
aws-sdk-s3 (1.119.0)
44-
aws-sdk-core (~> 3, >= 3.165.0)
42+
logger
43+
aws-sdk-kms (1.118.0)
44+
aws-sdk-core (~> 3, >= 3.239.1)
45+
aws-sigv4 (~> 1.5)
46+
aws-sdk-s3 (1.208.0)
47+
aws-sdk-core (~> 3, >= 3.234.0)
4548
aws-sdk-kms (~> 1)
46-
aws-sigv4 (~> 1.4)
47-
aws-sigv4 (1.5.2)
49+
aws-sigv4 (~> 1.5)
50+
aws-sigv4 (1.12.1)
4851
aws-eventstream (~> 1, >= 1.0.2)
4952
babosa (1.0.4)
5053
base64 (0.2.0)
@@ -234,6 +237,7 @@ GEM
234237
jwt (2.9.1)
235238
base64
236239
liferaft (0.0.6)
240+
logger (1.7.0)
237241
mini_magick (4.12.0)
238242
mini_mime (1.1.5)
239243
mini_portile2 (2.8.7)

canaries/example/Gemfile.lock

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,28 @@ GEM
77
public_suffix (>= 2.0.2, < 7.0)
88
artifactory (3.0.17)
99
atomos (0.1.3)
10-
aws-eventstream (1.2.0)
11-
aws-partitions (1.575.0)
12-
aws-sdk-core (3.130.0)
13-
aws-eventstream (~> 1, >= 1.0.2)
14-
aws-partitions (~> 1, >= 1.525.0)
15-
aws-sigv4 (~> 1.1)
16-
jmespath (~> 1.0)
17-
aws-sdk-kms (1.55.0)
18-
aws-sdk-core (~> 3, >= 3.127.0)
19-
aws-sigv4 (~> 1.1)
20-
aws-sdk-s3 (1.113.0)
21-
aws-sdk-core (~> 3, >= 3.127.0)
10+
aws-eventstream (1.4.0)
11+
aws-partitions (1.1197.0)
12+
aws-sdk-core (3.240.0)
13+
aws-eventstream (~> 1, >= 1.3.0)
14+
aws-partitions (~> 1, >= 1.992.0)
15+
aws-sigv4 (~> 1.9)
16+
base64
17+
bigdecimal
18+
jmespath (~> 1, >= 1.6.1)
19+
logger
20+
aws-sdk-kms (1.118.0)
21+
aws-sdk-core (~> 3, >= 3.239.1)
22+
aws-sigv4 (~> 1.5)
23+
aws-sdk-s3 (1.208.0)
24+
aws-sdk-core (~> 3, >= 3.234.0)
2225
aws-sdk-kms (~> 1)
23-
aws-sigv4 (~> 1.4)
24-
aws-sigv4 (1.4.0)
26+
aws-sigv4 (~> 1.5)
27+
aws-sigv4 (1.12.1)
2528
aws-eventstream (~> 1, >= 1.0.2)
2629
babosa (1.0.4)
2730
base64 (0.2.0)
31+
bigdecimal (4.0.1)
2832
claide (1.1.0)
2933
colored (1.2)
3034
colored2 (3.1.2)
@@ -155,6 +159,7 @@ GEM
155159
json (2.6.1)
156160
jwt (2.10.2)
157161
base64
162+
logger (1.7.0)
158163
mini_magick (4.13.2)
159164
mini_mime (1.1.5)
160165
multi_json (1.15.0)

0 commit comments

Comments
 (0)