How to set OIDC provider for AWSAssumeRoleWebIdentity - android

I am developing an android app which uses firebase authentication for signin and uses AWS S3 and dynamodb for managing data/images. I am trying to delegate an access to AWS resource via AWSAssumeRoleWebIdentity. The reason I am doing this is AWS Sign-In UI does not allow enough customization for UI and UI flow. I decided to use firebase authentication only for sign-in and sign-up.
Please find the source code and OIDC Provider setting. With them the error log is
No OpenIDConnect provider found in your account for https://securetoken.google.com/[project-name] (Service: AWSSecurityTokenService; Status Code: 400; Error Code: InvalidIdentityToken; Request ID: 37607060-9e1c-11e8-8ae0-636eae27c3bf)
Identity Provider of AWS IAM has been created with the name of "securetoken.google.com/[my-project-name]/" with the Thumbprint that I created referring to [1] and OAuth 2.0 client IDs obtained in Credentials of Google Cloud Service API & Services.
The source code is shown below.
public void uploadImageFile() {
CustomLog.logI("start of uploadImageFile");
setIDToken();
}
private void setIDToken() {
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
// To get ID Token of the user authenticated by google authentication
mUser.getIdToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete (#NonNull Task< GetTokenResult > task) {
if (task.isSuccessful()) {
// Token information is set to mIDToken of the global variable
mIDToken = task.getResult().getToken();
AsyncTaskForAssumeRole asyncTaskForAssumeRole = new AsyncTaskForAssumeRole();
asyncTaskForAssumeRole.execute();
} else {
CustomLog.logE(task.getException().getMessage());
}
}
});
}
public class AsyncTaskForAssumeRole extends AsyncTask<Void, Void, BasicSessionCredentials> {
protected BasicSessionCredentials doInBackground(Void... params) {
try {
// set credentials from AssumeRoleWithWebIdentity
BasicSessionCredentials credentials = setAssumeRoleWithWebIdentity();
return credentials;
} catch (Exception e) {
CustomLog.logE(e.getMessage());
return null;
}
}
protected void onPostExecute(BasicSessionCredentials credentials) {
// upload file with S3 connection
connectToS3ForUpload(credentials);
}
}
private BasicSessionCredentials setAssumeRoleWithWebIdentity(){
CustomLog.logD("start of setAssumeRoleWithWebIdentity");
String ROLE_ARN = [my role arn];
// set AssumeRoleWithWebIdentity request with created policy and token information retrieved through Google Sign in information
AssumeRoleWithWebIdentityRequest request = new AssumeRoleWithWebIdentityRequest()
.withWebIdentityToken(mIDToken)
.withRoleArn(ROLE_ARN)
.withRoleSessionName("wifsession");
BasicAWSCredentials basicCreds = new BasicAWSCredentials("", "");
AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(basicCreds);
AssumeRoleWithWebIdentityResult result = sts.assumeRoleWithWebIdentity(request);
Credentials stsCredentials = result.getCredentials();
String subjectFromWIF = result.getSubjectFromWebIdentityToken();
BasicSessionCredentials credentials = new BasicSessionCredentials(stsCredentials.getAccessKeyId(),
stsCredentials.getSecretAccessKey(),
stsCredentials.getSessionToken());
return credentials;
}
Great thanks in advance.
[1] http://docs.aws.amazon.com/cli/latest/reference/iam/create-open-id-connect-provider.html

Consider using Amazon Cognito Federated Identities (Identity Pools) to federate (map) the user from your Identity Provider into Amazon Cognito and obtain a Cognito Identity Id, which can be used to authorize access to AWS resources. See https://docs.aws.amazon.com/cognito/latest/developerguide/open-id.html for further details.
Map<String, String> logins = new HashMap<String, String>();
logins.put("login.provider.com", token);
credentialsProvider.setLogins(logins);
Now, you can use the credentialsProvider object with an Amazon S3 client.
AmazonS3 s3Client = new AmazonS3Client(getApplicationContext(), credentialsProvider);

Related

Aws mqtt connection using cognito credential provider in android

I am trying to my app connect with mqtt broker using cognito credentials provider. When I was trying to connect to mqtt broker..app saying identity poolId not found.
CognitoUserSession cognitoUserSession = AppHelper.getCurrSession();
String idToken = cognitoUserSession.getIdToken().getJWTToken();
Map<String,String> logins = new HashMap<String, String>();
logins.put("cognito-idp.ap-south-1.amazonaws.com/XXXX_XXXX", idToken);
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"ap-south-1:XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", // Identity pool ID
Regions.AP_SOUTH_1 // Region
);
credentialsProvider.setLogins(logins);
AmazonCognitoIdentity cognitoIdentity = new AmazonCognitoIdentityClient(credentialsProvider);
GetIdRequest getIdReq = new GetIdRequest();
getIdReq.setLogins(logins); //or if you have already set provider logins just use credentialsProvider.getLogins()
getIdReq.setIdentityPoolId(credentialsProvider.getIdentityPoolId());
GetIdResult getIdRes = cognitoIdentity.getId(getIdReq);
AttachPrincipalPolicyRequest attachPolicyReq = new AttachPrincipalPolicyRequest(); //in docs it called AttachPolicyRequest but it`s wrong
attachPolicyReq.setPolicyName(AWS_IOT_POLICY_NAME); //name of your IOTAWS policy
attachPolicyReq.setPrincipal(getIdRes.getIdentityId());
new AWSIotClient(credentialsProvider).attachPrincipalPolicy(attachPolicyReq);
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
#Override
public void onStatusChanged(final AWSIotMqttClientStatus status, Throwable throwable) {
runOnUiThread(new Runnable() {
#Override
public void run() {
txtStatus.setText(status.toString());
}
});
}
});
}catch(final AmazonClientException e){
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),"exception : "+e,Toast.LENGTH_LONG).show();
}
});
Log.d(LOG_TAG,"Exception : "+e);
}
Log file:
com.amazonaws.services.cognitoidentity.model.ResourceNotFoundException: IdentityPool 'ap-south-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' not found. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: ResourceNotFoundException; Request ID: bdd3c355-79b0-48f3-8a18-716fa17c44ce)
Can any one guide me how to resolve this one. What was the wrong in my code.
Thanks in
Advance.
As your log shows, you did not set the Cognito Identity Pool ID and the Region (where your Identity pool is) in your android app. You can find them under sample code of your identity pool in AWS federated identity dashboard.

com.amazonaws.services.cognitoidentity.model.NotAuthorizedException: Invalid login token. Can't pass in a Cognito token

I am using developer authentication in my android app. After successful login, the server sends the client the Cognito token. The intention of this token is to get access to Amazon S3 for uploading images.
I have created a sample project here: TestAWSCognito
Here is the snippet code where I am trying to create AmazonS3Client:
String identityPoolId = "eu-west-1:289fd4a0-2236-4ff4-9c2b-61c93e60bf0a";
AmazonS3Client testClient = null;
Map<String, String> loginsMap = new HashMap<>();
loginsMap.put("cognito-identity.amazonaws.com", "eyJraWQiOiJldS13ZXN0LTExIiwidHlwIjoiSldTIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJldS13ZXN0LTE6MjkyM2IxYWQtNGNiYS00ZTFmLWEyY2YtZGIyNDVmM2Q3NWJiIiwiYXVkIjoiZXUtd2VzdC0xOjI4OWZkNGEwLTIyMzYtNGZmNC05YzJiLTYxYzkzZTYwYmYwYSIsImFtciI6WyJhdXRoZW50aWNhdGVkIiwiZ3JhcGhxbC5oYXJhaiIsImdyYXBocWwuaGFyYWo6ZXUtd2VzdC0xOjI4OWZkNGEwLTIyMzYtNGZmNC05YzJiLTYxYzkzZTYwYmYwYTo1NjI4NDAiXSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkZW50aXR5LmFtYXpvbmF3cy5jb20iLCJleHAiOjE1NDUxNDgzMTYsImlhdCI6MTU0NTA2MTkxNn0.V-iZeAQdsdMb9LkzYNucka5PEYRMBKKTGm5CzZIJYg8Z5ehcq562JbXGJWr7Yea-w2APsbpVxgP8EjHxSLjsMggk2FdVd-m8YhNFwBYL91oph-wFiAIxLVginD3t3_EhmkPduXZgM1mwH1_yNsGqpBY4nr15cgjqLvfyb4t-QJADFFyjd2qpIUoNzU2EQ5ypEKmbVdgOeLCIe6a-L09yzO-M1xdC0Onc8fs5ELOISR8FA5YFJYIgyqfSz9wDmz929rmCV9EjFdNC3Jd_hSC_Ofp6NYjiW1HRTU0a2C3Z3FCNJFzKppQSUt78MWrJblhHJSEboeMoKhzxmkA0VPgNjg");
CognitoCredentialsProvider cognitoCredentialsProvider = new CognitoCachingCredentialsProvider(getApplicationContext(), identityPoolId, Regions.EU_WEST_1);
cognitoCredentialsProvider.setLogins(loginsMap);
try {
AmazonS3Client testClient = new AmazonS3Client(cognitoCredentialsProvider.getCredentials());
if (testClient != null) {
return testClient;
}
}
catch (Exception e) {
e.printStackTrace();
}
The token and identityPoolId are still valid.
When creating the AmazonS3Client it returns an exception: com.amazonaws.services.cognitoidentity.model.NotAuthorizedException: Invalid login token. Can't pass in a Cognito token. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException; Request ID: af373152-0452-11e9-b8c4-c3f49006c33b)
Any help would be appreciated. Thanks!

Credential Provider Not Authorised Exception AWS Android SDK

I was trying to Publish and Subscribe using authenticated user on AWS IoT with Federated Identities. Until I keep getting error CognitoCachingCredentialsProvider: Failure to get credentials
I looked up here. But the snippet they provided is not used by Facebook anymore. How can I fix this issue?
Android Code:
public void IntializeAwsIot() {
clientId = "us-east-1:fcbd66e0-***************";
// Initialize the AWS Cognito credentials provider
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(), // context
AWSConfiguration.AMAZON_COGNITO_IDENTITY_POOL_ID,// Identity Pool ID
AWSConfiguration.AMAZON_COGNITO_REGION // Region
);
Region region = Region.getRegion(AWSConfiguration.AMAZON_COGNITO_REGION);
// MQTT Client
mqttManager = new AWSIotMqttManager(clientId, CUSTOMER_SPECIFIC_ENDPOINT);
// The following block uses IAM user credentials for authentication with AWS IoT.
//awsCredentials = new BasicAWSCredentials("ACCESS_KEY_CHANGE_ME", "SECRET_KEY_CHANGE_ME");
//btnConnect.setEnabled(true);
// The following block uses a Cognito credentials provider for authentication with AWS IoT.
new Thread(new Runnable() {
#Override
public void run() {
awsCredentials = credentialsProvider.getCredentials();
Connect();
}
}).start();
}
Error:
/com.amazon.mysampleapp E/CognitoCachingCredentialsProvider: Failure to get credentials
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException: Access to Identity 'us-east-1:fcbd66e0-**************' is forbidden. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException; Request ID: 0fa5100d-88a0-11e7-af8c-854a7b8add4d)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:729)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:405)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:212)
at com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.invoke(AmazonCognitoIdentityClient.java:558)
at com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.getCredentialsForIdentity(AmazonCognitoIdentityClient.java:388)
at com.amazonaws.auth.CognitoCredentialsProvider.populateCredentialsWithCognito(CognitoCredentialsProvider.java:691)
at com.amazonaws.auth.CognitoCredentialsProvider.startSession(CognitoCredentialsProvider.java:617)
at com.amazonaws.auth.CognitoCredentialsProvider.getCredentials(CognitoCredentialsProvider.java:388)
at com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials(CognitoCachingCredentialsProvider.java:442)
at com.mysampleapp.AWSIoT.PubSub$1.run(PubSub.java:69)
at java.lang.Thread.run(Thread.java:818)
The problem was there was no credential in the Cognito Cache. So we need to save the appropriate credential so the get credential function can work.
We need to just add the following.
Map<String, String> logins = new HashMap<String, String>();
logins.put("graph.facebook.com", AccessToken.getCurrentAccessToken().getToken());
credentialsProvider.setLogins(logins);

Microsoft Azure Mobile authentication with Google provider SDK in Android

I am currently testing Microsoft Azure and the App Service/Mobile Apps feature using a native Android app and C# on the back end.
I started with the Getting Started application (ToDo) as the base app and now I am trying to enable Authentication using the https://azure.microsoft.com/en-us/documentation/articles/app-service-authentication-overview/ page and Google as the provider.
So far I have
created a Google project with a OAuth Web client
the authorized redirect uri set there is: https://.azurewebsites.net/.auth/login/google/callback
in the Azure portal and the App Service instance I have enabled Authorization/Authentication
the "Action to take when request is not authenticated" option is set to "Allow Request"
For the Google Provider I have set the Client Id and Client Secret
In the Android app I am using the GoogleApiClient class to let the user select a Google Account. Also I get the ID token and the Server Auth Code
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(getString(R.string.server_client_id))
.requestServerAuthCode(getString(R.string.server_client_id))
.build();
mScopes = gso.getScopeArray();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
Once the user picks up an account I retrieve the token and code and then I ask for an access token using the GoogleAuthUtil class. After I get the access_token I try to exchange it with an App Service token (authenticate2 method)
private void handleSignInResult(GoogleSignInResult result) {
Log.d("", "handleSignInResult: " + result.isSuccess());
if(result.isSuccess()) {
final GoogleSignInAccount account = result.getSignInAccount();
final String idToken = account.getIdToken();
String serverAuthCode = account.getServerAuthCode();
mSignInButton.setVisibility(View.GONE);
mGoogleUserText.setText(account.getDisplayName());
mGoogleUserText.setVisibility(View.VISIBLE);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.edit().putString("idToken", idToken).commit();
prefs.edit().putString("serverAuthCode", serverAuthCode).commit();
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
try {
StringBuilder scopesBuilder = new StringBuilder("oauth2:");
for(Scope scope : mScopes) {
scopesBuilder//.append("https://www.googleapis.com/auth/")
.append(scope.toString())
.append(" ");
}
String token = GoogleAuthUtil.getToken(ToDoActivity.this,
account.getEmail(), scopesBuilder.toString());
return token;
} catch (IOException | GoogleAuthException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ToDoActivity.this);
prefs.edit().putString("accessToken", result).apply();
authenticate2();
}
}.execute();
} else {
mSignInButton.setVisibility(View.VISIBLE);
mGoogleUserText.setVisibility(View.GONE);
}
}
private void authenticate2() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String idToken = prefs.getString("idToken", null);
String serverAuthCode = prefs.getString("serverAuthCode", null);
String accessToken = prefs.getString("accessToken", null);
JsonObject json = new JsonObject();
json.addProperty("access_token", accessToken);
json.addProperty("id_token", idToken);
json.addProperty("authorization_code", serverAuthCode);
ListenableFuture<MobileServiceUser> loginFuture =
mClient.login(MobileServiceAuthenticationProvider.Google, json);
Futures.addCallback(loginFuture, new FutureCallback<MobileServiceUser>() {
#Override
public void onSuccess(MobileServiceUser result) {
createTable();
}
#Override
public void onFailure(Throwable t) {
Log.e(TAG, t.getMessage(), t);
}
});
}
So I am using the MobileServiceClient.login() method to send back to the server the access_token of the user in order to get back an Azure session.
Nevertheless, this call fails and I get back a MobileServiceException:
com.microsoft.windowsazure.mobileservices.MobileServiceException: You do not have permission to view this directory or page.
Any ideas what am I missing here?
Thanks
Well this is more than embarassing :-)
I first tried to "manually" verify the id_token using the Google tokeninfo endpoint:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=
but I was getting a generic error that didn't give a lot of info.
Then I used the Google API Java client library and created a small test to verify my token (more info here: https://developers.google.com/identity/sign-in/android/backend-auth)
That test was failing as well and I realized that the reason for that was the expiring time of my token which was smaller than the current time. And this was happening because my emulator time was not correct!
When I set the "correct" timezone everything worked as expected.
Sorry for the post guys. You can use the code here as a template and don't forget to check your emulator time :-)

Why use Audience instead of Scopes with App Engine Cloud Endpoints from Android?

I'm developing an app for Android that uses Cloud Endpoints (Python) with Google Accounts as authentication. I've got everything set up pretty nicely, it all works great from the API Explorer, I've managed to generate the Gradle client library and use it in Android Studio, ...
Problem I have/had is this: When authenticating from Android using Audience (as the guide says) I can't get requests to complete. The oauth Bearer token App Engine receives is huge (over 800 characters) and my logs shows this message: Oauth framework user didn't match oauth token user. The audience I used is correct, when I enter a typo in the audience Google refuses to give me a token in the first place. If I authenticate using scopes (only replacing GoogleAccountCredential.usingAudience with .usingOAuth2) everything works fine and I get a 'regular', shorter token.
What am I doing wrong that results in the huge tokens when using the Audience, and why should I be using Audience instead of scopes in the first place?
Server code:
API = endpoints.api(
name='apiDaycare',
version='v1',
description='API for Appy Daycare',
audiences=['--WEB CLIENT ID HERE--'],
scopes=[endpoints.EMAIL_SCOPE, 'https://www.googleapis.com/auth/plus.login'],
allowed_client_ids=[endpoints.API_EXPLORER_CLIENT_ID, '--Android app client ID here--', '--web client ID here'],
canonical_name='Appy Daycare API',
owner_name='Ambroos Vaes',
title='Appy Daycare API',
auth_level=AUTH_LEVEL.REQUIRED)
App code:
private void LoginTest(String accountName) {
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, Constants.AUDIENCE);
credential.setSelectedAccountName(accountName);
AppyDaycareAPI.Builder builder = new AppyDaycareAPI.Builder(
AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), credential
);
builder.setApplicationName("Appy Daycare Android");
AppyDaycareAPI service = builder.build();
TempTestTask task = new TempTestTask(this, service, credential);
task.execute();
}
private class TempTestTask extends AsyncTask<Void, Void, ModelsUsersAccountMessage> {
private final Context context;
private final AppyDaycareAPI service;
private final GoogleAccountCredential credential;
public TempTestTask(Context context, AppyDaycareAPI service, GoogleAccountCredential credential) {
this.context = context;
this.credential = credential;
this.service = service;
}
protected ModelsUsersAccountMessage doInBackground(Void... unused) {
Log.d("Account API test", "Token:");
Log.d("Account API test", credential.getToken());
ModelsUsersAccountMessage result = null;
result = service.account().echo().execute();
return result;
}
#Override
protected void onPostExecute(ModelsUsersAccountMessage modelsUsersAccountMessage) {
Log.d("Account API test", modelsUsersAccountMessage.toPrettyString());
}
}

Categories

Resources