How can i authenticate to my Identity/User Pool with Cognito Identity? - android

I have been trying to solve this for a few days now but i can't figure out what the problem is.
I have a user pool and an identity pool on AWS Cognito. I have run the sample code and used it to log in and register users with Facebook and Google+ successfully. After login i am able to authenticate using the token to use SNS or the Sync services.
I have registered a user and got a token (after logging in) with Cognito Identity. However the next step is to use the token to authenticate into the identity pool. This is where i keep getting the error
I have followed the instructions from Integrating User Pools with Amazon Cognito Identity and I get a token which I then try to pass it along with the pool id to the credentials provider.
credentialsProvider.clear();
credentialsProvider.withLogins(loginMap).refresh();
My code fails on refresh(). At this point credentialsProvider is a AWSBasicCognitoIdentityProvider and refresh() fails in the super.refresh() which calls getIdentityId() in the abstract parent class AWSAbstractCognitoIdentityProvider
#Override
public String getIdentityId() {
if (identityId == null) {
GetIdRequest getIdRequest = new GetIdRequest()
.withAccountId(getAccountId())
.withIdentityPoolId(getIdentityPoolId())
.withLogins(loginsMap);
appendUserAgent(getIdRequest, getUserAgent());
GetIdResult getIdResult = cib.getId(getIdRequest);
No matter what values i put in the loginsMap, i get the error
Invalid login token. Issuer doesn't match providerName
at cid.getid(getIdRequest).
Most of the code is available to download free if you create an app in the Mobile Hub and generate the code. You can then configure authentication providers - FB, Google , etc.
What am I doing wrong ?
Thanks.
Edit : Here are examples of what i have tried
The token is always the object in the map. Below are the different keys
"cognito-identity.amazonaws.com"
the User Pool id - us-east-1_xxxxxxxxx
the User Pool ARN - cognito-idp:us-east-1:xxxxxxxxxxxx:userpool/us-east-1_xxxxxxxxx

OK i found the answer - I was putting in the wrong key value in the loginMap.
it should be
cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>
as it says in the documentation linked in the question
I don't know how I didn't see that.

Related

Android AWS Cognito app - User sign-in WITHOUT embedding sensitive info (like secret key)

TL;DR W/o distributed private keys, how can I perform the auth flow for an Android app where the Identity Provider is Cognito itself, and the user only signs-up/in with email/password?
I'm developing an Android app in which I want the user to sign-in with "email and password" (NOT Google, Facebook, etc.) and then be able to download/upload objects from S3. I've worked with AWS for months, but all the while the sign-in functionality of my app was built on the sample app (the source of which has apparently changed substantially), which used the "AWSConfiguration" class to store:
AWS_MOBILEHUB_USER_AGENT
AMAZON_COGNITO_REGION
AMAZON_COGNITO_IDENTITY_POOL_ID
AMAZON_COGNITO_USER_POOL_ID
AMAZON_COGNITO_USER_POOL_CLIENT_ID
AMAZON_COGNITO_USER_POOL_CLIENT_SECRET
I need to implement sign-in and authorization to use AWS modules (like S3) WITHOUT embedding sensitive info.
It seems clear to me that this is going to be a "roll my own" situation so in going back to the good ol' start of the documentation on cognito, I'm already to confused as to why my "Email and password" sign-in model fits neither the bill of a "Public Provider" nor a "Developer Authenticated Identity":
From Mobile Hub:
In Cognito > MyUserPool > App Integration > App client settings it still appears as an identity provider of some kind (also it worked without me having to "enable" it):
The big question:
Is it possible to implement a sign-in flow where the user enters an email and a password, gets signed-in with Cognito, and the app has the necessary info to instantiate something like S3Object = new S3Client(credentialsProvider).getObject(...) without requiring a (separate/proxy) server to process private keys?
Also, are callback/sign-out URLs necessary for an app (and if so, why)?
Regarding your question on the obfuscation of service constants in the SDK, there is no standard method to do this - which offers guaranteed security. There are several third party and server based solutions. However Cognito does not recommend any specific technique for obfuscation.
To implement Sign-In and get credentials to access AWS resources, you can add Cognito as a Authentication Provider for your identity-pool. With this you will be able to use ID token vended by Cognito to access AWS resources. This code snippet shows how to use tokens issued by Cognito UserPools to access AWS resources in Android.
// Get id token from CognitoUserSession.
String idToken = cognitoUserSession.getIdToken().getJWTToken();
// Create a credentials provider, or use the existing provider.
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(context, IDENTITY_POOL_ID, REGION);
// Set up as a credentials provider.
Map<String, String> logins = new HashMap<String, String>();
logins.put("cognito-idp.us-east-1.amazonaws.com/us-east-1_123456789", cognitoUserSession.getIdToken().getJWTToken());
credentialsProvider.setLogins(logins);
See this tutorial for further detail - http://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-integrating-user-pools-android.html#tutorial-integrating-user-pools-getting-aws-credentials-android.

Token is not from a supported provider of this identity pool Amazon Mobile Hub Android

When I click Google Login I'm getting Token in onActivityResult method using the below code:
GoogleSignInAccount account = result.getSignInAccount();
String token = account.getIdToken();
credentialsProvider = new CognitoCachingCredentialsProvider(
Login.this, // Context
"My Pool ID", // Identity Pool ID
Regions.US_EAST_1 // Region
);
I have added the Google client ID in Cognito using manage Federated Identities.
I have cross checked all the keys in IAM accounts.google.com, everything seems to be perfect.
final Map<String, String> logins = new HashMap<String, String>();
logins.put("accounts.google.com", account.getIdToken());
credentialsProvider.setLogins(logins);
credentialsProvider.refresh();
When I try to get the identity ID using the below code I'm getting error - Token is not from a supported provider of this identity pool.
What could be the mistake?
credentialsProvider.getIdentityId();
In my case, I had a trailing slash in my IAM identity provider for accounts.google.com, like this:
The one with the trailing slash is wrong; the one without the trailing slash works correctly. It's interesting that AWS will fetch the same thumbprint for both of those.
In the AWS IAM console under Accounts > Providers > accounts.google.com, add the key for "Android client for com.example.yourstuff (auto created by Google Service)" as an audience. It looks something like "222222222222-x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8x8.apps.googleusercontent.com" (Then, when you're debugging, go ahead and all the rest of the keys as audience entries; you can go back later and figure out which ones you can remove.)
In the call to GoogleSignInOptions.Builder, you need a call to #requestIdToken using your web application key under OAuath 2.0 client IDs on the Goole APIs > API Manager > Credentials page:
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("999999whateverxxxx.apps.googleusercontent.com")
.build()
(The token can get cached; if you run your app with the requestIdToken call, then remove the requestIdToken call, and run again, you can still get a result from a call to getIdToken() on the GoogleSignInAccount object.)
The google login code will eventually give you a GoogleSignInAccount object. Call #getIdToken on that object to get a string (in my case, it's 83 chars) that you're going to put in the login hash:
// pseudocode...
private fun fn(x: GoogleSignInAccount) {
val token = x.idToken // getIdToken if you're still using Java
val logins = HashMap<String, String>()
logins.put("accounts.google.com", token);
credentialsProvider.logins = logins
...
If you don't have the right key listed in IAM > Providers > accounts.google.com, you'll get a NotAuthorizedException(Invalid login token. Incorrect token audience.) exception.
If you added that extra slash to accounts.google.com/, you'll get a NotAuthorizedException(Token is not from a supported provider of this identity pool.)
If you try to add accounts.google.com/ to the login hash like this (don't do this, fix the IAM identity provider name instead):
logins.put("accounts.google.com/", token);
You'll get a NotAuthorizedException(Invalid login token. Issuer doesn't match providerName) exception.
If you use the wrong token you'll get a NotAuthorizedException (Invalid login token. Token signature invalid.) exception.
(I suspect there are many other ways to fail; these are just the one's I've found.)
First check if you are using correct user pool id.
If yes then open aws cognito console , select Federated Identities then select identity pool you are passing in "Auth.configure". Then click "Edit identity pool" then goto tab "Authentication providers". Under it, first tab is of "Cognito", press unlock of User Pool ID and App client id and pass correct value there.
Then you will able to login successfully.
You need to add the Google Provider App ID in your backend configuration with Cognito to make it work correctly. You can do that from Cognito Console or Mobile Hub Console having your pool id.
Thanks,
Rohan

Android, Cognito: can sign in, but can't make authenticated AWS calls

In an Android app, I can successfully authenticate with Cognito, which returns a CognitoIdToken and a CognitoAccessToken.
But I can't figure out how to use that to make actual authenticated call to, say Lambda or S3. It seems that there's a chasm between CognitoIdentityProvider (which I seem to be able to work with) and CognitoCredentialsProvider (which I do not).
Thanks for ideas, or even a pointer to a sample that uses Cognito and authenticated calls / roles.
[edit] Bonus question: Can someone explain why there is an AWSCognito* and an AmazonCognito*, and why they seem almost completely unrelated?
Take a look at your Federated identities pool in the aws console. Look in the identity browser for the identityId that you are getting authenticated with (from your logs).
Here is the key thing: if the identity id does not show logins, then you never have gotten authenticated. *When I say logins, there is a login count on each identity, and it shows the identityProviderName that the authentication occured with if you click on the identityID.
This is a simple test, if you have logins you are authenticated. if you don't, you aren't. Now why none of the documentation at AWS says that... well.. who knows.
if you ARE authenticated, then the SDK stores the tokens, and you don't need to worry about it you can begin working with aws with your service configuration.
(The response is based upon the IOS sdk, but I believe for these matters, they are the same - The diagrams in here may help you, but these are my notes for IOS SDK, and I admit I have not tried this in Android).
I finally determined that I was missing calls to set the tokens into the credentials provider:
final CognitoCachingCredentialsProvider credentialsProvider = getCredentialsProvider(applicationContext);
Map<String, String> logins = new HashMap<String, String>();
logins.put(Constants.COGNITO_USER_POOL_LOGIN_STRING, cognitoUserSession.getIdToken().getJWTToken());
credentialsProvider.setLogins(logins);
where Constants.COGNITO_USER_POOL_LOGIN_STRING is like "cognito-idp.us-west-2.amazonaws.com/" + MY_COGNITO_USER_POOL_ID. After doing that, a TransferUtility object was able to download with authentication. The TransferUtility was previously obtained like this (edited):
CognitoCredentialsProvider cp = . . .
AmazonS3Client s3Client = new AmazonS3Client(cp);
TransferUtility s3transferUtility = new TransferUtility(s3Client);

Android aws cognito Invalid login token. Not a Cognito token

I am trying to make aws android cognito work with only developer authenticated identities. But getting the below exception (sdk version 2.2.16).
I have cross checked identityId and identityPoolId and both values are correct.
In the loginsMap, I am using key as "cognito-identity.amazonaws.com" and token as the openId token received from my server back end.
The loginsMap is being set in the refresh and getIdentityId method of my android AWSAbstractCognitoDeveloperIdentityProvider class.
Anybody facing this kind of similar issue ?
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException: Invalid login token. Not a Cognito token. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException; Request ID: a270a26a-18fa-11e6-add3-c1b3dbd555de)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:716)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:392)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:199)
at com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.invoke(AmazonCognitoIdentityClient.java:533)
at com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.getCredentialsForIdentity(AmazonCognitoIdentityClient.java:468)
at com.amazonaws.auth.CognitoCredentialsProvider.populateCredentialsWithCognito(CognitoCredentialsProvider.java:627)
at com.amazonaws.auth.CognitoCredentialsProvider.startSession(CognitoCredentialsProvider.java:553)
at com.amazonaws.auth.CognitoCredentialsProvider.refresh(CognitoCredentialsProvider.java:503)
at com.amazonaws.auth.CognitoCachingCredentialsProvider.refresh(CognitoCachingCredentialsProvider.java:463)
at com.amazonaws.auth.CognitoCachingCredentialsProvider.getIdentityId(CognitoCachingCredentialsProvider.java:414)
at com.amazonaws.auth.CognitoCredentialsProvider.populateCredentialsWithCognito(CognitoCredentialsProvider.java:621)
at com.amazonaws.auth.CognitoCredentialsProvider.startSession(CognitoCredentialsProvider.java:553)
at com.amazonaws.auth.CognitoCredentialsProvider.refresh(CognitoCredentialsProvider.java:503)
at com.amazonaws.auth.CognitoCachingCredentialsProvider.refresh(CognitoCachingCredentialsProvider.java:463)
You should put the developer provider name as key in the logins map and the SDK will take care of rotating that to "cognito-identity.amazonaws.com" for you if required.
Another thing you must do is update the identity id that you get from your back end along with the token. If the identity id in the SDK cache is A and the token says it belongs to identity id B, we will throw "Not a Cognito token." error. Ideally you should use the update() method to update both identity id and token after they are refreshed from your back end.
Developer Authenticated Identities explains this in more detail. You should also look at the flow in our sample app. Hope this helps.

Android and AWS Cognito Developer Authenticated Identities

I'm building an Android app that will utilize Cognito for user authentication. I'm using Developer Authenticated Identities and retrieving an Identity ID and token from my backend service successfully however I'm not able to figure how to us the Identity ID and token for S3. I have done this successfully in JavaScript like so...
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
IdentityId: identityid,
Logins: {
'cognito-identity.amazonaws.com': token
}
});
I've tried with...
Map logins = new HashMap();;
logins.put("cognito-identity.amazonaws.com", token);
credentials = new CognitoCachingCredentialsProvider (
context,
"us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
Regions.US_EAST_1
);
credentials.setLogins(logins);
s3 = new AmazonS3Client(credentials);
List bucket = s3.listBuckets();
but this throws an exception...
Invalid login token. Can't pass in a Cognito token. I've also tried implement my own identity provider class as detailed here...
https://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html
but this is also unsuccessful. Can someone point my in the right direction?
Thanks in advance.
I did this with Node.js, but I assume Android SDK have a similar API. You get and IdentityId after creating an identity (either with Oauth provider, or without), then you request IAM temporary credentials for that identityId. You will have an accessKey, secretKey and a token, use them to configure the AWS.config.
After that you can use any service from AWS SDK, but you need to make suer that your pool of identities have the right roles/policies attached to it.
NB: give the least and the only needed access, or you will expose your services for malicious use!
The token have expiration date, which you can set, I didn't checked if AWS Cognito refresh it automatically or not.

Categories

Resources