Skip to content

Commit 846d8a5

Browse files
committed
First commit
1 parent 6547fba commit 846d8a5

File tree

3 files changed

+354
-0
lines changed

3 files changed

+354
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// swift-tools-version:5.9
2+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// The swift-tools-version declares the minimum version of Swift required to
6+
// build this package.
7+
8+
import PackageDescription
9+
10+
let package = Package(
11+
name: "cognito-resolver",
12+
// Let Xcode know the minimum Apple platforms supported.
13+
platforms: [
14+
.macOS(.v11),
15+
.iOS(.v13)
16+
],
17+
dependencies: [
18+
// Dependencies declare other packages that this package depends on.
19+
.package(
20+
url: "https://github.com/awslabs/aws-sdk-swift",
21+
from: "1.0.0"
22+
),
23+
.package(
24+
url: "https://github.com/apple/swift-argument-parser.git",
25+
branch: "main"
26+
),
27+
],
28+
targets: [
29+
// Targets are the basic building blocks of a package, defining a module or a test suite.
30+
// Targets can depend on other targets in this package and products from dependencies.
31+
.executableTarget(
32+
name: "cognito-resolver",
33+
dependencies: [
34+
.product(name: "AWSCognitoIdentity", package: "aws-sdk-swift"),
35+
.product(name: "AWSIAM", package: "aws-sdk-swift"),
36+
.product(name: "AWSSTS", package: "aws-sdk-swift"),
37+
.product(name: "AWSS3", package: "aws-sdk-swift"),
38+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
39+
],
40+
path: "Sources"),
41+
]
42+
)
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// snippet-start:[swift.identity.cognito.imports]
5+
import AWSCognitoIdentity
6+
import AWSSDKIdentity
7+
import AWSSTS
8+
// snippet-end:[swift.identity.cognito.imports]
9+
import AWSIAM
10+
import AWSS3
11+
import Foundation
12+
13+
/// Contains the data and code for the main body of the example.
14+
class Example {
15+
let region: String
16+
17+
var cognitoIdentityClient: CognitoIdentityClient!
18+
var iamClient: IAMClient!
19+
var roleName: String
20+
21+
/// The name of the AWS Cognito Identity Pool to use.
22+
let identityPoolName: String
23+
/// The ID of the Identity Pool.
24+
var identityPoolID: String!
25+
26+
/// The name of the managed policy granting Amazon S3 permissions.
27+
let managedPolicyName: String
28+
/// The ARN of the managed policy granting S3 permissions.
29+
var managedPolicyArn: String?
30+
31+
/// Initialize the example.
32+
///
33+
/// - Parameter region: The AWS Region to operate in.
34+
///
35+
/// - Throws: Any AWS errors thrown by IAM or Cognito.
36+
///
37+
/// ^ Note: IAM must always use `us-east-1`, so it doesn't use the value
38+
/// of the `region` parameter.
39+
init(region: String) throws {
40+
self.region = region
41+
42+
self.identityPoolName = "cognito-resolver-example-\(UUID().uuidString.split(separator: "-").first!.lowercased())"
43+
cognitoIdentityClient = try CognitoIdentityClient(region: region)
44+
45+
self.roleName = "cognito-unauth-\(identityPoolName)"
46+
iamClient = try IAMClient(region: "us-east-1")
47+
48+
self.managedPolicyName = "cognito-policy-\(identityPoolName)"
49+
}
50+
51+
/// The body of the example.
52+
///
53+
/// - Throws: Errors from IAM, STS, or Cognito.
54+
func run() async throws {
55+
// Set up the cleanup function to run automatically when this object
56+
// is discarded. This way, we clean up AWS artifacts whether the run
57+
// is successful or an error occurs.
58+
59+
defer {
60+
blocking {
61+
await self.cleanup()
62+
}
63+
}
64+
65+
// Create an identity pool to use for this example.
66+
67+
print("Creating a Cognito identity pool named \(identityPoolName)...")
68+
identityPoolID = try await cognitoIdentityClient.createIdentityPool(
69+
input: CreateIdentityPoolInput(
70+
allowUnauthenticatedIdentities: true,
71+
identityPoolName: identityPoolName
72+
)
73+
).identityPoolId
74+
75+
// Create an IAM role for unauthenticated users.
76+
77+
let trustPolicy = """
78+
{
79+
"Version": "2012-10-17",
80+
"Statement": [{
81+
"Effect": "Allow",
82+
"Principal": {"Federated": "cognito-identity.amazonaws.com"},
83+
"Action": "sts:AssumeRoleWithWebIdentity",
84+
"Condition": {
85+
"StringEquals": {"cognito-identity.amazonaws.com:aud": "\(identityPoolID!)"},
86+
"ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "unauthenticated"}
87+
}
88+
}]
89+
}
90+
"""
91+
92+
print("Creating an IAM role named \(roleName)...")
93+
let createRoleInput = CreateRoleInput(
94+
assumeRolePolicyDocument: trustPolicy,
95+
roleName: roleName
96+
)
97+
let createRoleOutput = try await iamClient.createRole(input: createRoleInput)
98+
99+
guard let role = createRoleOutput.role else {
100+
print("*** No role returned by CreateRole!")
101+
return
102+
}
103+
104+
// Wait for the role to be available.
105+
106+
print("Waiting for the role to be available...")
107+
try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds
108+
109+
// Assign the role to the identity pool.
110+
111+
print("Setting the identity pool's roles...")
112+
_ = try await cognitoIdentityClient.setIdentityPoolRoles(
113+
input: SetIdentityPoolRolesInput(
114+
identityPoolId: identityPoolID,
115+
roles: ["unauthenticated": role.arn!]
116+
)
117+
)
118+
119+
//======================================================================
120+
// Resolve an identity using the Cognito credential identity resolver
121+
// with the AWS STS function getCallerIdentity(input:). This is done
122+
// by configuring the STS client to use the Cognito credentials
123+
// resolver.
124+
//======================================================================
125+
126+
// snippet-start:[swift.identity.cognito.resolve]
127+
// Create a Cognito credential resolver that uses the Cognito Identity
128+
// Pool created above.
129+
let cognitoCredentialResolver = try CognitoAWSCredentialIdentityResolver(
130+
identityPoolId: identityPoolID,
131+
identityPoolRegion: region
132+
)
133+
134+
// Create an AWS STS client that uses the new Cognito credential
135+
// resolver to do credential identity resolution.
136+
let cognitoSTSConfig = try await STSClient.STSClientConfiguration(
137+
awsCredentialIdentityResolver: cognitoCredentialResolver,
138+
region: "us-east-1"
139+
)
140+
let cognitoSTSClient = STSClient(config: cognitoSTSConfig)
141+
142+
let output = try await cognitoSTSClient.getCallerIdentity(
143+
input: GetCallerIdentityInput()
144+
)
145+
146+
print("Authenticated with AWS using Cognito!")
147+
print(" ARN: \(output.arn ?? "<unknown>")")
148+
print(" Account ID: \(output.account ?? "<unknown>")")
149+
print(" User ID: \(output.userId ?? "<unknown>")")
150+
// snippet-end:[swift.identity.cognito.resolve]
151+
152+
//======================================================================
153+
// Add a managed policy to the role to allow access to the AWS S3
154+
// function ListBuckets.
155+
//======================================================================
156+
157+
print("Creating a managed policy to allow listing S3 buckets...")
158+
let createPolicyOutput = try await iamClient.createPolicy(
159+
input: CreatePolicyInput(
160+
policyDocument: """
161+
{
162+
"Version": "2012-10-17",
163+
"Statement": [
164+
{
165+
"Effect": "Allow",
166+
"Action": "s3:ListAllMyBuckets",
167+
"Resource": "arn:aws:s3:::*"
168+
}
169+
]
170+
}
171+
""",
172+
policyName: managedPolicyName
173+
)
174+
)
175+
176+
guard let managedPolicy = createPolicyOutput.policy else {
177+
print("No policy returned by CreatePolicy!")
178+
return
179+
}
180+
181+
managedPolicyArn = managedPolicy.arn
182+
183+
print("Attaching the policy to the IAM role...")
184+
_ = try await iamClient.attachRolePolicy(
185+
input: AttachRolePolicyInput(
186+
policyArn: managedPolicy.arn,
187+
roleName: roleName
188+
)
189+
)
190+
191+
// Wait for the policy to attach.
192+
193+
print("Waiting for the policy to attach to the role...")
194+
try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds
195+
196+
//======================================================================
197+
// This is where you can do tasks using the returned AWS credentials.
198+
// In this example, we list S3 buckets.
199+
//======================================================================
200+
201+
// snippet-start:[swift.identity.cognito.s3]
202+
let s3Config = try await S3Client.S3ClientConfiguration(
203+
awsCredentialIdentityResolver: cognitoCredentialResolver,
204+
region: region
205+
)
206+
let s3Client = S3Client(config: s3Config)
207+
208+
let listBucketsOutput = try await s3Client.listBuckets(
209+
input: ListBucketsInput()
210+
)
211+
// snippet-end:[swift.identity.cognito.s3]
212+
guard let buckets = listBucketsOutput.buckets else {
213+
print("No buckets returned by S3!")
214+
return
215+
}
216+
217+
print("Found \(buckets.count) S3 buckets:")
218+
for bucket in buckets {
219+
print(" \(bucket.name ?? "<unnamed>")")
220+
}
221+
}
222+
223+
/// Clean up by deleting AWS assets created by the example. Ignores
224+
/// errors since this is just simple cleanup work.
225+
func cleanup() async {
226+
print("Deleting the identity pool...")
227+
_ = try? await cognitoIdentityClient.deleteIdentityPool(
228+
input: DeleteIdentityPoolInput(identityPoolId: identityPoolID)
229+
)
230+
231+
print("Deleting the policy...")
232+
if managedPolicyArn != nil {
233+
_ = try? await iamClient.deletePolicy(
234+
input: DeletePolicyInput(policyArn: managedPolicyArn)
235+
)
236+
}
237+
238+
print ("Deleting the IAM role...")
239+
_ = try? await iamClient.deleteRole(
240+
input: DeleteRoleInput(roleName: roleName)
241+
)
242+
}
243+
244+
/// Create a function that blocks the caller until execution is complete.
245+
///
246+
/// - Parameter block: The function to call and wait for its return.
247+
private func blocking(_ block: @escaping @Sendable () async -> Void) {
248+
let semaphore = DispatchSemaphore(value: 0)
249+
Task {
250+
await block()
251+
semaphore.signal()
252+
}
253+
semaphore.wait()
254+
}
255+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
/// A simple example that shows how to use the AWS SDK for Swift to
5+
/// authenticate using Amazon Cognito.
6+
7+
// snippet-start:[swift.identity.cognito.imports]
8+
import ArgumentParser
9+
import AWSCognitoIdentity
10+
import AWSIAM
11+
import AWSS3
12+
import AWSSDKIdentity
13+
import AWSSTS
14+
import Foundation
15+
import SmithyIdentity
16+
// snippet-end:[swift.identity.cognito.imports]
17+
18+
struct ExampleCommand: ParsableCommand {
19+
@Option(help: "AWS Region name")
20+
var region = "us-east-1"
21+
22+
static var configuration = CommandConfiguration(
23+
commandName: "cognito-resolver",
24+
abstract: """
25+
Demonstrates how to use a Cognito credential identity resolver with the
26+
AWS SDK for Swift.
27+
""",
28+
discussion: """
29+
"""
30+
)
31+
32+
/// Called by ``main()`` to do the actual running of the AWS
33+
/// example.
34+
func runAsync() async throws {
35+
let example = try Example(region: region)
36+
37+
try await example.run()
38+
}
39+
}
40+
41+
/// The program's asynchronous entry point.
42+
@main
43+
struct Main {
44+
/// The function that serves as the main asynchronous entry point for the
45+
/// example. It parses the command line using the Swift Argument Parser,
46+
/// then calls the `runAsync()` function to run the example itself.
47+
static func main() async {
48+
let args = Array(CommandLine.arguments.dropFirst())
49+
50+
do {
51+
let command = try ExampleCommand.parse(args)
52+
try await command.runAsync()
53+
} catch {
54+
ExampleCommand.exit(withError: error)
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)