I am trying to download a JSON file which is in my bucket in my S3 on my AWS account. I created an Unauthenticated cognito pool and copied this into my app delegate from the sample code:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.USWest2,
identityPoolId:"us-west-2:59a31a8f-ee6a-45fe-adaa-fa3eff871c80")
let configuration = AWSServiceConfiguration(region:.USWest2, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
In my view controller I have this code:
let transferManager = AWSS3TransferManager.default()
let downloadingFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("db_storage/costdb_latest.json ")
if let downloadRequest = AWSS3TransferManagerDownloadRequest(){
downloadRequest.bucket = "coast-s3-bucket"
downloadRequest.key = "db_storage/costdb_latest.json "
downloadRequest.downloadingFileURL = downloadingFileURL
transferManager.download(downloadRequest).continueWith(executor: AWSExecutor.default(), block: { (task: AWSTask<AnyObject>) -> Any? in
if( task.error != nil){
print(task.error!.localizedDescription)
return nil
}
print(task.result!)
if let data = NSData(contentsOf: downloadingFileURL){
DispatchQueue.main.async(execute: { () -> Void in
print(data)
})
}
return nil
})
}
Both the bucket and the pool are in the same region. USWest2 (Oregon). My bucket is public, and I've added AmazonS3FullAccess and AmazonS3ReadOnlyAccess to my policies. And I'm getting this error:
The operation couldn’t be completed. (com.amazonaws.AWSS3ErrorDomain error 4.)
Its not an authentication/authorization error (I think). From the looks of it, there is some issue with the configuration (region,endpoint etc.). Generally, these errors contain a full description of what's wrong. Try logging the full error message.
Also, see this example app for S3 TransferManager. Use your S3 & Cognito details. If this works, there is some code issue and you can find out by comparing both of these. If this does not work, then this could be a service issue.
Related
is there a good function to download images from AWS S3 bucket? I have an access key and a secret key for permisson. The URL is thru a different database accessible. I also already imported AWSS3 and AWSCore.
I have already found a upload function:
func uploadFile(withImage image: UIImage) {
let access = "access_key"
let secret = "secret_key"
let credentials = AWSStaticCredentialsProvider(accessKey: access, secretKey: secret)
let configuration = AWSServiceConfiguration(region: AWSRegionType.EUCentral1, credentialsProvider: credentials)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let s3BucketName = "bucket_name"
let compressedImage = image.resizedImage(newSize: CGSize(width: 80, height: 80))
let data: Data = compressedImage.pngData()!
let remoteName = generateRandomStringWithLength(length: 12)+"."+data.format
print("REMOTE NAME : ",remoteName)
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { (task, progress) in
DispatchQueue.main.async(execute: {
// Update a progress bar
})
}
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
// Do something e.g. Alert a user for transfer completion.
// On failed uploads, `error` contains the error object.
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadData(data, bucket: s3BucketName, key: remoteName, contentType: "image/"+data.format, expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
print("Error : \(error.localizedDescription)")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(s3BucketName).appendingPathComponent(remoteName)
if let absoluteString = publicURL?.absoluteString {
// Set image with URL
print("Image URL : ",absoluteString)
}
}
return nil
}
}
I would not recommend to download files directly from S3 using an access and secret key.
I'd propose you do the following:
Make sure the bucket is as "private" as can be.
Have an API with authentication and authorisation (AWS API Gateway) that checks if the user is authenticated and permitted to download the S3 object.
Generate a pre-signed download URL with that is only valid for a short period of time (15-60 minutes).
Return that pre-signed download URL to your app through the API.
Use the URL within your app to download the S3 object.
This way you don't have to ship username and password in your app and the bucket is closed off to the "outside" reducing the risk of accidental information leakage.
Why I wouldn't recommend using the access key and secret key:
This is a potential security issue. People that reverse engineer the app could gain access to those "static" keys and depending on the underlying IAM role do all sorts of harm. But even if you have proper IAM roles with very limited access, essentially shipping a username and password with your app is not a good idea under any circumstance. How would you "rotate" the secret if something bad happens etc.
i try to Accessing aws Services Using an Identity pool after Sign-in by this aws Doc
https://docs.aws.amazon.com/en_us/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html
but in Integrating a User Pool with an Identity Pool part,
i can't add the token in the swift code
let serviceConfiguration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET", poolId: "YOUR_USER_POOL_ID")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "UserPool")
let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "YOUR_IDENTITY_POOL_ID", identityProviderManager:pool)
In javascript and android both have credentialsProvider.logins .
i got the message like this:
Message: The security token included in the request is invalid.
or i need to call aws sts api by this doc?
https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_temp_request.html
Thanks for help.
first i try this
let identity = AWSCognitoIdentity.default()
let input = AWSCognitoIdentityGetCredentialsForIdentityInput()
input?.identityId = identityid
input?.logins = ["cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX":identityToken]
identity.getCredentialsForIdentity(input!).continueWith(block: { (task:AWSTask<AWSCognitoIdentityGetCredentialsForIdentityResponse>) -> Any? in
if (task.error != nil) {
print("Error: " + task.error!.localizedDescription)
}
else {
self.accessKey = (task.result?.credentials?.accessKeyId)!
self.secrectKey = (task.result?.credentials?.secretKey)!
self.sessionToken = (task.result?.credentials?.sessionToken)
}
return task;
})
but it's not work, i'm not sure what happen. i use https to get aws iot shadow, it will return null value and get 403 message.
then i change the official doc code like this
let serviceConfiguration = AWSServiceConfiguration(region: .USEast1,credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET", poolId: "YOUR_USER_POOL_ID")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "UserPool")
let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "YOUR_IDENTITY_POOL_ID", identityProviderManager:pool)
then use this code
credentialsProvider.credentials().continueWith(block: { (task) -> AnyObject? in
if (task.error != nil) {
print("Error: " + task.error!.localizedDescription)
}
else {
let credentials = task.result!
self.accessKey = credentials.accessKey
self.secrectKey = credentials.secretKey
self.session = credentials.sessionKey! }
return task;
})
but this code is not use idtoken i got from cognito login, maybe this just let me access service, then i can parse the idtoken to get the idtoken detail message.
hope this will help to someone.
in addition to this, the import things is to attachPrincipalPolicy you temporary credential with iot, when you login cognito, you will get credential identityID,
then used this identityID to attach.
or you will get 403 forbidden, and a null return values.
FYR
https://github.com/awslabs/aws-sdk-ios-samples/blob/master/IoT-Sample/Swift/IoTSampleSwift/ConnectionViewController.swift
I'm getting a trouble when trying to connect AWSCognito to AWSLambda to pass an Auth role into it. My application has serverless architecture based on CognitoUserPool's, Lambda and IAM. So I have one configuration for all these things like the following:
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: CognitoIdentityUserPoolRegion,
identityPoolId: CognitoIdentityUserPoolIdentityId,
identityProviderManager: nil
)
// setup service configuration
let serviceConfiguration = AWSServiceConfiguration(
region: CognitoIdentityUserPoolRegion,
credentialsProvider: credentialsProvider
)
// create pool configuration
let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: CognitoIdentityUserPoolAppClientId,
clientSecret: nil,
poolId: CognitoIdentityUserPoolId)
// initialize user pool client
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: AWSCognitoUserPoolsSignInProviderKey)
// fetch the user pool client we initialized in above step
let pool = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey)
pool.delegate = self
AWSLambdaInvoker.register(with: serviceConfiguration!, forKey: AWSCognitoUserPoolsSignInProviderKey)
Then I do log in using the following snippet:
func signIn(username: String, password: String) -> Promise<Bool> {
let user = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey).getUser()
return Promise(resolvers: { resolve, reject in
user.getSession(username, password: password, validationData: nil)
.continueWith(block: { task -> Any? in
if let error = task.error {
let message = (error as NSError).userInfo["message"] as? String
return reject(AWSError.SignIn.sessionError(message))
}
guard let idToken = task.result?.idToken?.tokenString else {
return reject(AWSError.SignIn.noTokenProvided)
}
guard let identityId = user.username else {
return reject(AWSError.SignIn.noIdentityIdProvided)
}
return resolve(true)
})
})
}
The issue is that I'm always getting an error saying that:
User:
arn:aws:sts:{identity}:assumed-role/Cognito_myUnauth_Role/CognitoIdentityCredentials
is not authorized to perform: InvokeLambda
I tested an official example, I wrote my own implementations, and also I tried all the solutions mentioned in this thread: https://github.com/aws/aws-sdk-ios/issues/357 , but there was no luck with it.
I also tried implementing my own AWSCognitoCredentialsProviderHelper but the error is still presented. Do you have any ideas or maybe you also have faced with this issue so you can help me.
Thanks
For some reason the upload to S3 does work not whereas I was able to get the download function to work just fine.
Here is my upload code:
#IBAction func uploadFile(_ sender: UIButton) {
let CognitoRegionType = AWSRegionType.USWest2 // e.g. AWSRegionType.USEast1
let CognitoIdentityPoolId = "us-west-2:3c00122a-866c-4ce4-9dd3-ee23c16e58f3"
let DefaultServiceRegionType = AWSRegionType.USWest1 // e.g. AWSRegionType.USEast1
let S3BucketName = "snappcastphotos"
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:CognitoRegionType, identityPoolId: CognitoIdentityPoolId)
let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType , credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = "bingo"
uploadRequest?.body = URL(fileURLWithPath: "/users/rhom/desktop/test.rtf")
let transferManager = AWSS3TransferManager.default()
transferManager.upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as? NSError {
if error.domain == AWSS3TransferManagerErrorDomain, let code = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch code {
case .cancelled, .paused:
break
default:
print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
} else {
print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
return nil
}
let uploadOutput = task.result
print("Upload complete for: \(uploadRequest?.key)")
return nil
})
}
and this code produces the following error/output:
Error uploading: Optional("bingo") Error: Error Domain=com.amazonaws.AWSS3ErrorDomain Code=0 "(null)" UserInfo={HostId=KAsbvIqiY67dr/64f3uvZPB1Lr5Vj7eNNV198DLai/RG/tA+v3To8CBbnbFSM00V2COZnzebx/M=, Message=The request body terminated unexpectedly, Code=IncompleteBody, RequestId=A95236E53A1B8FE8}
Anyone have any ideas? I can't find any clue as to why the download works using Cognito and S3 but the upload doesn't work....
It looks more of an authorization error. Check if you are having the write permissions for this particular bucket and also check the path you are giving is correct, as if there is a mismatch it wont work.
I finally figured this problem out :)
It turns out that I had installed an older version of AWS Frameworks as I was following an older tutorial to get started... and had loaded the frameworks using Cocoapods specifying in the podfile for IOS 8.0 but I am IOS 10.0 now!!!! so changing the podfile to version 10.0 fixed the problem.
I ended up reinstalling AWS Frameworks using Cocoapods and the correct podfile with 10.0... but I think there is a way to just update the pod but I will look into that later
Hope this helps anyone who has this problem too :)
After looking around at different threads such as these (Swift - AWS S3 Upload Image from Photo Library and download it), and (Upload image AWS S3 bucket in swift), I got pretty close to getting an image upload to work but can't figure out what I'm doing wrong?
I get the following error below in my console.
Error: Error Domain=com.amazonaws.AWSS3TransferUtilityErrorDomain Code=1 "(null)" UserInfo={Server=AmazonS3, Transfer-Encoding=Identity, Connection=close, Content-Type=application/xml, Date=Tue, 13 Dec 2016 05:58:32 GMT, x-amz-request-id=2A76DF0FE33476C5, x-amz-id-2=QWZgOETbWQfddlqKmm0w3Z9HFGM2x1DWnrFjukiajTsIXfbSt9W0orTkoZeNXH/bI1xfc3mxI4Q=, x-amz-bucket-region=us-west-1}
My code is below:
let myIdentityPoolId = "us-west-2:dca2beb4-etcetcetc...."
let credentialsProvider:AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.usWest2, identityPoolId: myIdentityPoolId)
let configuration = AWSServiceConfiguration(region: AWSRegionType.usWest2, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
I then call to upload my image with a function I made below
func uploadImage(filename:String){
print("AWS Upload Image Attempt...")
//defining bucket and upload file name
let S3BucketName: String = "distribution-tech-mobile"
let filepath = "\(AppDelegate.appDelegate.applicationDocumentsDirectory())/\(filename)"
let imageURL = URL(fileURLWithPath: filepath)
let S3UploadKeyName = filename //TODO: Change this later
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = filename
uploadRequest?.contentType = "image/jpeg"
uploadRequest?.body = imageURL
uploadRequest?.serverSideEncryption = AWSS3ServerSideEncryption.awsKms
uploadRequest?.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: {
self.amountUploaded = totalBytesSent // To show the updating data status in label.
self.fileSize = totalBytesExpectedToSend
print("\(totalBytesSent)/\(totalBytesExpectedToSend)")
})
}
self.uploadCompletionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)");
}
else{
print("Sucess")
}
})
}
let transferUtility = AWSS3TransferUtility.default()
let expression = AWSS3TransferUtilityUploadExpression()
transferUtility.uploadFile(imageURL, bucket: S3BucketName, key: S3UploadKeyName, contentType: "image/jpeg", expression: expression, completionHander: uploadCompletionHandler).continue({ (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Upload Starting!")
}
return nil;
})
}
I get the console message "Upload Starting" and then a message "Failed with error" (which comes from my completion handler), followed by the error I assume from Amazon.
Any thoughts on what I'm doing wrong?
Okay I found the answer, but I have a different problem now that I'll post in another question regarding showing upload progress.
The answer was my bucket was created in the incorrect region. I created my credentials in Oregon, which is Us-West-2, and I created the bucket in Northern California by accident the first time. This apparently created the error.