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+ }
0 commit comments