@@ -10,38 +10,64 @@ import struct Smithy.Attributes
10
10
import ClientRuntime
11
11
import class Foundation. ProcessInfo
12
12
import enum Smithy. ClientError
13
- import class Foundation . NSLock
13
+
14
14
import struct Foundation. Date
15
+ @_spi ( FileBasedConfig) import AWSSDKCommon
15
16
16
17
/// A credential identity resolver that resolves credentials using AWS Cognito Identity.
17
- public struct CognitoAWSCredentialIdentityResolver : AWSCredentialIdentityResolver {
18
+ public actor CognitoAWSCredentialIdentityResolver : AWSCredentialIdentityResolver {
18
19
private let identityPoolId : String ?
19
20
private let identityId : String ?
20
21
private let accountId : String ?
21
22
private var logins : [ String : String ] ?
22
23
private let customRoleArn : String ?
23
24
private let cognitoPoolRegion : String
25
+ private let configFilePath : String ?
26
+ private let credentialsFilePath : String ?
27
+ private let profileName : String ?
24
28
25
29
private var cachedIdentityId : String ?
26
30
private var cachedCredentials : AWSCredentialIdentity ?
27
31
private var cachedLogins : [ String : String ] ?
28
- private let refreshLock = NSLock ( )
32
+ // Actor provides thread safety, no need for manual locking
29
33
30
34
public init (
31
35
identityPoolId: String ? = nil ,
32
36
identityId: String ? = nil ,
33
37
accountId: String ? = nil ,
34
38
logins: [ String : String ] ? = nil ,
35
39
customRoleArn: String ? = nil ,
36
- cognitoPoolRegion: String ? = nil
40
+ cognitoPoolRegion: String ? = nil ,
41
+ configFilePath: String ? = nil ,
42
+ credentialsFilePath: String ? = nil ,
43
+ profileName: String ? = nil
37
44
) throws {
38
45
let env = ProcessInfo . processInfo. environment
39
46
40
- // Resolve from environment variables if not provided
41
- let resolvedIdentityPoolId = identityPoolId ?? env [ " AWS_COGNITO_IDENTITY_POOL_ID " ]
42
- let resolvedIdentityId = identityId ?? env [ " AWS_COGNITO_IDENTITY_ID " ]
43
- let resolvedAccountId = accountId ?? env [ " AWS_ACCOUNT_ID " ]
44
- let resolvedCustomRoleArn = customRoleArn ?? env [ " AWS_COGNITO_CUSTOM_ROLE_ARN " ]
47
+ self . configFilePath = configFilePath
48
+ self . credentialsFilePath = credentialsFilePath
49
+ self . profileName = profileName
50
+
51
+ // Load config files
52
+ let fileBasedConfig = try CRTFileBasedConfiguration (
53
+ configFilePath: configFilePath,
54
+ credentialsFilePath: credentialsFilePath
55
+ )
56
+ let resolvedProfileName = profileName ?? env [ " AWS_PROFILE " ] ?? " default "
57
+
58
+ // Resolve configuration using helper functions
59
+ let resolvedIdentityPoolId = Self . resolveOptionalField (
60
+ identityPoolId, " AWS_COGNITO_IDENTITY_POOL_ID " , " cognito_identity_pool_id " , fileBasedConfig, resolvedProfileName
61
+ )
62
+ let resolvedIdentityId = Self . resolveOptionalField (
63
+ identityId, " AWS_COGNITO_IDENTITY_ID " , " cognito_identity_id " , fileBasedConfig, resolvedProfileName
64
+ )
65
+ let resolvedAccountId = Self . resolveOptionalField (
66
+ accountId, " AWS_ACCOUNT_ID " , " account_id " , fileBasedConfig, resolvedProfileName
67
+ )
68
+ let resolvedCustomRoleArn = Self . resolveOptionalField (
69
+ customRoleArn, " AWS_COGNITO_CUSTOM_ROLE_ARN " , " cognito_custom_role_arn " , fileBasedConfig, resolvedProfileName
70
+ )
45
71
46
72
guard resolvedIdentityPoolId != nil || resolvedIdentityId != nil else {
47
73
throw ClientError . invalidValue ( " Either identityPoolId or identityId must be provided " )
@@ -56,18 +82,19 @@ public struct CognitoAWSCredentialIdentityResolver: AWSCredentialIdentityResolve
56
82
self . logins = logins
57
83
self . customRoleArn = resolvedCustomRoleArn
58
84
59
- // Priority: cognitoPoolRegion parameter -> AWS_COGNITO_POOL_REGION -> AWS_REGION
60
- self . cognitoPoolRegion = try cognitoPoolRegion
61
- ?? env [ " AWS_COGNITO_POOL_REGION " ]
62
- ?? env [ " AWS_REGION " ]
63
- ?? { throw ClientError . dataNotFound ( " AWS region not configured " ) } ( )
85
+ // Resolve region with fallback from cognito_pool_region to region
86
+ self . cognitoPoolRegion = try Self . resolveOptionalField (
87
+ cognitoPoolRegion, " AWS_COGNITO_POOL_REGION " , " cognito_pool_region " , fileBasedConfig, resolvedProfileName
88
+ ) ?? Self . resolveOptionalField (
89
+ nil , " AWS_REGION " , " region " , fileBasedConfig, resolvedProfileName
90
+ ) ?? { throw ClientError . dataNotFound ( " AWS region not configured " ) } ( )
64
91
65
92
self . cachedIdentityId = nil
66
93
self . cachedCredentials = nil
67
- self . cachedLogins = nil
94
+ self . cachedLogins = logins
68
95
}
69
96
70
- public mutating func getIdentity( identityProperties: Attributes ? ) async throws -> AWSCredentialIdentity {
97
+ public func getIdentity( identityProperties: Attributes ? ) async throws -> AWSCredentialIdentity {
71
98
guard let identityProperties, let internalCognitoClient = identityProperties. get (
72
99
key: InternalClientKeys . internalCognitoIdentityClientKey
73
100
) else {
@@ -76,9 +103,6 @@ public struct CognitoAWSCredentialIdentityResolver: AWSCredentialIdentityResolve
76
103
+ " Missing IdentityProvidingCognitoIdentityClient in identity properties. "
77
104
)
78
105
}
79
-
80
- refreshLock. lock ( )
81
- defer { refreshLock. unlock ( ) }
82
106
83
107
// Check if cached credentials are still valid
84
108
if let cached = cachedCredentials,
@@ -101,16 +125,12 @@ public struct CognitoAWSCredentialIdentityResolver: AWSCredentialIdentityResolve
101
125
return credentials
102
126
}
103
127
104
- private mutating func resolveIdentityId( using client: IdentityProvidingCognitoIdentityClient ) async throws -> String {
128
+ private func resolveIdentityId( using client: IdentityProvidingCognitoIdentityClient ) async throws -> String {
105
129
if let existingId = identityId {
106
130
return existingId
107
131
}
108
132
109
- // If logins changed, clear cached identity ID to force refresh
110
- if loginsChanged ( ) {
111
- cachedIdentityId = nil
112
- }
113
-
133
+ // Use cached identity ID if logins haven't changed
114
134
if let cached = cachedIdentityId, !loginsChanged( ) {
115
135
return cached
116
136
}
@@ -126,25 +146,47 @@ public struct CognitoAWSCredentialIdentityResolver: AWSCredentialIdentityResolve
126
146
)
127
147
128
148
cachedIdentityId = resolvedId
149
+ cachedLogins = logins
129
150
return resolvedId
130
151
}
131
152
132
153
private func loginsChanged( ) -> Bool {
133
- guard let cached = cachedLogins else {
134
- return logins != nil
154
+ // Both nil - no change
155
+ if cachedLogins == nil && logins == nil {
156
+ return false
135
157
}
136
158
137
- guard let current = logins else {
159
+ // One is nil, other isn't - changed
160
+ guard let cached = cachedLogins, let current = logins else {
138
161
return true
139
162
}
140
163
164
+ // Compare the dictionaries
141
165
return cached != current
142
166
}
143
167
144
- public mutating func updateLogins( _ newLogins: [ String : String ] ? ) {
168
+ public func updateLogins( _ newLogins: [ String : String ] ? ) {
145
169
logins = newLogins
146
170
// Clear cached data when logins change
147
171
cachedIdentityId = nil
148
172
cachedCredentials = nil
173
+ cachedLogins = nil
174
+ }
175
+
176
+ private static func resolveOptionalField(
177
+ _ configValue: String ? ,
178
+ _ envVarName: String ,
179
+ _ configFieldName: String ,
180
+ _ config: CRTFileBasedConfiguration ,
181
+ _ profileName: String
182
+ ) -> String ? {
183
+ FieldResolver (
184
+ configValue: configValue,
185
+ envVarName: envVarName,
186
+ configFieldName: configFieldName,
187
+ fileBasedConfig: config,
188
+ profileName: profileName,
189
+ converter: { String ( $0) }
190
+ ) . value
149
191
}
150
- }
192
+ }
0 commit comments