Skip to content

Commit fb32eba

Browse files
committed
feat: Implement Cognito Credentials Provider
1 parent d452bba commit fb32eba

File tree

17 files changed

+2031
-2
lines changed

17 files changed

+2031
-2
lines changed

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Models/PackageManifestBuilder.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ struct PackageManifestBuilder {
111111
buildInternalAWSSTSDependencies(),
112112
buildInternalAWSSSODependencies(),
113113
buildInternalAWSSSOOIDCDependencies(),
114+
buildInternalAWSCognitoIdentityDependencies(),
114115
"",
115116
]
116117
return contents.joined(separator: .newline)
@@ -170,6 +171,10 @@ struct PackageManifestBuilder {
170171
buildInternalClientDependencies(name: "AWSSSOOIDC")
171172
}
172173

174+
private func buildInternalAWSCognitoIdentityDependencies() -> String {
175+
buildInternalClientDependencies(name: "AWSCognitoIdentity")
176+
}
177+
173178
private func buildInternalClientDependencies(name: String) -> String {
174179
let jsonFilePath = "Sources/Core/AWSSDKIdentity/InternalClients/Internal\(name)/Dependencies.json"
175180
let service = Service(name: name)

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/Package.Base.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ private var runtimeTargets: [Target] {
185185
"InternalAWSSTS",
186186
"InternalAWSSSO",
187187
"InternalAWSSSOOIDC",
188+
"InternalAWSCognitoIdentity",
188189
],
189190
path: "Sources/Core/AWSSDKIdentity/Sources/AWSSDKIdentity"
190191
),
@@ -203,6 +204,11 @@ private var runtimeTargets: [Target] {
203204
dependencies: internalAWSSSOOIDCDependencies,
204205
path: "Sources/Core/AWSSDKIdentity/InternalClients/InternalAWSSSOOIDC/Sources/InternalAWSSSOOIDC"
205206
),
207+
.target(
208+
name: "InternalAWSCognitoIdentity",
209+
dependencies: internalAWSCognitoIdentityDependencies,
210+
path: "Sources/Core/AWSSDKIdentity/InternalClients/InternalAWSCognitoIdentity/Sources/InternalAWSCognitoIdentity"
211+
),
206212
.target(
207213
name: "AWSSDKChecksums",
208214
dependencies: [

AWSSDKSwiftCLI/Tests/AWSSDKSwiftCLITests/Models/PackageManifestBuilderTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class PackageManifestBuilderTests: XCTestCase {
3333
let internalAWSSTSDependencies: [Target.Dependency] = []
3434
let internalAWSSSODependencies: [Target.Dependency] = []
3535
let internalAWSSSOOIDCDependencies: [Target.Dependency] = []
36+
let internalAWSCognitoIdentityDependencies: [Target.Dependency] = []
3637
3738
<contents of base package>
3839
"""
@@ -75,6 +76,7 @@ class PackageManifestBuilderTests: XCTestCase {
7576
let internalAWSSTSDependencies: [Target.Dependency] = []
7677
let internalAWSSSODependencies: [Target.Dependency] = []
7778
let internalAWSSSOOIDCDependencies: [Target.Dependency] = []
79+
let internalAWSCognitoIdentityDependencies: [Target.Dependency] = []
7880
7981
<contents of base package>
8082
"""

IntegrationTests/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private func integrationTestTarget(_ name: String) -> Target {
9494
case "AWSSTS":
9595
additionalDependencies = ["AWSIAM", "AWSCognitoIdentity"]
9696
case "AWSCognitoIdentity":
97-
additionalDependencies = ["AWSSTS"]
97+
additionalDependencies = ["AWSSTS", "AWSIAM"]
9898
default:
9999
break
100100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import XCTest
9+
import AWSSTS
10+
import AWSCognitoIdentity
11+
import AWSSDKIdentity
12+
import ClientRuntime
13+
import AWSIAM
14+
15+
/// Tests CognitoAWSCredentialIdentityResolver using STS::getCallerIdentity.
16+
class CognitoAWSCredentialIdentityResolverTests: XCTestCase {
17+
private let region = "us-west-2"
18+
19+
private var cognitoIdentityClient: CognitoIdentityClient!
20+
private var iamClient: IAMClient!
21+
22+
private let identityPoolName = "aws-cognito-integration-test-\(UUID().uuidString.split(separator: "-").first!.lowercased())"
23+
private var identityPoolId: String!
24+
private var roleName: String!
25+
26+
override func setUp() async throws {
27+
try await super.setUp()
28+
29+
// CognitoIdentity client for creating & deleting identity pool
30+
cognitoIdentityClient = try CognitoIdentityClient(region: region)
31+
iamClient = try IAMClient(region: "us-east-1")
32+
33+
// Create identity pool that allows unauthenticated identities
34+
identityPoolId = try await cognitoIdentityClient.createIdentityPool(
35+
input: CreateIdentityPoolInput(
36+
allowUnauthenticatedIdentities: true,
37+
identityPoolName: identityPoolName
38+
)
39+
).identityPoolId
40+
41+
// Create an IAM role for unauthenticated users
42+
roleName = "CognitoUnauth_\(identityPoolName)"
43+
let trustPolicy = """
44+
{
45+
"Version": "2012-10-17",
46+
"Statement": [{
47+
"Effect": "Allow",
48+
"Principal": {"Federated": "cognito-identity.amazonaws.com"},
49+
"Action": "sts:AssumeRoleWithWebIdentity",
50+
"Condition": {
51+
"StringEquals": {"cognito-identity.amazonaws.com:aud": "\(identityPoolId!)"},
52+
"ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "unauthenticated"}
53+
}
54+
}]
55+
}
56+
"""
57+
58+
let createRoleInput = CreateRoleInput(
59+
assumeRolePolicyDocument: trustPolicy,
60+
roleName: roleName
61+
)
62+
let createRoleResponse = try await iamClient.createRole(input: createRoleInput)
63+
64+
try await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds
65+
66+
// Assign the role to the identity pool
67+
_ = try await cognitoIdentityClient.setIdentityPoolRoles(
68+
input: SetIdentityPoolRolesInput(
69+
identityPoolId: identityPoolId,
70+
roles: ["unauthenticated": createRoleResponse.role!.arn!]
71+
)
72+
)
73+
}
74+
75+
override func tearDown() async throws {
76+
_ = try? await cognitoIdentityClient.deleteIdentityPool(
77+
input: DeleteIdentityPoolInput(identityPoolId: identityPoolId)
78+
)
79+
80+
_ = try? await iamClient.deleteRole(
81+
input: DeleteRoleInput(roleName: roleName)
82+
)
83+
84+
try await super.tearDown()
85+
}
86+
87+
/// Confirm CognitoAWSCredentialIdentityResolver works by calling STS getCallerIdentity
88+
func testGetCallerIdentityWithCognitoCredentials() async throws {
89+
// Create CognitoAWSCredentialIdentityResolver
90+
let cognitoCredentialResolver = try CognitoAWSCredentialIdentityResolver(
91+
identityPoolId: identityPoolId,
92+
cognitoPoolRegion: region // us-west-2
93+
)
94+
95+
// Configure STS client with Cognito credentials
96+
let cognitoStsConfig = try await STSClient.STSClientConfiguration(
97+
awsCredentialIdentityResolver: cognitoCredentialResolver,
98+
region: "us-east-1" // different from cognito pool region
99+
)
100+
let cognitoStsClient = STSClient(config: cognitoStsConfig)
101+
let response = try await cognitoStsClient.getCallerIdentity(
102+
input: GetCallerIdentityInput()
103+
)
104+
105+
let account = try XCTUnwrap(response.account)
106+
let userId = try XCTUnwrap(response.userId)
107+
let arn = try XCTUnwrap(response.arn)
108+
109+
XCTAssertNotEqual(account, "")
110+
XCTAssertNotEqual(userId, "")
111+
XCTAssertTrue(arn.contains(roleName))
112+
}
113+
}

Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ let serviceTargets: [String: [Target.Dependency]] = [
441441
let internalAWSSTSDependencies: [Target.Dependency] = [.AWSClientRuntime, .AWSSDKChecksums, .AWSSDKHTTPAuth, .ClientRuntime, .Smithy, .SmithyFormURL, .SmithyHTTPAPI, .SmithyHTTPAuthAPI, .SmithyIdentity, .SmithyReadWrite, .SmithyRetries, .SmithyRetriesAPI, .SmithyTimestamps, .SmithyXML]
442442
let internalAWSSSODependencies: [Target.Dependency] = [.AWSClientRuntime, .AWSSDKChecksums, .AWSSDKHTTPAuth, .ClientRuntime, .Smithy, .SmithyHTTPAPI, .SmithyHTTPAuthAPI, .SmithyIdentity, .SmithyJSON, .SmithyReadWrite, .SmithyRetries, .SmithyRetriesAPI]
443443
let internalAWSSSOOIDCDependencies: [Target.Dependency] = [.AWSClientRuntime, .AWSSDKChecksums, .AWSSDKHTTPAuth, .ClientRuntime, .Smithy, .SmithyHTTPAPI, .SmithyHTTPAuthAPI, .SmithyIdentity, .SmithyJSON, .SmithyReadWrite, .SmithyRetries, .SmithyRetriesAPI]
444+
let internalAWSCognitoIdentityDependencies: [Target.Dependency] = [.AWSClientRuntime, .AWSSDKChecksums, .AWSSDKHTTPAuth, .ClientRuntime, .Smithy, .SmithyHTTPAPI, .SmithyHTTPAuthAPI, .SmithyIdentity, .SmithyJSON, .SmithyReadWrite, .SmithyRetries, .SmithyRetriesAPI, .SmithyTimestamps]
444445

445446
// MARK: - Static Content
446447

@@ -629,6 +630,7 @@ private var runtimeTargets: [Target] {
629630
"InternalAWSSTS",
630631
"InternalAWSSSO",
631632
"InternalAWSSSOOIDC",
633+
"InternalAWSCognitoIdentity",
632634
],
633635
path: "Sources/Core/AWSSDKIdentity/Sources/AWSSDKIdentity"
634636
),
@@ -647,6 +649,11 @@ private var runtimeTargets: [Target] {
647649
dependencies: internalAWSSSOOIDCDependencies,
648650
path: "Sources/Core/AWSSDKIdentity/InternalClients/InternalAWSSSOOIDC/Sources/InternalAWSSSOOIDC"
649651
),
652+
.target(
653+
name: "InternalAWSCognitoIdentity",
654+
dependencies: internalAWSCognitoIdentityDependencies,
655+
path: "Sources/Core/AWSSDKIdentity/InternalClients/InternalAWSCognitoIdentity/Sources/InternalAWSCognitoIdentity"
656+
),
650657
.target(
651658
name: "AWSSDKChecksums",
652659
dependencies: [
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
"AWSClientRuntime",
3+
"AWSSDKChecksums",
4+
"AWSSDKHTTPAuth",
5+
"ClientRuntime",
6+
"Smithy",
7+
"SmithyHTTPAPI",
8+
"SmithyHTTPAuthAPI",
9+
"SmithyIdentity",
10+
"SmithyJSON",
11+
"SmithyReadWrite",
12+
"SmithyRetries",
13+
"SmithyRetriesAPI",
14+
"SmithyTestUtil",
15+
"SmithyTimestamps"
16+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// Code generated by smithy-swift-codegen. DO NOT EDIT!
9+
10+
import class Smithy.Context
11+
import enum Smithy.ClientError
12+
import enum SmithyHTTPAuthAPI.SigningPropertyKeys
13+
import protocol SmithyHTTPAuthAPI.AuthSchemeResolver
14+
import protocol SmithyHTTPAuthAPI.AuthSchemeResolverParameters
15+
import struct SmithyHTTPAuthAPI.AuthOption
16+
17+
package struct CognitoIdentityAuthSchemeResolverParameters: SmithyHTTPAuthAPI.AuthSchemeResolverParameters {
18+
public let authSchemePreference: [String]?
19+
public let operation: Swift.String
20+
// Region is used for SigV4 auth scheme
21+
public let region: Swift.String?
22+
}
23+
24+
package protocol CognitoIdentityAuthSchemeResolver: SmithyHTTPAuthAPI.AuthSchemeResolver {
25+
// Intentionally empty.
26+
// This is the parent protocol that all auth scheme resolver implementations of
27+
// the service CognitoIdentity must conform to.
28+
}
29+
30+
package struct DefaultCognitoIdentityAuthSchemeResolver: CognitoIdentityAuthSchemeResolver {
31+
32+
public func resolveAuthScheme(params: SmithyHTTPAuthAPI.AuthSchemeResolverParameters) throws -> [SmithyHTTPAuthAPI.AuthOption] {
33+
var validAuthOptions = [SmithyHTTPAuthAPI.AuthOption]()
34+
guard let serviceParams = params as? CognitoIdentityAuthSchemeResolverParameters else {
35+
throw Smithy.ClientError.authError("Service specific auth scheme parameters type must be passed to auth scheme resolver.")
36+
}
37+
switch serviceParams.operation {
38+
case "getCredentialsForIdentity":
39+
validAuthOptions.append(SmithyHTTPAuthAPI.AuthOption(schemeID: "smithy.api#noAuth"))
40+
case "getId":
41+
validAuthOptions.append(SmithyHTTPAuthAPI.AuthOption(schemeID: "smithy.api#noAuth"))
42+
default:
43+
var sigv4Option = SmithyHTTPAuthAPI.AuthOption(schemeID: "aws.auth#sigv4")
44+
sigv4Option.signingProperties.set(key: SmithyHTTPAuthAPI.SigningPropertyKeys.signingName, value: "cognito-identity")
45+
guard let region = serviceParams.region else {
46+
throw Smithy.ClientError.authError("Missing region in auth scheme parameters for SigV4 auth scheme.")
47+
}
48+
sigv4Option.signingProperties.set(key: SmithyHTTPAuthAPI.SigningPropertyKeys.signingRegion, value: region)
49+
validAuthOptions.append(sigv4Option)
50+
}
51+
return self.reprioritizeAuthOptions(authSchemePreference: serviceParams.authSchemePreference, authOptions: validAuthOptions)
52+
}
53+
54+
public func constructParameters(context: Smithy.Context) throws -> SmithyHTTPAuthAPI.AuthSchemeResolverParameters {
55+
guard let opName = context.getOperation() else {
56+
throw Smithy.ClientError.dataNotFound("Operation name not configured in middleware context for auth scheme resolver params construction.")
57+
}
58+
let authSchemePreference = context.getAuthSchemePreference()
59+
let opRegion = context.getRegion()
60+
return CognitoIdentityAuthSchemeResolverParameters(authSchemePreference: authSchemePreference, operation: opName, region: opRegion)
61+
}
62+
}

0 commit comments

Comments
 (0)