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.
Related
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.
Error is:
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException: Unauthenticated access is not supported for this identity pool. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException;
Please Help me out here.
The process of signin was successful and when i enter Amazon lex chat bot window, I get the above error Unauthenticated access is not supported for this identity pool. This actually makes sense because i have unchecked unauthenticated access to my bot so i know that without authentication no user will be allowed to interact with the Lex ChatBot. But now my signin is successful and still i have this error.
My question is do we need to maintain the user session who has signed in?? If yes how do i do that.
Or
How do i link Login to Cognito Authenticated Identity.
We get this error when the Identity Pool has unauthenticated access disabled and no id token was found in the login map for the application. For example, in Android applications, after initializing CognitoCachingCredentialsProvider you also need to call the setLogins() method and provide a login map.
//Relevant imports
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
//Initialize credentials provider
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"IDENTITY_POOL_ID",
Regions.US_EAST_1
);
//Create a login map
Map<String, String> logins = new HashMap<String, String>();
logins.put("www.amazon.com", "login with Amazon token");
//Set login map
credentialsProvider.setLogins(logins);
credentialsProvider.getCredentials();
//Create clients for AWS services with credentialsProvider as a parameter in the constructor
In the above example, I assumed that 'Login with Amazon' was used. For different providers, change the key "www.amazon.com" with the appropriate token. To know the key, just decode the id token at https://jwt.io and look for iss claim. The value without https:// will be the key for the login map.
As to where to put this code, check if it is using a CognitoCachingCredentialsProvider object anywhere and then add a login map to it. To forcibily refresh credentials, call the refresh() method
for aws amplify enable multiauth
finally,
Do you want to edit your add-to-group function now? (Y/n) n
In your command line run
amplify update auth
then follow the prompt as it is done in the image below
Happy coding...
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
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.
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.