Using Amazon Cognito Developer Identities - ios

I am following amazon documentation but it doesn't work as advertised. I have the latest sdk too. The self.identity = code below doesn't work because it is read only.
#implementation DeveloperAuthenticatedIdentityProvider
/*
* Use the token method to communicate with your backend to get an
* identityId and token.
*/
- (AWSTask <NSString*>) token {
//Write code to call your backend:
//Pass username/password to backend or some sort of token to authenticate user
//If successful, from backend call getOpenIdTokenForDeveloperIdentity with logins map
//containing "your.provider.name":"enduser.username"
//Return the identity id and token to client
//You can use AWSTaskCompletionSource to do this asynchronously
// Set the identity id and return the token
self.identityId = response.identityId;
return [AWSTask taskWithResult:response.token];
}
#end
How do I overright this? Thanks.

You have compiler errors in your other question, fix those first Using AWS Cognito and aws-ios-sdk v.2.4.16 with developer identities
The proper signature for the method, as suggested by the compiler is
- (AWSTask<NSString *> *)token;

Related

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];

Unauthenticated user to authenticated user on AWS Cognito

My code is here: code
Reposted because I wanted to ask a more direct question. How do I switch between unauthenticated user and authenticated? My unauthenticated seems cached and I've used these methods:
[credentialsProvider clearCredentials];
[credentialsProvider clearKeychain];
before the rest of my api code and it still doesn't work. Any help is appreciated
Note: I know it's not working because I make a call using lambda right after I switch up my configuration/credentials provider and only authorized users should be able to call this method.
EDIT #behrooziAWS answer:
API CODE:
id<AWSCognitoIdentityProvider> identityProvider = [[DeveloperIdentityProviderClass alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:#"SOMEIDENTITYPOOLID"
logins:#{#"MYIDENTITYPROVIDERNAME": #"MYUSERNAME"}
providerName:#"MYIDENTITYPROVIDERNAME"
];
[credentialsProvider setIdentityProvider:identityProvider];
[credentialsProvider setLogins:#{#"MYIDENTITYPROVIDERNAME": #"MYUSERNAME"}];
[[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){
[self testAuth];
return [BFTask taskWithResult:nil];
}];
Full Error:
BusyTime[27043:7097936] AWSiOSSDKv2 [Verbose] AWSURLResponseSerialization.m line:87 | -[AWSJSONResponseSerializer responseObjectForResponse:originalRequest:currentRequest:data:error:] | Response body: [{"message":"The security token included in the request is invalid."}]
2015-10-20 08:51:17.280 BusyTime[27043:7097936] Error: Error Domain=com.amazonaws.AWSLambdaErrorDomain Code=0 "The operation couldn’t be completed. UnrecognizedClientException" UserInfo=0x7ff27ab41150 {NSLocalizedFailureReason=UnrecognizedClientException, responseStatusCode=403, message=The security token included in the request is invalid., responseHeaders={type = immutable dict, count = 6,
IMPORTANT EDIT: I've hardcoded my refresh to use a working token and identityId. so:
self.identityId = #"someID";
self.token = #"someToken";
return [super getIdentityId];
and then ALL my code is working. But obviously this isn't sustainable, I need to be able to make a call to aws lambda to refresh my credentials. But when I set my identity provider, and set my login, I think it's changing me to my authenticated version, but I need to be in unauthenticated to call aws lambda. Please refer to my code link above and take a look at my refresh method to understand what I'm poorly trying to describe. Also please let me know if this should go in a new thread as this is a slightly different question. Not so familiar with stackoverflow's policies on questions.
Another error: [{"Message":"User: arn:aws:sts::445291524102:assumed-role/Cognito_BusyTimeAuth_Role/CognitoIdentityCredentials is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:445291524102:function:login"}], SO now I'm assuming my auth provider for my refresh which is incorrect login flow. I'm thinking that I switch this up so that I login in my API class. When I return my identity ID and token, I save them to keychain. Finally, I use the above API code to switch my logins and in my refresh method, I simply return what I found in my keychain. The only problem is I'm not sure if this flow is correct because it doesnt actually "refresh" as I'm not calling my backend. I was wondering if I could wrap the refresh by changing back and forth from my unauth role to my auth role but this seems messy.
[credentialsProvder clearKeychain] will clear the identityId, credentials and any logins, so clearCredentials is unnecessary: clearKeychain Documentation
Normally you don't want to clear your identity id when you transition to an authenticated user. If you simply add your provider and valid login token to the logins map and call [credentialsProvider refresh], you will become authenticated with the same identity id. From that point forward, you will only be able to access that identity if you provide a valid login token. If you want to switch identities by logging out and then login as a authenticated user, that is when you use clearKeychain.

Evernote iOS SDK - How do I authenticate with a token?

I am using Evernote SDK for iOS and I am saving the authentication token when the user has authorized access.
Once the user installs my application on a different device, I want to use that token to reauthenticate automatically, but it looks like SDK doesn't support that. Is there a way to do that?
I had the same issue last week, and their SDK indeed doesn't support it out-of-the-box, but after some research I found a solution that works perfectly. This solution mimics a valid authentication flow.
A little background:
When the ENSession class initializes, it retrieves the credentials that are saved on the keychain (unless [[ENSession sharedSession] unauthenticate] was called earlier). The problem is that the keychain is empty when using a different device, so our goal is to add a valid ENCredentials instance to the ENCredentialStore.
Solution:
Add the following imports to your code: ENCredentials.h and ENCredentialStore.h. We will need them later.
Initialize the ENSession like you already do, using setSharedSessionConsumerKey:(NSString *)key consumerSecret:(NSString *)secret optionalHost:(NSString *)host.
In order to create a valid ENCredentials object, we need to provide the following objects:
NSString * host
NSString * edamUserId
NSString * noteStoreUrl
NSString * webApiUrlPrefix
NSString * authenticationToken
NSDate * expirationDate
The host is always www.evernote.com (as defined in ENSession under ENSessionBootstrapServerBaseURLStringUS).
edamUserId is the user id you received when you got the original token. Same for the expirationDate. If you are not sure how to get them then you should use [[ENSession sharedSession].userStore getUserWithSuccess:^(EDAMUser *user) once authenticated.
So the only objects that are actually missing are noteStoreUrl and webApiUrlPrefix. Their format is always:
noteStoreUrl: https://www.evernote.com/shard/edam_shard/notestore
webApiUrlPrefix: https://www.evernote.com/shard/edam_shard/
Luckily, your token already contains edam_shared (value of S=, see this):
#"S=s161:U=5ce3f20:E=1561182201b:C=24eb9d000f8:P=285:A=app:V=2:H=e8ebf56eac26aaacdef2f3caed0bc309"
If you extract s161 and put it in the URLs above it will work (I am sure you know how to extract that, but let me know if you're having problems).
Now we are ready to authenticate using the token. First, expose the necessary functions from ENSession using a category:
#interface ENSession(Authentication)
- (void)startup;
- (void)addCredentials:(ENCredentials *)credentials;
#end
And authenticate using the token:
ENCredentials *credentials = [[ENCredentials alloc] initWithHost:ENSessionBootstrapServerBaseURLStringUS edamUserId:userId noteStoreUrl:noteStoreUrl webApiUrlPrefix:webApiUrlPrefix authenticationToken:token expirationDate:expirationDate];
[[ENSession sharedSession] addCredentials:credentials];
[[ENSession sharedSession] startup];
The last line is important in order to refresh the ENSession and retrieve the new stored credentials.
Now you are authenticated and ready to query the SDK. Good luck.

Unauthenticated identites not mapping to Developer Authenticated Identity

When a user launches the app they are an unauthenticated user and they receive a unauthenticated cognito identity supplied from the DeveloperAuthenticatedIdentityProvider class in the iOS app. I can see that unauthenticated cognito identity in the cognito console. However, when they login and I make a call to my nodejs backend with a logins map of:
{
DevAuthIdentityLogin:<username>
}
and using this backend code:
getCognitoIdentity: function(logins, cognitoId, error) {
var cognitoidentity = new AWS.CognitoIdentity(awsOptions);
var params = {
IdentityPoolId: identityPool,
Logins: logins,
TokenDuration: (60 * 5)
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, function(err, data) {
if (err) {
console.log(err, err.stack);
error(err)
} else {
console.log(data); // successful response
cognitoId(data);
}
});
}
It creates a new identity id with the developer authenticated identity and I can see that in the cognito console, but the old unauthenticated one is not being mapped to this new developer authenticated one.
Do I need to supply the unauthenticated identity id in the logins map when making a call to my backend to associate the two? OR am I not making this call correctly. I need some clarification on how to map/merge these identities please.
I already answered your question on our forums, but you need to include the unauth identitity id as a parameter to the GetOpenIdTokenForDeveloperIdentity call, otherwise Amazon Cognito will have no way of knowing that it should associate your user identifier to that identity.

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