iOS Facebook sign in with AWS Federated Identities issues - ios

I am using AWS Cognito Federated ID with my iOS app for social providers (Facebook, Google). After sign in with Facebook, user is getting error 'Token is not from a supported provider of this identity pool'. If I allow unauthenticated ids in Fed IDs, then that user gets unauthenticated user ids (linked logins = 0) in Identity browser and gets session key etc.
Can anyone help on resolving this issue?
Please don't mark this question as duplicated as I have seen all the similar questions and followed the solutions there, but to no avail!
Brief about code:
I have added Facebook in Authenticated Providers for Federated ID.
I have also added Facebook in Identity Providers for User Pool and done attributes mapping (mapped Id and Email).
I have created a social ID provider class which implements AWSIdentityProviderManager protocol. This class contains logins method as given:
(AWSTask<NSDictionary<NSString *, NSString *> *> )logins {
FBSDKAccessToken fbToken = [FBSDKAccessToken currentAccessToken];
if(fbToken){
NSString *token = fbToken.tokenString;
return [AWSTask taskWithResult: #{ AWSIdentityProviderFacebook : token }];
} else {
NSLog(#"FB Token is not found in AWSSocialIdentityProvider class");
return [AWSTask taskWithError:[NSError errorWithDomain:#"facebook"
code:-1
userInfo:#{#"error":#"No current Facebook access token"}]];
}
}
My Sign In View Controller does FB sign in. It creates fbCredentialsProvider by passing the socialIDProvider class as an identityProvider in it's construction, implemented as below:
self.fbCredentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"us-east-1:XXXXXXXXXXXX" identityProviderManager:self.socialIDProvider];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:self.fbCredentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
//wipe cached credentials
[self.fbCredentialsProvider clearCredentials];
//For calling fb logins
[self.fbCredentialsProvider credentials];
The above code is called in didCompleteWithResult method of FB Login Button.
That's it.

Related

AWSAPIGatewayClient always results in unauthorised

When using the amazon generated code for AWSAPIGatewayClient I always get
message = Unauthorized;
as a response.
What could be the cause of this?
AppDelegate
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"UserPool"];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
identityPoolId:CognitoPoolId
identityProviderManager:pool];
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:CognitoIdentityUserPoolRegion
credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = serviceConfiguration;
AWSCognitoIdentityUserPoolConfiguration *configuration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:CognitoIdentityUserPoolAppClientId
clientSecret:CognitoIdentityUserPoolAppClientSecret
poolId:CognitoIdentityUserPoolId];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration
userPoolConfiguration:configuration
forKey:#"UserPool"];
ViewController
[[[AWSPrjctRtClient defaultClient] suggestionsGet] continueWithBlock:^id _Nullable(AWSTask * _Nonnull task) {
NSLog(#"%#", task.error);
return nil;
}];
Results in
2017-04-07 16:02:24.386 xxxx[38051:1025018] Error Domain=com.amazonaws.AWSAPIGatewayErrorDomain Code=1 "(null)" UserInfo={HTTPBody={
message = Unauthorized;
The response looks like your API Gateway resource is configured to use Cognito user pools for authorization, but your code actually uses Cognito Federated Identities. In turn, Federated Identities requires API Gateway to use AWS_IAM authorizers, using IAM roles to manage access to your resources.
I would suggest you go through the following steps:
Follow this guide. Basically, within Cognito Federated Identities, configure an Identity Pool to use your User Pool as (one of) its Authentication provider(s). (You may have already done this)
Check the Authorization of your API Gateway resource(s) under Method Requests/Settings/Authorization. Set it to AWS_IAM. Don't forget to redeploy the newly configured API, and export a new SDK.
Your Identity Pool will require two IAM roles, for both unauthenticated and authenticated access to AWS services. You will have to add a policy to your role(s) to specify access to your AWS services, in this case you will need to grant "execute-api:Invoke" access to (presumably only) your authenticated role. I recommend using the policy generator for this, and make sure you get set the ARN for the policy to be only for the resource(s) you want to grant access to, otherwise all of your API Gateway resources may be accessed.
As for configuration on the iOS SDK side, make sure you use the code from the guide (shown below), it seems yours is slightly different. I have found that getting this wrong can induce a whole range of confusing errors that could have you look in all sorts of wrong directions for a solution.
Add to AppDelegate
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
credentialsProvider:nil];
AWSCognitoIdentityUserPoolConfiguration *userPoolConfiguration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:#"YOUR_CLIENT_ID"
clientSecret:#"YOUR_CLIENT_SECRET"
poolId:#"YOUR_USER_POOL_ID"];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration
userPoolConfiguration:userPoolConfiguration forKey:#"UserPool"];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"UserPool"];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]
initWithRegionType:AWSRegionUSEast1
identityPoolId:#"YOUR_IDENTITY_POOL_ID"
identityProviderManager:pool];
One important addition though! I've found this particularly confusing at first, but in the above code you initialize an AWSServiceConfiguration with credentialsProvider set to nil in order to register your AWSCognitoIdentityUserPool. However, you will need to initialize a new AWSServiceConfiguration that references your credentialsProvider to assign to your AWSServiceManager.defaultServiceManager.defaultServiceConfiguration. Like so:
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:CognitoUserPoolRegion
credentialsProvider:credentialsProvider];
The above described steps ultimately led me to successfully integrating Cognito User Pools with Federated Identities to allow access to API Gateway resources. The process involved some confusion about what services do what exactly, and piecing together pieces of code from different guides. I hope this helps!
Note that you can also probably do without Federated Identities and leave your API to be authorized using the User Pool directly. But I haven't been successful in that approach. Also, Federated Identities will allow you to add other authorizers at a later stage if you please to do so.

Calling AWS API Gateway using AWS Cognito

We use AWS V4 signature mechanism to make calls to our API Gateway endpoint from our iOS app. We had embedded the access key id and secret key in the code, which was working fine. Obviously this is not a secure way and the recommended way is to use AWS Cognito.
I wanted to know how do we use the temporary access key and secret (and session key probably as well) that we get from the AWSCredentials object in my Objective-C iOS code to make secure requests to our API Gateway endpoint.
We tried to use the temporary access key and secret retrieved from Cognito to generate the V4 signature in place of account access key and secret, but this does not seem like the correct approach. The API Gateway method is enabled with AWS_IAM as the authorization setting.
This is the error we get:
{ status code: 403, headers {
Connection = "keep-alive";
"Content-Length" = 69;
"Content-Type" = "application/json";
Date = "Fri, 13 Jan 2017 10:26:38 GMT";
Via = "1.1 .....cloudfront.net (CloudFront)";
"X-Amz-Cf-Id" = "...";
"X-Cache" = "Error from cloudfront";
"x-amzn-ErrorType" = UnrecognizedClientException;
"x-amzn-RequestId" = "...";
} }
The IdentityPoolId used is from the Identity Pool created under federated identities in AWS Cognito
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"];
We are using unauthenticated role since we do not need any form of user specific authentication. And this role has the following policies:
AmazonAPIGatewayInvokeFullAccess
AmazonAPIGatewayPushToCloudWatchLogs
CloudFrontFullAccess
AmazonCognitoDeveloperAuthenticatedIdentities
AmazonAPIGatewayAdministrator
CloudFrontReadOnlyAccess
IAMReadOnlyAccess
AmazonCognitoPowerUser
Can you please help here as to how can I use Cognito to generate V4 signatures or completely bypass the process.
It looks you are getting UnrecognizedClientException as response, but API Gateway doesn't return UnrecognizedClientException. Do you have the request id that was getting the error?
Just in case you might forget to register the configuration, you need to register the configuration to service manager.
AWSCognitoCredentialsProvider *creds = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:your_cognito_pool_id];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:creds];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
For your unauthenticated role policy, I think you are granting too powerful permission to the unauthenticated users. if you want them to be able to invoke your API, you can just give them AmazonAPIGatewayInvokeFullAccess or even scope down to method level.
Sending an HTTP header "x-amz-security-token" with the value of variable sessionKey obtained from the AWSCredentials object solved the problem:
[request setValue:sessionToken forHTTPHeaderField:#"X-Amz-Security-Token"];
The AWSCredentials object is retrieved using:
[[credentialsProvider credentials] continueWithBlock:^id(AWSTask<AWSCredentials *> *task) {
if (task.error) {
DDLogCError(#"failed getting credentials: %#", task.error);
}
else {
AWSCredentials *credentials = task.result;
}
return nil;
}]
And yes, have trimmed the policies to just one - AmazonAPIGatewayInvokeFullAccess.
Thanks you for your feedback.

Point of using the AWS Cognito Identity

Is the point of using the following code so that I can access other AWS tools directly with my ios app?
AWSCognitoCredentialsProvider *credentialsProvider = [[DeveloperAuthenticationProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"poolId"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
__block NSString *cognitoId = nil;
// Retrieve your Amazon Cognito ID
[[credentialsProvider getIdentityId] continueWithBlock:^id(AWSTask *task)
{
if (task.error)
{
NSLog(#"Error: %#", task.error);
}
else
{
// the task result will contain the identity id
cognitoId = task.result;
}
return nil;
}];
I then use AWS Lambda with an API gateway to get user identities.
Cognito is required in order to provide an execution context (authentication) when accessing an AWS resource. What that means, is that nothing is truly anonymous on AWS - even if you don't have your users "log in", they still have a unique identifier associated with their device.
What this means is that some random person outside of your app cannot simply hit your AWS resources (S3, Lambda, etc) and execute code.
This also means you can, and must, assign execution permissions to your Lambda to allow your Cognito group to execute.
Another thing to note: You do not need to use API gateway in order to execute Lambdas on iOS. You can invoke natively. I prefer doing it thusly - less configuration.
http://docs.aws.amazon.com/mobile/sdkforios/developerguide/lambda.html
Hope that answers your question.

Developer Authenticated Identities with Amazon

I'm not familar very well with Amazon so have few questions:
I want to perform login for users on amazon with Cognito using my own custom backEnd solution. Found this like possible solution. This backEnd return after registering and logging for me
{
identityId = "eu-xxxx-x:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx";
login = 1;
token = "eyJraWQiOiJldS13ZXN0LTExIiwidHlwIjoiSldTIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJldS13ZXN0LTE6NGFmNDdmMWItZDNmOC00NmZkLWI2MzEtZGE2OGU3ZmRmYzE1IiwiYXVkIjoiZXUtd2VzdC0xOjIwNjBiZDc3LWEwNDAtNGI4OC05MDU4LTczMGY3Y2RmNGQyZSIsImFtciI6WyJhdXRoZW50aWNhdGVkIiwibG9naW4ud3A0Lm15YXBwIiwibG9naW4ud3A0Lm15YXBwOmV1LXdlc3QtMToyMDYwYmQ3Ny1hMDQwLTRiODgtOTA1OC03MzBmN2NkZjRkMmU6a2lyaWxsLmdlQGdtYWlsLmNvbSJdLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRlbnRpdHkuYW1hem9uYXdzLmNvbSIsImV4cCI6MTQ1NTI5MDA5OSwiaWF0IjoxNDU1Mjg5MTk5fQ.FGnBUEQZ3wDENFNa_g29l2UhlIAklBnLdqpMomSE3_ayesNV1dqGyzAMQCvwyr4XdJpB3lI0KF-k3wc4t8BKPYg5QKrZ-q-aNkjwp34tHFMIr8vGw4vbZiKB6XnGMRghYSbPtuwwFG80ibZMAAXik4nld8sGxoQSrCjTubPKU4I9Mzi6lJQsDGAZxmm56E2lVSeBw2nZbE1iwRDhJf6hHJsKOLceDDtWoknRX3NHeNuoueNLS1JrbphD8wVqejxhEjrK-qucoUL_uj81GxYUkyONQtu-3B79epsXIsxvU_zW1MwVufFg5p5ID83F1Cic77QkzF2FJnEJIadEG6R_yw";
}
I want follow this guide. But according to this i should provide IdentityProviderName and token and set it like after any social's login :
credentialsProvider.logins = #{
#(AWSCognitoLoginProviderKeyFacebook): token
};
As IdentityProviderName i used name that was setted in config.json
"DEVELOPER_PROVIDER_NAME": "<ProviderName>"
But i got exception that i havent any provider
AWSCognitoCredentialsProvider
getCredentialsWithCognito:authenticated:]_block_invoke |
GetCredentialsForIdentity failed. Error is [Error
Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=8 "(null)"
UserInfo={__type=InvalidParameterException, message=Please provide a
valid public provider}]
When i go to IAM Console to create providerID it ask me to choose type of provider (OpenID or SAML)
So i'm really dont understand what exactly should i put there? what type to select.
For server i used Lambda service with DynamoDB service, for web pages used S3.
According to this flow:
I already complete first 4 steps and i'm stack on 5 step, So i need only to send this data to Cognito (no needs to prepare my custom dev provider, because i already have idendityID and token, thats looks like incorrect).
As result
developer Auth - only for login method from lambda but when i try to use DynamoDB user are not auth.
All roles are added - currently i add temp role for Unath users
{
"Sid": "DynamoDBPolicy",
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"*"
]
}
All works (huh) but it's not really good
Question - how to make Cognito DevAuth with received token from my backEnd solution placed on Lambda? Does i miss something?
UPDATE
thanks to #Rachit Dhall
AWSCognitoIdentity *cognitoIdentity = [AWSCognitoIdentity defaultCognitoIdentity];
AWSCognitoIdentityGetCredentialsForIdentityInput *input = [[AWSCognitoIdentityGetCredentialsForIdentityInput alloc] init];
input.identityId = [task.result valueForKey:#"identityId"];
input.logins = #{
#"cognito-identity.amazonaws.com" : [task.result valueForKey:#"token"]
};
[cognitoIdentity getCredentialsForIdentity:input completionHandler:^(AWSCognitoIdentityGetCredentialsForIdentityResponse * _Nullable response, NSError * _Nullable error) {
//how to update my configuration for AWS with
//AWSCognitoIdentityCredentials object?
}];
but not sure what to do with AWSCognitoIdentityCredentials received in response - how to update my configuration?
Also found next:
Does it mean that there is no another way for this? Or something missed?
The error which says present a valid public provider issue, is because you are calling GetCredentialsForIdentity with your login provider. But instead the flow is that you call GetOpenIdTokenForDeveloperIdentity from your backend, you get a token as response. Then to get credentials you call GetCredentialsForIdentity with key cognito-identity.amazonaws.com in the logins parameter and value as the token received from previous call.
Update:
To make sure Amazon DynamoDB uses credentials vended by Amazon Cognito you can create a default AWSServiceConfiguration with your credentials provider and use this to initialize the client.
// create a configuration that uses the provider
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 provider:credentialsProvider];
// get a client with the default service configuration
AWSDynamoDB *dynamoDB = [AWSDynamoDB defaultDynamoDB];

AWSS3PresignedURLErrorDomain, iOS, AWSSDK v2

Using AWSSDK V2 with iOS 8, went through sample code bases and setup
cognito with proper access to my S3Bucket. following example here
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/S3BackgroundTransfer-Sample/Objective-C
When I try the upload I get this error
Error: Error Domain=com.amazonaws.AWSS3PresignedURLErrorDomain Code=1
"accessKey in credentialProvider can not be nil" UserInfo=0x7d905610
{NSLocalizedDescription=accessKey in credentialProvider can not be nil}
I made sure the role used by cognito poold id has access to my s3 bucket based on the policy I created for it. What could be the problem?
Agfter sebastians commnent, I went back and verified that I am using unauthenticated users, with a role that has access to my s3bucket, and then looked at the last comment he made about cognito being async and about its initialization. I am doing this in this method here
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AWSCognitoCredentialsProvider *credentialsProvider = [AWSCognitoCredentialsProvider credentialsWithRegionType:CognitoRegionType
identityPoolId:CognitoIdentityPoolId];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:DefaultServiceRegionType
credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
// Override point for customization after application launch.
return YES;
}
now when breaking on the line
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
and inspecting in debugger the properties of credentialsProvider, It does have a lot of nil properties !!
credentialsProvider AWSCognitoCredentialsProvider * 0x7886a0d0 0x7886a0d0
NSObject NSObject
_useEnhancedFlow BOOL YES '\x01'
_identityId NSString * nil 0x00000000
_accessKey NSString * nil 0x00000000
_secretKey NSString * nil 0x00000000
_sessionKey NSString * nil 0x00000000
_expiration NSDate * nil 0x00000000
I did create the entity pool and embed the ID in my code, what should I look for here?
I had the exact same problem, however the answers above didn't help me either.
Changing
let CognitoRegionType = AWSRegionType.Unknown
let DefaultServiceRegionType = AWSRegionType.Unknown
To
let CognitoRegionType = AWSRegionType.EUWest1
let DefaultServiceRegionType = AWSRegionType.EUWest1
In Constants.swift solved it for me. Of course this is in the context of the sample application mentioned above.
From the error above, it looks like your CognitoCredentialsProvider has no access key / secret key.
When using Cognito with authenticated users, just creating an IdentityPool and creating an AWSCognitoCredentialsProvider instance is not enough.
You must write code to authenticate your user. Currently Cognito supports user authentication with Amazon, Facebook, Google, any generic OpenID Connect or your own backend.
Once you receive an authentication token from the Identity Provider, you can give it to Cognito (using its .logins property), Cognito will then exchange that token for a valid Access Key and Secret Key.
It looks like your Cognito Identity provider has no access key / secret key yet. The example you linked above is an example of Cognito usage for unauthenticated users - which might be valid for your application, or not. Depending on your use case.
Also remember that CognitoIdentitProvider class is asynchronous (like all AWS iOS SDK classes). Ensure that you are not calling your S3 Transfer Manager class before Cognito is fully initialized.
To better understand Cognito Identity, I would invite you to read http://docs.aws.amazon.com/mobile/sdkforios/developerguide/cognito-auth.html
after much inspection, I found the problem ( lack of documentation ).
I needed to create an online policy for the role used by cognito. when going to IAM services -> roles -> click on role -> permissions, under that tab there are two types of policies managed and inline, before I setup a managed policy which didn't do anything, so I went back and created an inline policy and that is where I give this role access to the S3 bucket, and now it works !!
Hope this helps someone else, and AWS really need to document better.

Resources