I am using user pools with my iOS mobile app.
I would like to access my lambda functions using the sdk but can't find documentation on how to provide the authentication necessary. Also unclear as to whether I need to use the API Gateway if I am using the sdk with user pools.
I am using this method to access my lambda function:
private func invokeLambda(data: [String: Any]) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let pool = appDelegate.pool
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "MyIdentityPoolId", identityProviderManager:pool)
let configuration = AWSServiceConfiguration(region:.USEast1, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let lambdaInvoker = AWSLambdaInvoker.default()
let jsonObject: [String: Any] = data
lambdaInvoker.invokeFunction("myFunction", jsonObject: jsonObject).continueWith(block: {(task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as NSError? {
if (error.domain == AWSLambdaInvokerErrorDomain) && (AWSLambdaInvokerErrorType.functionError == AWSLambdaInvokerErrorType(rawValue: error.code)) {
print("Function error: \(error.userInfo[AWSLambdaInvokerFunctionErrorKey])")
} else {
print("Error: \(error)")
}
return nil
}
// Handle response in task.result
if let JSONDictionary = task.result as? NSDictionary {
print("Result: \(JSONDictionary)")
print("resultKey: \(JSONDictionary["resultKey"])")
}
return nil
})
}
I am getting the following error:
"AccessDeniedException"
Edit #1
The error that I am getting indicates the unauth role is being used for access rather than the auth role. the user is correctly logged in and the token is valid. Not sure where I am going wrong here.
Try attaching AWSLambdaInvocation-DynamoDB to the role.
Related
My app has a "Sign in with the Apple" account feature. I am wondering if there is sign out feature from the Apple account.
I tried the below but doesn't get success
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedOperation = .operationLogout
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.performRequests()
Apple only allows currently for the user to perform a signout (iOS/watchOS/tvOS) or shown as a revoke of permissions to us. They recommend you get the state of the credentials before use to check for revoke and if that has occurred to delete any local information (Remove the user identifier where ever you have it stored) (And possibly change UI if needed; For example like showing login view).
let appleIDProvider = ASAuthorizationAppleIDProvider()
appleIDProvider.getCredentialState(forUserID: KeychainItem.currentUserIdentifier) { (credentialState, error) in
switch credentialState {
case .authorized:
// The Apple ID credential is valid.
break
case .revoked:
// The Apple ID credential is revoked.
break
case .notFound:
// No credential was found, so show the sign-in UI.
break
default:
break
}
}
You could provide a prompt to the user on signout guiding them to revoke in their device's settings as well and listen for the change notification.
You need to delete the existing item from the keychain.
This is my sample code, using Apple sample code.
You can get sample code from Apple
Apple recommend you get the state of the credentials before use to check for revoke and if that has occurred to delete any local information
struct KeychainItem {
init(service: String, account: String, accessGroup: String? = nil) {
self.service = service
self.account = account
self.accessGroup = accessGroup
}
static func deleteUserIdentifierFromKeychain() {
do { //please change service id to your bundle ID
try KeychainItem(service: "com.example.apple-samplecode", account: "userIdentifier").deleteItem()
} catch {
print("Unable to delete userIdentifier from keychain")
}
}
func deleteItem() throws {
// Delete the existing item from the keychain.
let query = KeychainItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup)
let status = SecItemDelete(query as CFDictionary)
// Throw an error if an unexpected status was returned.
guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError }
}
keychainQuery
keychainQuery is from apple sample code.
private static func keychainQuery(withService service: String, account: String? = nil, accessGroup: String? = nil) -> [String: AnyObject] {
var query = [String: AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrService as String] = service as AnyObject?
if let account = account {
query[kSecAttrAccount as String] = account as AnyObject?
}
if let accessGroup = accessGroup {
query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
}
return query
}
I'm not able to do a S3 Upload despite AWS Cognito indicating that the device is signedIn and the IdentityID being obtained.
The storage error description is "Session expired could not fetch identity id". This is despite the identityID that was returned and passed into the s3 upload file function.
Logs into AWS Cognito using the ASAuthorizationAppleIDCredential.identityToken
Also obtains the IdentityID
func SignIn() {
awsmobileclient.federatedSignIn(providerName: IdentityProvider.apple.rawValue,
token: identityToken) { (userState, error) in
if let error = error {
print("Error in federatedSignIn: \(error)")
return
}
guard let userState = userState else {
print("userState unexpectedly nil")
return
}
print("federatedSignIn successful: \(userState.rawValue)")
sleep(5)
// Retrieve your Amazon Cognito ID
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .CACentral1, identityPoolId: "ca-central-1:3e8d12d5-9739-4934-8eb0-df6bec232d77")
let configuration = AWSServiceConfiguration(region: .CACentral1, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
credentialsProvider.getIdentityId().continueWith(block: { (task) -> AnyObject? in
if (task.error != nil) {
print("Error: " + task.error!.localizedDescription)
}
else {
// the task result will contain the identity id
let cognitoId = task.result!
print("Cognito id: \(cognitoId)")
UserDefaults.standard.set(cognitoId, forKey: "cognitoId")
}
return task;
})
}
Uploads Data to S3
func uploadData(key: String, data: Data) {
var progressSink: AnyCancellable?
var resultSink: AnyCancellable?
let options = StorageUploadDataRequest.Options(accessLevel: .private, targetIdentityId: UserDefaults.standard.string(forKey: "cognitoId"), contentType: "image/jpeg")
let storageOperation = Amplify.Storage.uploadData(key: key, data: data, options: options)
progressSink = storageOperation.progressPublisher.sink { progress in print("Progress: \(progress)") }
resultSink = storageOperation.resultPublisher.sink {
if case let .failure(storageError) = $0 {
print("Failed: \(storageError.errorDescription). \(storageError.recoverySuggestion)")
}
}
receiveValue: { data in
print("Completed: \(data)")
}
}
Turns out it was likely due to AWS Cognito settings. AWS Cognito configured as, "enable access to unauthenticated users" unchecked, Allow Basic (Classic) Flow checked, Apple Services ID should be Bundle ID, Role Selection Default, Attributes Disabled.
This was done using the AWS Amplify Escape Hatch to AWS Mobile Client SDK with the AWSMobileClient.federatedSignIn
In app didFinishLaunchingWithOptions
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "my-identity-pool-id")
let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialProvider)
AWSFirehoseRecorder.register(with: configuration!, forKey: "somestupidkey")
In view controller
let firehoseRecorder = AWSFirehoseRecorder(forKey: "somestupidkey")
let yourData = "Test_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_dataTest_data".data(using: .utf8)
firehoseRecorder.saveRecord(yourData, streamName: kinesisDirectory)
NSLog("Added %d records", firehoseRecorder.diskBytesUsed) // ALWAYS ZERO!!!!
It looks like it's not writing data at all. diskBytesUsed always returns 0. Any idea what might be issue?
Thanks
Ok, solved it. It turned to be authentication issue. So using this code will actually catch the error message of why the records weren't submitted:
firehoseRecorder?.saveRecord(jsonData, streamName: "YourStreamName").continueOnSuccessWith(block: { (task:AWSTask) -> AWSTask? in
NSLog("RecordBeen saved")
return nil;
}).continueWith(block: { (task:AWSTask) -> Any? in
if let error = task.error as? NSError {
print("Error: \(error)")
return nil
}
return nil
})
Showing error messages actually helped.
Hope it helps someone else having same issue.
Thanks.
I am trying to integrate S3 upload to upload a video file and tried Developer authenticated Identity method. Everything is configured as per the aws docs says.
DeveloperAuthenticatedIdentityProvider Class :
class DeveloperAuthenticatedIdentityProvider : AWSCognitoCredentialsProviderHelper {
override func token() -> AWSTask<NSString> {
//return AWSTask //with token and will set identityId
}
and then
let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: COGNITO_REGION, identityPoolId: COGNITO_POOL_ID, useEnhancedFlow: true, identityProviderManager:nil)
let credentialsProvider =
AWSCognitoCredentialsProvider(regionType: COGNITO_REGION, identityProvider:devAuth)
let configuration =
AWSServiceConfiguration(region: S3_REGION, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
after configuring these things tried to upload using AWSS3TransferManager
let transferManager = AWSS3TransferManager.default()
let uploadingFileURL = URL(fileURLWithPath: "your/file/path/myTestFile.txt")
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.bucket = "myBucket"
uploadRequest.key = "myTestFile.txt"
uploadRequest.body = uploadingFileURL
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
})
Whenever I call Upload method it shows
[Error Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=8
"(null)" UserInfo={__type=NotAuthorizedException,
message=Unauthenticated access is not supported for this identity
pool.}]
also DeveloperAuthenticatedIdentityProvider not getting fired
kindly please help.
When you using Developer authenticated identity for cognito identity provider you need not use
AWSS3TransferManager.default()
You need to register the AWSServiceConfiguration to the AWSS3TransferManager with a key.
AWSS3TransferManager.register(with: configuration!, forKey:
"KEY")
Try this way:
let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: COGNITO_REGION, identityPoolId: COGNITO_POOL_ID, useEnhancedFlow: true, identityProviderManager:nil)
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: COGNITO_REGION, identityProvider:devAuth)
let configuration = AWSServiceConfiguration(region: S3_REGION, credentialsProvider:credentialsProvider)
AWSS3TransferManager.register(with: configuration!, forKey: "YOUR_KEY")
//Start Upload
let uploadRequest = AWSS3TransferManagerUploadRequest()
//Set all properties to uploadRequest
AWSS3TransferManager.s3TransferManager(forKey: "YOUR_KEY").upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
// Do something with the response
if task.isCancelled {
print("Cancelled Upload")
}
else if (task.error != nil) {
print("Upload error --> \(task.error)")
}else{
print("Upload success!!! Be happy :)")
}
return task
})
Just try, I think it may work.
I want to use the "verifyEmailIdentity" action which is defined in Objective-C as part of the Amazon Simple Email Service API but I'm having trouble doing so in Swift. I want to call the action in Swift code and have the documentation of the action defined in a pod but I'm not really sure how to go about this.
Here is some sample code but my program doesn't recognize the return type.
func createRequest(verifyEmailIdentityRequest: SESVerifyEmailIdentityRequest) -> AmazonServiceRequest {
var request: AmazonServiceRequest = SESRequest()
request.setParameterValue("VerifyEmailIdentity", forKey: "Action")
request.setParameterValue("2010-12-01", forKey: "Version")
request.delegate = verifyEmailIdentityRequest.delegate
request.credentials = verifyEmailIdentityRequest.credentials()
request.endpoint = verifyEmailIdentityRequest.requestEndpoint()
request.requestTag = verifyEmailIdentityRequest.requestTag()
if verifyEmailIdentityRequest != nil {
if verifyEmailIdentityRequest.emailAddress != nil {
request.setParameterValue("\ (verifyEmailIdentityRequest.emailAddress)", forKey: "\("EmailAddress")")
}
}
return request
}
http://docs.aws.amazon.com/ses/latest/APIReference/API_VerifyEmailIdentity.html
You can achieve that by using the following snippet:
func verifyEmailIdentity(verifyEmailIdentityRequest: AWSSESVerifyEmailIdentityRequest) {
// You should ideally set your configuration in app delegate
// Set the region and cognito pool id
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: AWSRegionType.Unknown,
identityPoolId: "YOUR_POOL_ID")
let configuration = AWSServiceConfiguration(
region: AWSRegionType.Unknown,
credentialsProvider: credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
let ses = AWSSES.defaultSES()
ses.verifyEmailIdentity(verifyEmailIdentityRequest).continueWithBlock { (task: AWSTask) -> AnyObject? in
if let error = task.error {
// handle error here
} else if let result = task.result {
// handle result here
}
return nil
}
}
Thanks,
Rohan