I am using AWS MobileHub SDK with a developer authenticated identity setup.
The mobile hub SDK has an AWSIdentityManager that handles the session and
I have followed the guide here to login with my identity provider.
The identity provider works fine and the login succeeds but whenever I try to make a cloudlogic call (invoke a lambda function) to fails with:
AWSiOSSDKv2 [Error] AWSCloudLogic.m line:47 | __67-[AWSCloudLogic invokeFunction:withParameters:withCompletionBlock:]_block_invoke | invokeFunction: Error: The operation couldn’t be completed. AccessDeniedException
This indicates that when the lambda functions are being invoked that MobileHub is not using the correct AIM role and
Lambda is denying access. My AIM setup is fine and checked so I am not sure where the problem is.
In the AWSIdentityManager I have modified only this function:
- (AWSTask *)initializeClients:(NSDictionary *)logins {
NSLog(#"initializing clients...");
[AWSLogger defaultLogger].logLevel = AWSLogLevelError; //AWSLogLevelVerbose;
[AWSServiceConfiguration addGlobalUserAgentProductToken:AWS_MOBILEHUB_USER_AGENT];
NSString *email = [JNKeychain loadValueForKey:#"email"];
NSDictionary *logins = [NSDictionary dictionaryWithObject:email forKey:developerProvider];
id<AWSCognitoIdentityProvider> identityProvider = [[CBDeveloperAuthenticatedIdentityProvider alloc]
initWithRegionType:AMAZON_COGNITO_REGION
identityId:nil
identityPoolId:AMAZON_COGNITO_IDENTITY_POOL_ID
logins:logins
providerName:developerProvider];
self.credentialsProvider = [[AWSCognitoCredentialsProvider alloc]
initWithRegionType:AMAZON_COGNITO_REGION
identityProvider:identityProvider
unauthRoleArn:nil
authRoleArn:nil];
//I have AWS_COGNITO_UNAUTH_ROLE and AWS_COGNITO_AUTH_ROLE but the docs don't seem to use them... and when I do there is no change
//http://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AMAZON_COGNITO_REGION
credentialsProvider:self.credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
return [self.credentialsProvider getIdentityId];
}
Amazon has a lot of information scattered everywhere but I couldn't find anything out there for mobile hub with regards to developer
authenticated identities but
Related
I am adding KMS (SSE-KMS) encryption to an AWS S3 bucket programmatically using the AWS SDK iOS v2.6.21. I do this with the following objective-c code:
AWSS3 *awsClient = [AWSS3 S3ForKey:bucketObject.regionShortName];
AWSS3ServerSideEncryptionRule *rule= [AWSS3ServerSideEncryptionRule new];
AWSS3ServerSideEncryptionByDefault *applyServerSideEncryptionByDefault =[AWSS3ServerSideEncryptionByDefault new];applyServerSideEncryptionByDefault.SSEAlgorithm=AWSS3ServerSideEncryptionAwsKms;
applyServerSideEncryptionByDefault.KMSMasterKeyID=kmsAliasKeyId;
AWSS3ServerSideEncryptionConfiguration *configuration =
[AWSS3ServerSideEncryptionConfiguration new];
configuration.rules = #[rule];
AWSS3PutBucketEncryptionRequest *request =
[AWSS3PutBucketEncryptionRequest new];
request.bucket = bucketObject.name;
request.serverSideEncryptionConfiguration=configuration;
[awsClient putBucketEncryption:request];
}
This seemingly works and shows my bucket having default encryption. However, it does not specify that the encryption is KMS, it just shows a checkmark that encryption is on (when looking at the console).
When I programmatically check the status of the encryption it does not specify anything for SSEAlgorithm:
<AWSS3ServerSideEncryptionConfiguration: 0x604000008790> {
rules = (
"<AWSS3ServerSideEncryptionRule: 0x6040000087f0> {\n}"
);
}
I would expect the output showing SSEAlgorithm to be 2 based on how AWSS3ServerSideEncryption is defined in AWS SDK:
AWSS3ServerSideEncryption:
AWSS3ServerSideEncryptionUnknown,
AWSS3ServerSideEncryptionAES256,
AWSS3ServerSideEncryptionAwsKms,
When I compare the output when I do the same code using SSE-S3 (AWSS3ServerSideEncryptionAES256) instead and the output is:
<AWSS3ServerSideEncryptionConfiguration: 0x600000009c40> {
rules = (
"<AWSS3ServerSideEncryptionRule: 0x600000009c50> {\n
applyServerSideEncryptionByDefault = \"<AWSS3ServerSideEncryptionByDefault: 0x60000022b6a0> {\\n SSEAlgorithm = 1;\\n}\";\n}"
);
}
Can anyone see what I am missing in my request to correctly use KMS as the default encryption for the entire bucket?
Thanks. Cheers, Trond
You have setup all the objects correctly, expect for the step where you associate the encryption on the rule. See the code below for the change you need to make to get it setup correctly.
...
...
...
AWSS3ServerSideEncryptionRule *rule= [AWSS3ServerSideEncryptionRule new];
AWSS3ServerSideEncryptionByDefault *applyServerSideEncryptionByDefault =[AWSS3ServerSideEncryptionByDefault new];
applyServerSideEncryptionByDefault.SSEAlgorithm=AWSS3ServerSideEncryptionAwsKms;
applyServerSideEncryptionByDefault.KMSMasterKeyID=kmsAliasKeyId;
//Add the encryption information to the rule
rule.applyServerSideEncryptionByDefault = applyServerSideEncryptionByDefault;
AWSS3ServerSideEncryptionConfiguration *configuration =
...
...
...
I am using a "Cognito User Pool authorizer" (no "AWS_IAM" option, no custom coded authorizer) to call Lambda methods via API Gateway and identify the user logged in on the iOS client.
On Lambda, I use the user id I get from the Cognito User Pool authorizer via event.requestContext.authorizer.claims.sub (to store the user id with some DynamoDB items).
I now need to compare this with the id of the logged in user in the iOS client.
I found [AWSIdentityManager defaultIdentityManager].identityId, but this (obviously) returns he IdentityID (which I can look up in the AWS console in Cognito --> Federated Identities --> Identity Browser), which is different from the "sub" id I see in Cognito --> User Pools --> Users and groups
Can I get the "sub" via the AWS iOS SDK?
If I cannot get it, what other id parameter should I use that I can retrieve both on Lambda and the client to identify the current client user/the user making the API request?
It seems that I have to specifically request the attributes via the user details like this:
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:AWSCognitoUserPoolsSignInProviderKey];
AWSCognitoIdentityUser *user = [pool currentUser];
NSString *mySub;
[[user getDetails] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserGetDetailsResponse *> * _Nonnull task) {
if(!task.error){
AWSCognitoIdentityUserGetDetailsResponse *response = task.result;
NSArray<AWSCognitoIdentityProviderAttributeType*> *userAttributes = response.userAttributes;
for (AWSCognitoIdentityProviderAttributeType *attr in self.userAttributes) {
if ([attr.name isEqualToString:#"sub"]) {
mySub = attr.value;
}
}
} else {
NSLog(#"Error fetching Cognito User Attributes: %#", task.error.localizedDescription);
}
}];
Another solution (tested with the AWS JavaScript SDK):
When we authenticate with Cognito, we can retrieve a JWT token:
user.authenticateUser(authenticationDetails, {
onSuccess: (result) => resolve(result.getIdToken().getJwtToken()),
onFailure: (err) => reject(err)
})
It happens that this JWT token is an standard object that can be decoded.
Using Auth0 JWT decode (npm install jwt-decode), we can decode this token and retrieve all user attributes (e-mail, username, etc.) and the sub.
var jwtDecode = require('jwt-decode');
var decoded = jwtDecode(token);
console.log(decoded);
// prints sub, email, username, ...
I'm trying to implement DataTrans iOS Library and I'm getting the following error:
Error Domain=ch.datatrans.ios-payment Code=3 "Validation Error" UserInfo={NSUnderlyingError=0x600000459530 {Error Domain=DTNetworkingErrorDomain Code=2000 "access denied (XML alias service)" UserInfo={NSLocalizedDescription=access denied (XML alias service)}}, ch.datatrans.payment-method=VIS, NSLocalizedDescription=Validation Error}
This is what I've done so far according to the documentation:
NSArray* creditCards = [NSArray arrayWithObjects:DTPaymentMethodVisa, DTPaymentMethodMasterCard,
DTPaymentMethodPostFinanceCard, nil];
DTPaymentRequest* paymentRequest = [[DTPaymentRequest alloc] init];
paymentRequest.amountInSmallestCurrencyUnit = 10000;
paymentRequest.localizedPriceDescription = #"123";
paymentRequest.currencyCode = #"CHF";
paymentRequest.merchantId = #"xxxxxx";
paymentRequest.refno = [ModelProfile singleton].shopCartNo;
paymentRequest.signature = #"xxxxxxx";
DTPaymentController* paymentController = [DTPaymentController
paymentControllerWithDelegate:self
paymentRequest:paymentRequest
paymentMethods:creditCards];
paymentController.paymentOptions.showBackButtonOnFirstScreen = YES;
paymentController.paymentOptions.testing = YES;
[paymentController presentInNavigationController:self.navigationController animated:YES];
After contacting DataTrans i found the solution.
The problem was that my test merchant ID was configured for web transactions, so creating a new test merchant-ID for in-app transactions (iOS/Android libraries) solved my problem.
I use Restkit in my iOS app to call an API.
When the app first launched, a client token is retrieved from the API in order to call the service to post a new user.
After this user has successfully being created, a new access token is sent back by the API. This time is it is a user token.
All the other requests to the API made by the app will now have to use this user token.
I am using a singleton class that inherits from RKObjectManager. I then built one class per ressource to access (example : Users, Images, ...) all inheriting from that main class called AKObjectManager.
In AKObjectManager I have the following method :
+ (instancetype)sharedManager
{
NSURL *url = [NSURL URLWithString:LLY_API_BASE_URL];
AKObjectManager *sharedManager = [self managerWithBaseURL:url];
sharedManager.requestSerializationMIMEType = RKMIMETypeJSON;
...
// Access Token
NSUserDefaults* userData = [NSUserDefaults standardUserDefaults];
if ([userData objectForKey:#"accessToken"]) {
// Not too sure if this is being taken into account for the other class that inherits
[ sharedManager.HTTPClient setDefaultHeader:#"Authorization" value:[userData objectForKey:#"accessToken"]];
}
return sharedManager;
}
I thought that by checking for every access the accessToken in NSUserDefaults and setting it in the Authorization field in the header would work but no. I can see through NSLog that the new access token is set when changing it for for some reasons using Charles the header of the request still points to the old one.
I then used
[[AKObjectManager sharedManager].HTTPClient setDefaultHeader:#"Authorization" value:accessToken.accessToken];
As soon as I got the new token but faced the same issue.
Finally I went for that road (UserManager inherits from AKObjectManager)
// Force the newly refresh token to be set in the Authorization header
[[UserManager sharedManager].HTTPClient setDefaultHeader:#"Authorization" value:[[NSUserDefaults standardUserDefaults] objectForKey:#"accessToken"]];
[[UserManager sharedManager] show:nil // userId equals nil meaning it will be replaced by 'self'
success:^(User* user){
self.user = user;
...
And it worked but I am not too happy about the implementation.
Could you point me to where I got it wrong and advise on how to do it ?
I am using following code to handle twitter integration in my Application.
- (IBAction)signInWithTwitter:(id)sender {
NSURL *requestURL = [NSURL URLWithString:#"https://api.twitter.com/oauth/request_token"];
NSURL *accessURL = [NSURL URLWithString:#"https://api.twitter.com/oauth/access_token"];
NSURL *authorizeURL = [NSURL URLWithString:#"https://api.twitter.com/oauth/authorize"];
NSString *scope = #"http://api.twitter.com/";
GTMOAuthAuthentication *auth = [self authForTwitter];
[auth setCallback:#"http://www.noop.com/OAuthCallback"];
GTMOAuthViewControllerTouch *viewController;
viewController = [[GTMOAuthViewControllerTouch alloc] initWithScope:scope
language:nil
requestTokenURL:requestURL
authorizeTokenURL:authorizeURL
accessTokenURL:accessURL
authentication:auth
appServiceName:#"CK12: Twitter"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
}
- (GTMOAuthAuthentication *)authForTwitter {
GTMOAuthAuthentication *auth = [[GTMOAuthAuthentication alloc] initWithSignatureMethod:kGTMOAuthSignatureMethodHMAC_SHA1
consumerKey:TWITTER_CONSUMER_KEY
privateKey:TWITTER_CONSUMER_SECRET];
[auth setServiceProvider:#"Twitter"];
return auth;
}
My problem is, if I am changing device time i.e making it 1 hour late, then I am getting following error:
Error Domain=com.google.HTTPStatus Code=401 and error message is : failed to validate oauth signature and token .
So can anybody please suggest how to solve this. if system time is wrong then also I want to make it work .
Finally I got solution for this . . . .
One of the reason we get this error "Failed to validate OAuth signature and token"
when system time is wrong . Because OAuth request carry system timestamp parameters with it , so when device time is not within the 5 minutes of twitter server time .we get "Failed to validate OAuth signature and token" .
There are two ways to make it work , if the device time is even wrong .
1.make HTTP HEAD request to an endpoint on api.twitter.com -- you'll get a Date HTTP header in the response that indicates the current time understood by Twitter. You would then convert this to epoch time and adjust your oauth_timestamp values by a determined offset.
2.There's a small iOS library named ios-ntp link: http://code.google.com/p/ios-ntp . use this to get the current Accurate time .
After that i just set the timestamp of OAuth object in the following method
- (GTMOAuthAuthentication *)authForTwitter
{
GTMOAuthAuthentication *auth = [[GTMOAuthAuthentication alloc]
initWithSignatureMethod:kGTMOAuthSignatureMethodHMAC_SHA1
consumerKey:TWITTER_CONSUMER_KEY
privateKey:TWITTER_CONSUMER_SECRET];
[auth setServiceProvider:#"Twitter"];
NSDate * currentdate = //GET ACCURATE DATE HERE ;
[auth setTimestamp:[NSString stringWithFormat:#"%f",[currentdate timeIntervalSince1970]]];
return auth;
}
that's it . . .Happy Coding :)