From 0bb288de0e049bd07fa0b1803e43af9ddf2e934d Mon Sep 17 00:00:00 2001 From: Eric Shepherd Date: Thu, 4 Sep 2025 17:35:34 +0000 Subject: [PATCH 1/3] First commit --- .../cognito-resolver/Package.swift | 42 +++ .../cognito-resolver/Sources/Example.swift | 255 ++++++++++++++++++ .../cognito-resolver/Sources/entry.swift | 57 ++++ 3 files changed, 354 insertions(+) create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Package.swift create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Package.swift b/swift/example_code/identity-resolvers/cognito-resolver/Package.swift new file mode 100644 index 00000000000..458af4b323c --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version:5.9 +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// The swift-tools-version declares the minimum version of Swift required to +// build this package. + +import PackageDescription + +let package = Package( + name: "cognito-resolver", + // Let Xcode know the minimum Apple platforms supported. + platforms: [ + .macOS(.v11), + .iOS(.v13) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package( + url: "https://github.com/awslabs/aws-sdk-swift", + from: "1.0.0" + ), + .package( + url: "https://github.com/apple/swift-argument-parser.git", + branch: "main" + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .executableTarget( + name: "cognito-resolver", + dependencies: [ + .product(name: "AWSCognitoIdentity", package: "aws-sdk-swift"), + .product(name: "AWSIAM", package: "aws-sdk-swift"), + .product(name: "AWSSTS", package: "aws-sdk-swift"), + .product(name: "AWSS3", package: "aws-sdk-swift"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + path: "Sources"), + ] +) diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift new file mode 100644 index 00000000000..ea3f95c0e16 --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift @@ -0,0 +1,255 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// snippet-start:[swift.identity.cognito.imports] +import AWSCognitoIdentity +import AWSSDKIdentity +import AWSSTS +// snippet-end:[swift.identity.cognito.imports] +import AWSIAM +import AWSS3 +import Foundation + +/// Contains the data and code for the main body of the example. +class Example { + let region: String + + var cognitoIdentityClient: CognitoIdentityClient! + var iamClient: IAMClient! + var roleName: String + + /// The name of the AWS Cognito Identity Pool to use. + let identityPoolName: String + /// The ID of the Identity Pool. + var identityPoolID: String! + + /// The name of the managed policy granting Amazon S3 permissions. + let managedPolicyName: String + /// The ARN of the managed policy granting S3 permissions. + var managedPolicyArn: String? + + /// Initialize the example. + /// + /// - Parameter region: The AWS Region to operate in. + /// + /// - Throws: Any AWS errors thrown by IAM or Cognito. + /// + /// ^ Note: IAM must always use `us-east-1`, so it doesn't use the value + /// of the `region` parameter. + init(region: String) throws { + self.region = region + + self.identityPoolName = "cognito-resolver-example-\(UUID().uuidString.split(separator: "-").first!.lowercased())" + cognitoIdentityClient = try CognitoIdentityClient(region: region) + + self.roleName = "cognito-unauth-\(identityPoolName)" + iamClient = try IAMClient(region: "us-east-1") + + self.managedPolicyName = "cognito-policy-\(identityPoolName)" + } + + /// The body of the example. + /// + /// - Throws: Errors from IAM, STS, or Cognito. + func run() async throws { + // Set up the cleanup function to run automatically when this object + // is discarded. This way, we clean up AWS artifacts whether the run + // is successful or an error occurs. + + defer { + blocking { + await self.cleanup() + } + } + + // Create an identity pool to use for this example. + + print("Creating a Cognito identity pool named \(identityPoolName)...") + identityPoolID = try await cognitoIdentityClient.createIdentityPool( + input: CreateIdentityPoolInput( + allowUnauthenticatedIdentities: true, + identityPoolName: identityPoolName + ) + ).identityPoolId + + // Create an IAM role for unauthenticated users. + + let trustPolicy = """ + { + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": {"Federated": "cognito-identity.amazonaws.com"}, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": {"cognito-identity.amazonaws.com:aud": "\(identityPoolID!)"}, + "ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "unauthenticated"} + } + }] + } + """ + + print("Creating an IAM role named \(roleName)...") + let createRoleInput = CreateRoleInput( + assumeRolePolicyDocument: trustPolicy, + roleName: roleName + ) + let createRoleOutput = try await iamClient.createRole(input: createRoleInput) + + guard let role = createRoleOutput.role else { + print("*** No role returned by CreateRole!") + return + } + + // Wait for the role to be available. + + print("Waiting for the role to be available...") + try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds + + // Assign the role to the identity pool. + + print("Setting the identity pool's roles...") + _ = try await cognitoIdentityClient.setIdentityPoolRoles( + input: SetIdentityPoolRolesInput( + identityPoolId: identityPoolID, + roles: ["unauthenticated": role.arn!] + ) + ) + + //====================================================================== + // Resolve an identity using the Cognito credential identity resolver + // with the AWS STS function getCallerIdentity(input:). This is done + // by configuring the STS client to use the Cognito credentials + // resolver. + //====================================================================== + + // snippet-start:[swift.identity.cognito.resolve] + // Create a Cognito credential resolver that uses the Cognito Identity + // Pool created above. + let cognitoCredentialResolver = try CognitoAWSCredentialIdentityResolver( + identityPoolId: identityPoolID, + identityPoolRegion: region + ) + + // Create an AWS STS client that uses the new Cognito credential + // resolver to do credential identity resolution. + let cognitoSTSConfig = try await STSClient.STSClientConfiguration( + awsCredentialIdentityResolver: cognitoCredentialResolver, + region: "us-east-1" + ) + let cognitoSTSClient = STSClient(config: cognitoSTSConfig) + + let output = try await cognitoSTSClient.getCallerIdentity( + input: GetCallerIdentityInput() + ) + + print("Authenticated with AWS using Cognito!") + print(" ARN: \(output.arn ?? "")") + print(" Account ID: \(output.account ?? "")") + print(" User ID: \(output.userId ?? "")") + // snippet-end:[swift.identity.cognito.resolve] + + //====================================================================== + // Add a managed policy to the role to allow access to the AWS S3 + // function ListBuckets. + //====================================================================== + + print("Creating a managed policy to allow listing S3 buckets...") + let createPolicyOutput = try await iamClient.createPolicy( + input: CreatePolicyInput( + policyDocument: """ + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:ListAllMyBuckets", + "Resource": "arn:aws:s3:::*" + } + ] + } + """, + policyName: managedPolicyName + ) + ) + + guard let managedPolicy = createPolicyOutput.policy else { + print("No policy returned by CreatePolicy!") + return + } + + managedPolicyArn = managedPolicy.arn + + print("Attaching the policy to the IAM role...") + _ = try await iamClient.attachRolePolicy( + input: AttachRolePolicyInput( + policyArn: managedPolicy.arn, + roleName: roleName + ) + ) + + // Wait for the policy to attach. + + print("Waiting for the policy to attach to the role...") + try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds + + //====================================================================== + // This is where you can do tasks using the returned AWS credentials. + // In this example, we list S3 buckets. + //====================================================================== + + // snippet-start:[swift.identity.cognito.s3] + let s3Config = try await S3Client.S3ClientConfiguration( + awsCredentialIdentityResolver: cognitoCredentialResolver, + region: region + ) + let s3Client = S3Client(config: s3Config) + + let listBucketsOutput = try await s3Client.listBuckets( + input: ListBucketsInput() + ) + // snippet-end:[swift.identity.cognito.s3] + guard let buckets = listBucketsOutput.buckets else { + print("No buckets returned by S3!") + return + } + + print("Found \(buckets.count) S3 buckets:") + for bucket in buckets { + print(" \(bucket.name ?? "")") + } + } + + /// Clean up by deleting AWS assets created by the example. Ignores + /// errors since this is just simple cleanup work. + func cleanup() async { + print("Deleting the identity pool...") + _ = try? await cognitoIdentityClient.deleteIdentityPool( + input: DeleteIdentityPoolInput(identityPoolId: identityPoolID) + ) + + print("Deleting the policy...") + if managedPolicyArn != nil { + _ = try? await iamClient.deletePolicy( + input: DeletePolicyInput(policyArn: managedPolicyArn) + ) + } + + print ("Deleting the IAM role...") + _ = try? await iamClient.deleteRole( + input: DeleteRoleInput(roleName: roleName) + ) + } + + /// Create a function that blocks the caller until execution is complete. + /// + /// - Parameter block: The function to call and wait for its return. + private func blocking(_ block: @escaping @Sendable () async -> Void) { + let semaphore = DispatchSemaphore(value: 0) + Task { + await block() + semaphore.signal() + } + semaphore.wait() + } +} \ No newline at end of file diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift new file mode 100644 index 00000000000..917861d533e --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift @@ -0,0 +1,57 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +/// A simple example that shows how to use the AWS SDK for Swift to +/// authenticate using Amazon Cognito. + +// snippet-start:[swift.identity.cognito.imports] +import ArgumentParser +import AWSCognitoIdentity +import AWSIAM +import AWSS3 +import AWSSDKIdentity +import AWSSTS +import Foundation +import SmithyIdentity +// snippet-end:[swift.identity.cognito.imports] + +struct ExampleCommand: ParsableCommand { + @Option(help: "AWS Region name") + var region = "us-east-1" + + static var configuration = CommandConfiguration( + commandName: "cognito-resolver", + abstract: """ + Demonstrates how to use a Cognito credential identity resolver with the + AWS SDK for Swift. + """, + discussion: """ + """ + ) + + /// Called by ``main()`` to do the actual running of the AWS + /// example. + func runAsync() async throws { + let example = try Example(region: region) + + try await example.run() + } +} + +/// The program's asynchronous entry point. +@main +struct Main { + /// The function that serves as the main asynchronous entry point for the + /// example. It parses the command line using the Swift Argument Parser, + /// then calls the `runAsync()` function to run the example itself. + static func main() async { + let args = Array(CommandLine.arguments.dropFirst()) + + do { + let command = try ExampleCommand.parse(args) + try await command.runAsync() + } catch { + ExampleCommand.exit(withError: error) + } + } +} From 12938f6bf24cc2411398847156501683d652a30c Mon Sep 17 00:00:00 2001 From: Eric Shepherd Date: Thu, 4 Sep 2025 17:36:36 +0000 Subject: [PATCH 2/3] Remove stuff added by accident --- .../cognito-resolver/Package.swift | 42 --- .../cognito-resolver/Sources/Example.swift | 255 ------------------ .../cognito-resolver/Sources/entry.swift | 57 ---- 3 files changed, 354 deletions(-) delete mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Package.swift delete mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift delete mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Package.swift b/swift/example_code/identity-resolvers/cognito-resolver/Package.swift deleted file mode 100644 index 458af4b323c..00000000000 --- a/swift/example_code/identity-resolvers/cognito-resolver/Package.swift +++ /dev/null @@ -1,42 +0,0 @@ -// swift-tools-version:5.9 -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -// -// The swift-tools-version declares the minimum version of Swift required to -// build this package. - -import PackageDescription - -let package = Package( - name: "cognito-resolver", - // Let Xcode know the minimum Apple platforms supported. - platforms: [ - .macOS(.v11), - .iOS(.v13) - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - .package( - url: "https://github.com/awslabs/aws-sdk-swift", - from: "1.0.0" - ), - .package( - url: "https://github.com/apple/swift-argument-parser.git", - branch: "main" - ), - ], - targets: [ - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. - .executableTarget( - name: "cognito-resolver", - dependencies: [ - .product(name: "AWSCognitoIdentity", package: "aws-sdk-swift"), - .product(name: "AWSIAM", package: "aws-sdk-swift"), - .product(name: "AWSSTS", package: "aws-sdk-swift"), - .product(name: "AWSS3", package: "aws-sdk-swift"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - ], - path: "Sources"), - ] -) diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift deleted file mode 100644 index ea3f95c0e16..00000000000 --- a/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// snippet-start:[swift.identity.cognito.imports] -import AWSCognitoIdentity -import AWSSDKIdentity -import AWSSTS -// snippet-end:[swift.identity.cognito.imports] -import AWSIAM -import AWSS3 -import Foundation - -/// Contains the data and code for the main body of the example. -class Example { - let region: String - - var cognitoIdentityClient: CognitoIdentityClient! - var iamClient: IAMClient! - var roleName: String - - /// The name of the AWS Cognito Identity Pool to use. - let identityPoolName: String - /// The ID of the Identity Pool. - var identityPoolID: String! - - /// The name of the managed policy granting Amazon S3 permissions. - let managedPolicyName: String - /// The ARN of the managed policy granting S3 permissions. - var managedPolicyArn: String? - - /// Initialize the example. - /// - /// - Parameter region: The AWS Region to operate in. - /// - /// - Throws: Any AWS errors thrown by IAM or Cognito. - /// - /// ^ Note: IAM must always use `us-east-1`, so it doesn't use the value - /// of the `region` parameter. - init(region: String) throws { - self.region = region - - self.identityPoolName = "cognito-resolver-example-\(UUID().uuidString.split(separator: "-").first!.lowercased())" - cognitoIdentityClient = try CognitoIdentityClient(region: region) - - self.roleName = "cognito-unauth-\(identityPoolName)" - iamClient = try IAMClient(region: "us-east-1") - - self.managedPolicyName = "cognito-policy-\(identityPoolName)" - } - - /// The body of the example. - /// - /// - Throws: Errors from IAM, STS, or Cognito. - func run() async throws { - // Set up the cleanup function to run automatically when this object - // is discarded. This way, we clean up AWS artifacts whether the run - // is successful or an error occurs. - - defer { - blocking { - await self.cleanup() - } - } - - // Create an identity pool to use for this example. - - print("Creating a Cognito identity pool named \(identityPoolName)...") - identityPoolID = try await cognitoIdentityClient.createIdentityPool( - input: CreateIdentityPoolInput( - allowUnauthenticatedIdentities: true, - identityPoolName: identityPoolName - ) - ).identityPoolId - - // Create an IAM role for unauthenticated users. - - let trustPolicy = """ - { - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": {"Federated": "cognito-identity.amazonaws.com"}, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": {"cognito-identity.amazonaws.com:aud": "\(identityPoolID!)"}, - "ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "unauthenticated"} - } - }] - } - """ - - print("Creating an IAM role named \(roleName)...") - let createRoleInput = CreateRoleInput( - assumeRolePolicyDocument: trustPolicy, - roleName: roleName - ) - let createRoleOutput = try await iamClient.createRole(input: createRoleInput) - - guard let role = createRoleOutput.role else { - print("*** No role returned by CreateRole!") - return - } - - // Wait for the role to be available. - - print("Waiting for the role to be available...") - try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds - - // Assign the role to the identity pool. - - print("Setting the identity pool's roles...") - _ = try await cognitoIdentityClient.setIdentityPoolRoles( - input: SetIdentityPoolRolesInput( - identityPoolId: identityPoolID, - roles: ["unauthenticated": role.arn!] - ) - ) - - //====================================================================== - // Resolve an identity using the Cognito credential identity resolver - // with the AWS STS function getCallerIdentity(input:). This is done - // by configuring the STS client to use the Cognito credentials - // resolver. - //====================================================================== - - // snippet-start:[swift.identity.cognito.resolve] - // Create a Cognito credential resolver that uses the Cognito Identity - // Pool created above. - let cognitoCredentialResolver = try CognitoAWSCredentialIdentityResolver( - identityPoolId: identityPoolID, - identityPoolRegion: region - ) - - // Create an AWS STS client that uses the new Cognito credential - // resolver to do credential identity resolution. - let cognitoSTSConfig = try await STSClient.STSClientConfiguration( - awsCredentialIdentityResolver: cognitoCredentialResolver, - region: "us-east-1" - ) - let cognitoSTSClient = STSClient(config: cognitoSTSConfig) - - let output = try await cognitoSTSClient.getCallerIdentity( - input: GetCallerIdentityInput() - ) - - print("Authenticated with AWS using Cognito!") - print(" ARN: \(output.arn ?? "")") - print(" Account ID: \(output.account ?? "")") - print(" User ID: \(output.userId ?? "")") - // snippet-end:[swift.identity.cognito.resolve] - - //====================================================================== - // Add a managed policy to the role to allow access to the AWS S3 - // function ListBuckets. - //====================================================================== - - print("Creating a managed policy to allow listing S3 buckets...") - let createPolicyOutput = try await iamClient.createPolicy( - input: CreatePolicyInput( - policyDocument: """ - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:ListAllMyBuckets", - "Resource": "arn:aws:s3:::*" - } - ] - } - """, - policyName: managedPolicyName - ) - ) - - guard let managedPolicy = createPolicyOutput.policy else { - print("No policy returned by CreatePolicy!") - return - } - - managedPolicyArn = managedPolicy.arn - - print("Attaching the policy to the IAM role...") - _ = try await iamClient.attachRolePolicy( - input: AttachRolePolicyInput( - policyArn: managedPolicy.arn, - roleName: roleName - ) - ) - - // Wait for the policy to attach. - - print("Waiting for the policy to attach to the role...") - try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds - - //====================================================================== - // This is where you can do tasks using the returned AWS credentials. - // In this example, we list S3 buckets. - //====================================================================== - - // snippet-start:[swift.identity.cognito.s3] - let s3Config = try await S3Client.S3ClientConfiguration( - awsCredentialIdentityResolver: cognitoCredentialResolver, - region: region - ) - let s3Client = S3Client(config: s3Config) - - let listBucketsOutput = try await s3Client.listBuckets( - input: ListBucketsInput() - ) - // snippet-end:[swift.identity.cognito.s3] - guard let buckets = listBucketsOutput.buckets else { - print("No buckets returned by S3!") - return - } - - print("Found \(buckets.count) S3 buckets:") - for bucket in buckets { - print(" \(bucket.name ?? "")") - } - } - - /// Clean up by deleting AWS assets created by the example. Ignores - /// errors since this is just simple cleanup work. - func cleanup() async { - print("Deleting the identity pool...") - _ = try? await cognitoIdentityClient.deleteIdentityPool( - input: DeleteIdentityPoolInput(identityPoolId: identityPoolID) - ) - - print("Deleting the policy...") - if managedPolicyArn != nil { - _ = try? await iamClient.deletePolicy( - input: DeletePolicyInput(policyArn: managedPolicyArn) - ) - } - - print ("Deleting the IAM role...") - _ = try? await iamClient.deleteRole( - input: DeleteRoleInput(roleName: roleName) - ) - } - - /// Create a function that blocks the caller until execution is complete. - /// - /// - Parameter block: The function to call and wait for its return. - private func blocking(_ block: @escaping @Sendable () async -> Void) { - let semaphore = DispatchSemaphore(value: 0) - Task { - await block() - semaphore.signal() - } - semaphore.wait() - } -} \ No newline at end of file diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift deleted file mode 100644 index 917861d533e..00000000000 --- a/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -// -/// A simple example that shows how to use the AWS SDK for Swift to -/// authenticate using Amazon Cognito. - -// snippet-start:[swift.identity.cognito.imports] -import ArgumentParser -import AWSCognitoIdentity -import AWSIAM -import AWSS3 -import AWSSDKIdentity -import AWSSTS -import Foundation -import SmithyIdentity -// snippet-end:[swift.identity.cognito.imports] - -struct ExampleCommand: ParsableCommand { - @Option(help: "AWS Region name") - var region = "us-east-1" - - static var configuration = CommandConfiguration( - commandName: "cognito-resolver", - abstract: """ - Demonstrates how to use a Cognito credential identity resolver with the - AWS SDK for Swift. - """, - discussion: """ - """ - ) - - /// Called by ``main()`` to do the actual running of the AWS - /// example. - func runAsync() async throws { - let example = try Example(region: region) - - try await example.run() - } -} - -/// The program's asynchronous entry point. -@main -struct Main { - /// The function that serves as the main asynchronous entry point for the - /// example. It parses the command line using the Swift Argument Parser, - /// then calls the `runAsync()` function to run the example itself. - static func main() async { - let args = Array(CommandLine.arguments.dropFirst()) - - do { - let command = try ExampleCommand.parse(args) - try await command.runAsync() - } catch { - ExampleCommand.exit(withError: error) - } - } -} From c74bcef9d47c3536d9d9cada7c69cbf94a5ebd25 Mon Sep 17 00:00:00 2001 From: Eric Shepherd Date: Thu, 4 Sep 2025 17:37:49 +0000 Subject: [PATCH 3/3] First commit --- .../cognito-resolver/Package.swift | 42 +++ .../cognito-resolver/Sources/Example.swift | 255 ++++++++++++++++++ .../cognito-resolver/Sources/entry.swift | 57 ++++ 3 files changed, 354 insertions(+) create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Package.swift create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift create mode 100644 swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Package.swift b/swift/example_code/identity-resolvers/cognito-resolver/Package.swift new file mode 100644 index 00000000000..458af4b323c --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version:5.9 +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// The swift-tools-version declares the minimum version of Swift required to +// build this package. + +import PackageDescription + +let package = Package( + name: "cognito-resolver", + // Let Xcode know the minimum Apple platforms supported. + platforms: [ + .macOS(.v11), + .iOS(.v13) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package( + url: "https://github.com/awslabs/aws-sdk-swift", + from: "1.0.0" + ), + .package( + url: "https://github.com/apple/swift-argument-parser.git", + branch: "main" + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .executableTarget( + name: "cognito-resolver", + dependencies: [ + .product(name: "AWSCognitoIdentity", package: "aws-sdk-swift"), + .product(name: "AWSIAM", package: "aws-sdk-swift"), + .product(name: "AWSSTS", package: "aws-sdk-swift"), + .product(name: "AWSS3", package: "aws-sdk-swift"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + path: "Sources"), + ] +) diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift new file mode 100644 index 00000000000..ea3f95c0e16 --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Sources/Example.swift @@ -0,0 +1,255 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// snippet-start:[swift.identity.cognito.imports] +import AWSCognitoIdentity +import AWSSDKIdentity +import AWSSTS +// snippet-end:[swift.identity.cognito.imports] +import AWSIAM +import AWSS3 +import Foundation + +/// Contains the data and code for the main body of the example. +class Example { + let region: String + + var cognitoIdentityClient: CognitoIdentityClient! + var iamClient: IAMClient! + var roleName: String + + /// The name of the AWS Cognito Identity Pool to use. + let identityPoolName: String + /// The ID of the Identity Pool. + var identityPoolID: String! + + /// The name of the managed policy granting Amazon S3 permissions. + let managedPolicyName: String + /// The ARN of the managed policy granting S3 permissions. + var managedPolicyArn: String? + + /// Initialize the example. + /// + /// - Parameter region: The AWS Region to operate in. + /// + /// - Throws: Any AWS errors thrown by IAM or Cognito. + /// + /// ^ Note: IAM must always use `us-east-1`, so it doesn't use the value + /// of the `region` parameter. + init(region: String) throws { + self.region = region + + self.identityPoolName = "cognito-resolver-example-\(UUID().uuidString.split(separator: "-").first!.lowercased())" + cognitoIdentityClient = try CognitoIdentityClient(region: region) + + self.roleName = "cognito-unauth-\(identityPoolName)" + iamClient = try IAMClient(region: "us-east-1") + + self.managedPolicyName = "cognito-policy-\(identityPoolName)" + } + + /// The body of the example. + /// + /// - Throws: Errors from IAM, STS, or Cognito. + func run() async throws { + // Set up the cleanup function to run automatically when this object + // is discarded. This way, we clean up AWS artifacts whether the run + // is successful or an error occurs. + + defer { + blocking { + await self.cleanup() + } + } + + // Create an identity pool to use for this example. + + print("Creating a Cognito identity pool named \(identityPoolName)...") + identityPoolID = try await cognitoIdentityClient.createIdentityPool( + input: CreateIdentityPoolInput( + allowUnauthenticatedIdentities: true, + identityPoolName: identityPoolName + ) + ).identityPoolId + + // Create an IAM role for unauthenticated users. + + let trustPolicy = """ + { + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": {"Federated": "cognito-identity.amazonaws.com"}, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": {"cognito-identity.amazonaws.com:aud": "\(identityPoolID!)"}, + "ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "unauthenticated"} + } + }] + } + """ + + print("Creating an IAM role named \(roleName)...") + let createRoleInput = CreateRoleInput( + assumeRolePolicyDocument: trustPolicy, + roleName: roleName + ) + let createRoleOutput = try await iamClient.createRole(input: createRoleInput) + + guard let role = createRoleOutput.role else { + print("*** No role returned by CreateRole!") + return + } + + // Wait for the role to be available. + + print("Waiting for the role to be available...") + try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds + + // Assign the role to the identity pool. + + print("Setting the identity pool's roles...") + _ = try await cognitoIdentityClient.setIdentityPoolRoles( + input: SetIdentityPoolRolesInput( + identityPoolId: identityPoolID, + roles: ["unauthenticated": role.arn!] + ) + ) + + //====================================================================== + // Resolve an identity using the Cognito credential identity resolver + // with the AWS STS function getCallerIdentity(input:). This is done + // by configuring the STS client to use the Cognito credentials + // resolver. + //====================================================================== + + // snippet-start:[swift.identity.cognito.resolve] + // Create a Cognito credential resolver that uses the Cognito Identity + // Pool created above. + let cognitoCredentialResolver = try CognitoAWSCredentialIdentityResolver( + identityPoolId: identityPoolID, + identityPoolRegion: region + ) + + // Create an AWS STS client that uses the new Cognito credential + // resolver to do credential identity resolution. + let cognitoSTSConfig = try await STSClient.STSClientConfiguration( + awsCredentialIdentityResolver: cognitoCredentialResolver, + region: "us-east-1" + ) + let cognitoSTSClient = STSClient(config: cognitoSTSConfig) + + let output = try await cognitoSTSClient.getCallerIdentity( + input: GetCallerIdentityInput() + ) + + print("Authenticated with AWS using Cognito!") + print(" ARN: \(output.arn ?? "")") + print(" Account ID: \(output.account ?? "")") + print(" User ID: \(output.userId ?? "")") + // snippet-end:[swift.identity.cognito.resolve] + + //====================================================================== + // Add a managed policy to the role to allow access to the AWS S3 + // function ListBuckets. + //====================================================================== + + print("Creating a managed policy to allow listing S3 buckets...") + let createPolicyOutput = try await iamClient.createPolicy( + input: CreatePolicyInput( + policyDocument: """ + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:ListAllMyBuckets", + "Resource": "arn:aws:s3:::*" + } + ] + } + """, + policyName: managedPolicyName + ) + ) + + guard let managedPolicy = createPolicyOutput.policy else { + print("No policy returned by CreatePolicy!") + return + } + + managedPolicyArn = managedPolicy.arn + + print("Attaching the policy to the IAM role...") + _ = try await iamClient.attachRolePolicy( + input: AttachRolePolicyInput( + policyArn: managedPolicy.arn, + roleName: roleName + ) + ) + + // Wait for the policy to attach. + + print("Waiting for the policy to attach to the role...") + try await Task.sleep(nanoseconds: 10_000_000_000) // Wait 10 seconds + + //====================================================================== + // This is where you can do tasks using the returned AWS credentials. + // In this example, we list S3 buckets. + //====================================================================== + + // snippet-start:[swift.identity.cognito.s3] + let s3Config = try await S3Client.S3ClientConfiguration( + awsCredentialIdentityResolver: cognitoCredentialResolver, + region: region + ) + let s3Client = S3Client(config: s3Config) + + let listBucketsOutput = try await s3Client.listBuckets( + input: ListBucketsInput() + ) + // snippet-end:[swift.identity.cognito.s3] + guard let buckets = listBucketsOutput.buckets else { + print("No buckets returned by S3!") + return + } + + print("Found \(buckets.count) S3 buckets:") + for bucket in buckets { + print(" \(bucket.name ?? "")") + } + } + + /// Clean up by deleting AWS assets created by the example. Ignores + /// errors since this is just simple cleanup work. + func cleanup() async { + print("Deleting the identity pool...") + _ = try? await cognitoIdentityClient.deleteIdentityPool( + input: DeleteIdentityPoolInput(identityPoolId: identityPoolID) + ) + + print("Deleting the policy...") + if managedPolicyArn != nil { + _ = try? await iamClient.deletePolicy( + input: DeletePolicyInput(policyArn: managedPolicyArn) + ) + } + + print ("Deleting the IAM role...") + _ = try? await iamClient.deleteRole( + input: DeleteRoleInput(roleName: roleName) + ) + } + + /// Create a function that blocks the caller until execution is complete. + /// + /// - Parameter block: The function to call and wait for its return. + private func blocking(_ block: @escaping @Sendable () async -> Void) { + let semaphore = DispatchSemaphore(value: 0) + Task { + await block() + semaphore.signal() + } + semaphore.wait() + } +} \ No newline at end of file diff --git a/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift b/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift new file mode 100644 index 00000000000..917861d533e --- /dev/null +++ b/swift/example_code/identity-resolvers/cognito-resolver/Sources/entry.swift @@ -0,0 +1,57 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +/// A simple example that shows how to use the AWS SDK for Swift to +/// authenticate using Amazon Cognito. + +// snippet-start:[swift.identity.cognito.imports] +import ArgumentParser +import AWSCognitoIdentity +import AWSIAM +import AWSS3 +import AWSSDKIdentity +import AWSSTS +import Foundation +import SmithyIdentity +// snippet-end:[swift.identity.cognito.imports] + +struct ExampleCommand: ParsableCommand { + @Option(help: "AWS Region name") + var region = "us-east-1" + + static var configuration = CommandConfiguration( + commandName: "cognito-resolver", + abstract: """ + Demonstrates how to use a Cognito credential identity resolver with the + AWS SDK for Swift. + """, + discussion: """ + """ + ) + + /// Called by ``main()`` to do the actual running of the AWS + /// example. + func runAsync() async throws { + let example = try Example(region: region) + + try await example.run() + } +} + +/// The program's asynchronous entry point. +@main +struct Main { + /// The function that serves as the main asynchronous entry point for the + /// example. It parses the command line using the Swift Argument Parser, + /// then calls the `runAsync()` function to run the example itself. + static func main() async { + let args = Array(CommandLine.arguments.dropFirst()) + + do { + let command = try ExampleCommand.parse(args) + try await command.runAsync() + } catch { + ExampleCommand.exit(withError: error) + } + } +}