I have the the authenticated user with federated ID. But when I try to access the AWS IOT stuff I get this error which is driving me crazy.
I am following the iot sample code. All the relevant credentials are correct too.
`MQTTHelper`
....
credentialsProvider = new CognitoCachingCredentialsProvider(
mContext.getApplicationContext(), // context
BuildConfig.COGNITO_POOL_ID, // Identity Pool ID
MY_REGION // Region
);
Region region = Region.getRegion(MY_REGION);
mqttManager = new AWSIotMqttManager(clientId, BuildConfig.CUSTOMER_SPECIFIC_ENDPOINT);
mqttManager.setKeepAlive(10);
mAwsIotDataClient = new AWSIotDataClient(credentialsProvider);
String iotDataEndpoint = BuildConfig.CUSTOMER_SPECIFIC_ENDPOINT;
mAwsIotDataClient.setEndpoint(iotDataEndpoint);
mAwsIotDataClient.setRegion(region);
// mqttManager.setMqttLastWillAndTestament(lwt);
mIotAndroidClient = new AWSIotClient(credentialsProvider);
mIotAndroidClient.setRegion(region);
keystorePath = mContext.getFilesDir().getPath();
keystoreName = BuildConfig.KEYSTORE_NAME;
keystorePassword = BuildConfig.KEYSTORE_PASSWORD;
certificateId = BuildConfig.CERTIFICATE_ID;
// To load cert/key from keystore on filesystem
try {
if (AWSIotKeystoreHelper.isKeystorePresent(keystorePath, keystoreName)) {
if (AWSIotKeystoreHelper.keystoreContainsAlias(certificateId, keystorePath,
keystoreName, keystorePassword)) {
Log.d(LOG_TAG, "Certificate " + certificateId
+ " found in keystore - using for MQTT.");
// load keystore from file into memory to pass on connection
clientKeyStore = AWSIotKeystoreHelper.getIotKeystore(certificateId,
keystorePath, keystoreName, keystorePassword);
//btnConnect.setEnabled(true);
Log.i(LOG_TAG, "Connected....");
//CONNECTED_TO_DEVICE = true;
} else {
Log.i(LOG_TAG, "Key/cert " + certificateId + " not found in keystore.");
}
} else {
Log.i(LOG_TAG, "Keystore " + keystorePath + "/" + keystoreName + " not found.");
}
} catch (Exception e) {
Log.e(LOG_TAG, "An error occurred retrieving cert/key from keystore.", e);
}
if (clientKeyStore == null) {
IS_CERTIFICATE_GENERATED = false;
Log.i(LOG_TAG, "Cert/key was not found in keystore - creating new key and certificate.");
doGenerateNewCertificate();
} else {
IS_CERTIFICATE_GENERATED = true;
doMqttConnect();
}
}
private static void doMqttConnect() {
Log.d(LOG_TAG, "clientId = " + clientId);
try {
mqttManager.connect(clientKeyStore, new AWSIotMqttClientStatusCallback() {
#Override
public void onStatusChanged(final AWSIotMqttClientStatus status,
final Throwable throwable) {
Log.d(LOG_TAG, "Status = " + String.valueOf(status));
if (mqttManagerConnStatus != null) {
//Send Mqtt Manager Status Back
mqttManagerConnStatus.onStatusChanged(status, throwable);
}
}
});
} catch (final Exception e) {
Log.e(LOG_TAG, "Connection error.", e);
}
and similarly as mentioned in the sample code I am calling GetShadow() in another class
GetThingShadowRequest getThingShadowRequest = new GetThingShadowRequest() .withThingName(thingName);
GetThingShadowResult result = mDashboard.mqttHelper.doGetAwsIotDataClient()
.getThingShadow(getThingShadowRequest);
byte[] bytes = new byte[result.getPayload().remaining()];
result.getPayload().get(bytes);
String resultString = new String(bytes);
return new AsyncTaskResult<String>(resultString);
I am able to get the KMS working so there is no problem with the authenticated (federated Id). The only source of information I get on AWS IOT is just this which is not helpful from client perspective.
Is it the issue with the AWS IOT configuration or code issue? I have to subscribe to the Thing Group, is there anything else I need to do to subscribe to the group?
This is the Thing Group ARN that I needs to subscribe to
arn:aws:iot:us-east-1:XXXXXXXXXX:thinggroup/A_GROUP
Stack Trace
getShadowTask
com.amazonaws.AmazonServiceException: null (Service: AWSIotData; Status Code: 403; Error Code: ForbiddenException; Request ID: f78eea4d-9053-4b19-1840-297dd67c2667)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:730)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:405)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:212)
at com.amazonaws.services.iotdata.AWSIotDataClient.invoke(AWSIotDataClient.java:571)
at com.amazonaws.services.iotdata.AWSIotDataClient.getThingShadow(AWSIotDataClient.java:406)
at com.lyrebird.abc.device.MyDevicesFragment_RV_Adapter$GetShadowTask.doInBackground(MyDevicesFragment_RV_Adapter.java:519)
at com.lyrebird.abc.device.MyDevicesFragment_RV_Adapter$GetShadowTask.doInBackground(MyDevicesFragment_RV_Adapter.java:497)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
06-18 06:00:54.029 7489-7489/com.lyrebird.abc E/com.lyrebird.abc.device.MyDevicesFragment_RV_Adapter.GetShadowTask: getShadowTask
com.amazonaws.AmazonServiceException: null (Service: AWSIotData; Status Code: 403; Error Code: ForbiddenException; Request ID: f78eea4d-9053-4b19-1840-297dd67c2667)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:730)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:405)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:212)
at com.amazonaws.services.iotdata.AWSIotDataClient.invoke(AWSIotDataClient.java:571)
at com.amazonaws.services.iotdata.AWSIotDataClient.getThingShadow(AWSIotDataClient.java:406)
at com.lyrebird.abc.device.MyDevicesFragment_RV_Adapter$GetShadowTask.doInBackground(MyDevicesFragment_RV_Adapter.java:519)
at com.lyrebird.abc.device.MyDevicesFragment_RV_Adapter$GetShadowTask.doInBackground(MyDevicesFragment_RV_Adapter.java:497)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:*",
"lambda:*"
],
"Resource": [
"*"
]
}
]
}
Here are the few reasons why you might be getting error 403
In Cognito, there are no appropriate permissions for Update/Get Shadow both for authenticated and unauthenticated pool
The ARN of the Cognito Pool id as well as the IoT are incorrect
Check the IAM policy and the following policy to the Cognito users, Also for the Cognito user, you have to attach AttachPrincipalPolicy policy to give them appropriate permissions for Get/update the shadow. The policy below should be in the Cognito Auth and UnAuth roles.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:AttachPrincipalPolicy"
],
"Resource": [
"*"
]
}
] }
Related
this is my code
try {
if (AWSIotKeystoreHelper.isKeystorePresent(keystorePath, keystoreName)) {
if (AWSIotKeystoreHelper.keystoreContainsAlias(certificateId, keystorePath,
keystoreName, keystorePassword)) {
Log.i(LOG_TAG, "Certificate " + certificateId
+ " found in keystore - using for MQTT.");
// load keystore from file into memory to pass on connection
clientKeyStore = AWSIotKeystoreHelper.getIotKeystore(certificateId,
keystorePath, keystoreName, keystorePassword);
btnConnect.setEnabled(true);
mqttManager.setAutoReconnect(false);
} else {
Log.i(LOG_TAG, "Key/cert " + certificateId + " not found in keystore.");
}
} else {
Log.i(LOG_TAG, "Keystore " + keystorePath + "/" + keystoreName + " not found.");
}
} catch (Exception e) {
Log.e(LOG_TAG, "An error occurred retrieving cert/key from keystore.", e);
}
if (clientKeyStore == null) {
Log.i(LOG_TAG, "Cert/key was not found in keystore - creating new key and certificate.");
new Thread(new Runnable() {
#Override
public void run() {
try {
// Create a new private key and certificate. This call
// creates both on the server and returns them to the
// device.
CreateKeysAndCertificateRequest createKeysAndCertificateRequest =
new CreateKeysAndCertificateRequest();
createKeysAndCertificateRequest.setSetAsActive(true);
final CreateKeysAndCertificateResult createKeysAndCertificateResult;
createKeysAndCertificateResult =
mIotAndroidClient.createKeysAndCertificate(createKeysAndCertificateRequest);
Log.i(LOG_TAG,
"Cert ID: " +
createKeysAndCertificateResult.getCertificateId() +
" created.");
// store in keystore for use in MQTT client
// saved as alias "default" so a new certificate isn't
// generated each run of this application
AWSIotKeystoreHelper.saveCertificateAndPrivateKey(certificateId,
createKeysAndCertificateResult.getCertificatePem(),
createKeysAndCertificateResult.getKeyPair().getPrivateKey(),
keystorePath, keystoreName, keystorePassword);
// load keystore from file into memory to pass on
// connection
clientKeyStore = AWSIotKeystoreHelper.getIotKeystore(certificateId,
keystorePath, keystoreName, keystorePassword);
// Attach a policy to the newly created certificate.
// This flow assumes the policy was already created in
// AWS IoT and we are now just attaching it to the
// certificate.
AttachPrincipalPolicyRequest policyAttachRequest =
new AttachPrincipalPolicyRequest();
policyAttachRequest.setPolicyName(AWS_IOT_POLICY_NAME);
policyAttachRequest.setPrincipal(createKeysAndCertificateResult
.getCertificateArn());
mIotAndroidClient.attachPrincipalPolicy(policyAttachRequest);
runOnUiThread(new Runnable() {
#Override
public void run() {
btnConnect.setEnabled(true);
}
});
} catch (Exception e) {
Log.e(LOG_TAG,
"Exception occurred when generating new private key and certificate.",
e);
}
}
}).start();
}
}
View.OnClickListener connectClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(LOG_TAG, "clientId = " + clientId);
try {
mqttManager.connect(clientKeyStore, new AWSIotMqttClientStatusCallback() {
#Override
public void onStatusChanged(final AWSIotMqttClientStatus status,
final Throwable throwable) {
Log.d(LOG_TAG, "Status = " + String.valueOf(status));
runOnUiThread(new Runnable() {
#Override
public void run() {
if (status == AWSIotMqttClientStatus.Connecting) {
tvStatus.setText("Connecting...");
} else if (status == AWSIotMqttClientStatus.Connected) {
tvStatus.setText("Connected");
} else if (status == AWSIotMqttClientStatus.Reconnecting) {
if (throwable != null) {
Log.e(LOG_TAG, "Connection error.", throwable);
}
tvStatus.setText("Reconnecting");
} else if (status == AWSIotMqttClientStatus.ConnectionLost) {
if (throwable != null) {
Log.e(LOG_TAG, "Connection error.", throwable);
}
tvStatus.setText("Disconnected");
} else {
tvStatus.setText("Disconnected");
}
}
});
}
});
} catch (final Exception e) {
Log.e(LOG_TAG, "Connection error.", e);
tvStatus.setText("Error! " + e.getMessage());
}
}
};
when i'm trying to connect with aws iot mqtt broker using android phone i got error as under:
E/com.amazonaws.demo.androidpubsub.PubSubActivity: Connection error.
Connection lost (32109) - java.io.EOFException
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:146)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.io.EOFException
at java.io.DataInputStream.readByte(DataInputStream.java:77)
at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:65)
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:107)
at java.lang.Thread.run(Thread.java:818)
The above exception can be due to variety of reasons such as loss of network connectivity, policy restrictions to connect or subscribe etc. Unfortunately, Mqtt paho client does not always propagate the connectivity exceptions perfectly and so it might be difficult to root cause the issue just from this exception. It seems you are following this sample app. I was able to get the app working with the README instructions. Following are some of the points that I suspect might cause this issue:
Ensure that the following IAM Policy is attached to the unauthenticated role created as part of the Identity Pool creation
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:AttachPrincipalPolicy",
"iot:CreateKeysAndCertificate"
],
"Resource": [
"*"
]
}
]
}
Ensure that the following IoT Policy is attached to the device certificate
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": ""
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Subscribe",
"iot:Receive"
],
"Resource": ""
}
]
}
Hope it helps!
As already suggested, the problem is almost certainly due to not having an appropriate policy attached, but this is further complicated by poor error handling in the AWS sample code. The code above first looks in the device keystore for a saved certificate and, if found, tries to connect using it. If it cannot find a certificate under the expected keystore name, it then creates a new certificate, saves it in the keystore, and then tries to attach the principle policy to it. If anything goes wrong getting the policy from AWS, then you are left with an incorrectly configured entry in the keystore and the code as supplied by AWS can never recover from that. I had spent many hours experimenting with policies without success until I actually traced through the code and realised that it made no difference what policy I specified - the code was ignoring it and using the incorrectly configured policy in the keystore!
In the catch block in the code above, add the following
AWSIotKeystoreHelper.deleteKeystoreAlias(certificateId,
keystorePath, keystoreName, keystorePassword);
That will ensure that there is no incorrectly configured certificate hanging around to trip you up.
As far as the initial error is concerned, the problem is probably the name and context of the policy that you specified. I created policies in IAM and they were not recognised - you actually need to create the IoT policy as described above by Roshan within the context of IoT Core Security and specify the name that you give it within the PubSub sample code.
I'm trying to implement the functionality of add subscription to youtube channel from android app.
i already done with :
register app on developer console. -Done
package name and SHA-1 certificate fingerprint. -Done
Image
Google account account authentication with "https://www.googleapis.com/auth/youtube" . -Done
Note : functionality is working fine in debug mode.
Issue: when i create a signed apk for publishing app on play store then subscribe button not working throw different errors each time .
i.e With unrestricted api key :
W/System.err: {
"c" : 0,
"errors" : [ {
"domain" : "global",
"reason" : "required",
"message" : "Required parameter: part",
"locationType" : "parameter",
"location" : "part"
} ],
"code" : 400,
"message" : "Required parameter: part"
}
Here is the code that Im doing
// Initialize credentials and service object.
mCredential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff());
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
YouTube mService = new YouTube.Builder(transport, jsonFactory, mCredential)
.setApplicationName(getResources().getString(R.string.app_name))
.setYouTubeRequestInitializer(new YouTubeRequestInitializer(Config.KEY))
.build();
channelId = myListModel.getYoutubeChannelId();
// Create a resourceId that identifies the channel ID.
ResourceId resourceId = new ResourceId();
resourceId.setChannelId(channelId);
resourceId.setKind("youtube#channel");
// Create a snippet that contains the resourceId.
SubscriptionSnippet snippet = new SubscriptionSnippet();
snippet.setResourceId(resourceId);
// Create a request to add the subscription and send the request.
// The request identifies subscription metadata to insert as well
// as information that the API server should return in its response.
Subscription subscription = new Subscription();
subscription.setSnippet(snippet);
YouTube.Subscriptions.Insert subscriptionInsert = mService.subscriptions().insert("snippet,contentDetails", subscription);
try {
Subscription returnedSubscription = subscriptionInsert.execute();
// Print information from the API response.
System.out.println("\n================== Returned Subscription ==================\n");
System.out.println(" - Id: " + returnedSubscription.getId());
System.out.println(" - Title: " + returnedSubscription.getSnippet().getTitle());
addSubscriber(myListModel);
} catch (UserRecoverableAuthIOException mLastError) {
startActivityForResult(mLastError.getIntent(), REQUEST_AUTHORIZATION);
}
} catch (GoogleJsonResponseException e) {
System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode() + " : "
+ e.getDetails().getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
e.printStackTrace();
} catch (Throwable t) {
System.err.println("Throwable: " + t.getMessage());
t.printStackTrace();
}
I am trying to use 'Authenticate using Cognito-Identity with Cognito user pool' in my Android application. My Cognito user pool authentication works well, when I run that separately and I had seen a JWTToken as well. When I run the the 'PubSub' sample application with Unauthenticated role, it worked as expected. When I integrate these two features in one application, the application threw following error.
W/System.err: MqttException (0) - java.io.IOException: Already connected
W/System.err: at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:664)
W/System.err: at java.lang.Thread.run(Thread.java:761)
W/System.err: Caused by: java.io.IOException: Already connected
W/System.err: at java.io.PipedOutputStream.connect(PipedOutputStream.java:100)
W/System.err: at java.io.PipedInputStream.connect(PipedInputStream.java:195)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketReceiver.<init>(WebSocketReceiver.java:42)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule.start(WebSocketSecureNetworkModule.java:78)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:650)
W/System.err: ... 1 more
I have been trying to resolve this issue since last Thursday and still stuck at the same place. Really No idea where should i check.!
I am adding my Authentication(Cognito user pool authentication) activity and Connect activity.
AmazonCognitoIdentityProviderClient identityProviderClient = new
AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), new ClientConfiguration());
identityProviderClient.setRegion(Region.getRegion(Regions.US_WEST_2));
CognitoUserPool userPool = new CognitoUserPool(getApplicationContext(), "us-west-2_ghtcc6ho9", "4t0mk45hNso69dp2j4jvel5ghm", "1jmq0lhhq721oif9k6nug31c29i760vihua8hvrgu5umfr2a1vd7", identityProviderClient);
cogUser = userPool.getUser();
authenticationHandler = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice) {
String ids = userSession.getIdToken().getJWTToken();
Log.d("MyToken","session id___"+userSession.getIdToken().getExpiration()+"___"+userSession.getIdToken().getIssuedAt());
Intent pubSub = new Intent(MainActivity.this, PubSubActivity.class);
pubSub.putExtra("token",""+ids);
startActivity(pubSub);
//MainActivity.this.finish();
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
Log.d("MyToken","getAuthenticationDetails");
AuthenticationDetails authenticationDetails = new AuthenticationDetails("shone", "172737", null);
authenticationContinuation.setAuthenticationDetails(authenticationDetails);
// Allow the sign-in to continue
authenticationContinuation.continueTask();
}
#Override
public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) {
Log.d("MyToken","getMFACode");
multiFactorAuthenticationContinuation.continueTask();
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
Log.d("MyToken","authenticationChallenge"+continuation.getChallengeName());
newPasswordContinuation.continueTask();
}
#Override
public void onFailure(Exception exception) {
exception.printStackTrace();
Log.d("MyToken","onFailure");
}
};
cogUser.getSessionInBackground(authenticationHandler);
When It reaches 'OnSuccess' I am launching my connect activity and passing my session token along with the Intent. Moving to the next activity
private static final String COGNITO_POOL_ID = "us-west-2:a153a090-508c-44c0-a9dd-efd450298c4b";
private static final Regions MY_REGION = Regions.US_WEST_2;
AWSIotMqttManager mqttManager;
String clientId;
AWSCredentials awsCredentials;
CognitoCachingCredentialsProvider credentialsProvider;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
if(null == intent){
Toast.makeText(getApplicationContext(), "Token is null", Toast.LENGTH_SHORT).show();
}else {
token = intent.getStringExtra("token");
}
clientId = UUID.randomUUID().toString();
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
COGNITO_POOL_ID,
MY_REGION
);
mqttManager = new AWSIotMqttManager(clientId, CUSTOMER_SPECIFIC_ENDPOINT);
Map loginsMap = new HashMap();
loginsMap.put("cognito-idp.us-west-2.amazonaws.com/us-west-2_ghtcc6ho9", token);
credentialsProvider.setLogins(loginsMap);
Log.d("SESSION_ID", ""+token);
new Thread(new Runnable() {
#Override
public void run() {
credentialsProvider.refresh();
awsCredentials = credentialsProvider.getCredentials();
Log.d("SESSION_ID B: ", ""+awsCredentials.getAWSAccessKeyId());
Log.d("SESSION_ID C: ", ""+awsCredentials.getAWSSecretKey());
}
}).start();
}
View.OnClickListener connectClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(LOG_TAG, "clientId = " + clientId);
try {
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
#Override
public void onStatusChanged(final AWSIotMqttClientStatus status,
final Throwable throwable) {
Log.d(LOG_TAG, "Status = " + String.valueOf(status)+"______"+((null !=throwable)?throwable.getMessage():""));
runOnUiThread(new Runnable() {
#Override
public void run() {
if (status == AWSIotMqttClientStatus.Connecting) {
tvStatus.setText("Connecting...");
} else if (status == AWSIotMqttClientStatus.Connected) {
tvStatus.setText("Connected");
} else if (status == AWSIotMqttClientStatus.Reconnecting) {
if (throwable != null) {
Log.e(LOG_TAG, "Connection error.", throwable);
}
tvStatus.setText("Reconnecting");
} else if (status == AWSIotMqttClientStatus.ConnectionLost) {
if (throwable != null) {
Log.e(LOG_TAG, "Connection error.", throwable);
throwable.printStackTrace();
}
tvStatus.setText("Disconnected");
} else {
tvStatus.setText("Disconnected");
}
}
});
}
});
} catch (final Exception e) {
Log.e(LOG_TAG, "Connection error.", e);
}
}
};
What is wrong in my code? Why it throws exception when the MQTT connect is being invoked? Any help would be appreciated.
I beat my head up with this almost a week.
Full course of action ->
After succesfull login you will have a jwt token
String idToken = cognitoUserSession.getIdToken().getJWTToken();
put it into a map
Map<String, String> logins = new HashMap<String, String>();
//fill it with Cognito User token
logins.put("cognito-idp.<REGION>.amazonaws.com/<COGNITO_USER_POOL_ID>", idToken);
then use it to set in two places (not stated in any documentation!)
CognitoCachingCredentialsProvider credentialsProvider = new
CognitoCachingCredentialsProvider(context, IDENTITY_POOL_ID, REGION);
credentialsProvider.setLogins(logins);
and
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(COGNITO_POOL_ID);
GetIdResult getIdRes = cognitoIdentity.getId(getIdReq);
after that you still nedd to make some call
AttachPrincipalPolicyRequest attachPolicyReq = new AttachPrincipalPolicyRequest(); //in docs it called AttachPolicyRequest but it`s wrong
attachPolicyReq.setPolicyName("allAllowed"); //name of your IOTAWS policy
attachPolicyReq.setPrincipal(getIdRes.getIdentityId());
new AWSIotClient(credentialsProvider).attachPrincipalPolicy(attachPolicyReq);
and only after that you can enable connect button and continue like that
mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
Really for this small piece of code i spent a lot of time...
I was also getting same error -
Feb 27, 2019 10:23:09 AM com.amazonaws.services.iot.client.mqtt.AwsIotMqttConnectionListener onFailure
WARNING: Connect request failure
MqttException (0) - java.io.IOException: Already connected
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:664)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: Already connected
at java.io.PipedOutputStream.connect(PipedOutputStream.java:100)
but the problem was different.
First of all, you do not need to call attachPrincipalPolicy from code. You can use the command line as well. You can do something like -
aws iot attach-principal-policy --principal us-east-1:1c973d17-98e6-4df6-86bf-d5cedc1fbc0d --policy-name "thingpolicy" --region us-east-1 --profile osfg
You will get the principal ID from identity browser of your identity pool. Now lets come to the error -
To successfully connect to mqtt with authenticated Cognito credentials, you need 2 correct policies -
Authenticated role corresponding to your identity pool should allow all mqtt operations.
AWS IoT policy should allow the same operations and you need to associate your cognito identity with this policy. We use attachPrincipalPolicy to do so.
If anyone step is missed we get above error. I agree the error is misleading - Already connected makes no sense to me for this. I would normally think it has to do with clientId, which should be unique. But anyways hopefully AWS folks would make this better at some point.
For my particular case issue was point 1. Though my IoT policy had all the required permissions, the auth role corresponding to the identity pool did not. So make sure you do that.
I have created a youtube video to show this as well: https://www.youtube.com/watch?v=j2KJVHGHaFc
When a client connected to broker it has a unique client ID. If clients tried to connect with same client id then this error occur. Use different client IDs like foo1, foo2, foo3, etc.
I use android AccountManager to get authToken like this:
private void getGoogleAccountName(){
AccountManager.get(activity.getApplicationContext())
.getAuthTokenByFeatures("com.google", "oauth2:https://gdata.youtube.com", null, activity, null, null, this, null);
}
// I implement AccountManagerCallback<Bundle> in this class
#Override
public void run(AccountManagerFuture<Bundle> future) {
boolean hasAccount = checkGoogleAccount(future);
if (hasAccount) {
getYoutubeVideoByLib("");
}
}
private boolean checkGoogleAccount(AccountManagerFuture<Bundle> future){
try {
Bundle bundle = future.getResult();
accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
} catch (Exception e) {
return false;
}
return true;
}
then I use google apis with authToken trying to get user's playlists
private void getYoutubeVideoByLib(String pageToken){
YouTube youtube = new YouTube.Builder(
new NetHttpTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
}
}).setApplicationName(activity.getString(R.string.app_name)).build();
YouTube.Playlists.List query = null;
try{
query = youtube.playlists().list("snippet");
query.setOauthToken(authToken);
query.setKey("YOUTBE_API_KEY");
query.setMine(true);
if(!TextUtils.isEmpty(pageToken)) {
query.setPageToken(pageToken);
}
PlaylistListResponse response = query.execute();
...
} catch(IOException e) {
return;
}
}
but I found out some google account got GoogleJsonResponseException in query.execute();
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
the weird thing is that, some accounts work fine before but after these users got this exception, they can't get their playlists anymore.
Does anyone meet the same problem?
===================================================================
I solved it myself. I made a big mistake...
In google developer console, I set API KEY but not OAuth 2.0 client ID.
After setting both and change code below, it work fine now.
String[] SCOPES = {YouTubeScopes.YOUTUBE_READONLY};
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES));
credential.setSelectedAccountName(accountName);
YouTube youtube = new YouTube.Builder(transport, jsonFactory, credential).setApplicationName(getString(R.string.app_name)).build();
In order to implement social features in an android app, I try to use the "writeMoment" method of the "PlusClient" class, but nothing happens. I am able to get a successful connection with "PlusClient" and to write deep link posts with my app.
Here is my code when I open the Google+ connection :
monPlusClient = new PlusClient.Builder(this,
new GooglePlayServicesClient.ConnectionCallbacks() {
#Override
public void onConnected() {
plusencours = false;
String accountName = monPlusClient.getAccountName();
// We've resolved any connection errors.
Toast.makeText(
ActiviteAfficher.this,
accountName
+ " "
+ ActiviteAfficher.this
.getResources()
.getString(
R.string.texteconnexion),
Toast.LENGTH_LONG).show();
// Log.d(TAG_DEBUG, accountName + " connected");
}
#Override
public void onDisconnected() {
plusencours = false;
// Log.d(TAG_DEBUG, "disconnected");
}
}, new GooglePlayServicesClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult resultat) {
if (resultat.hasResolution()) {
try {
resultat.startResolutionForResult(
ActiviteAfficher.this,
REQUEST_CODE_RESOLVE_ERR);
} catch (SendIntentException e) {
plusencours = true;
monPlusClient.connect();
}
}
// Save the result and resolve the connection failure
// upon a user click.
mConnectionResult = resultat;
}
})
.setVisibleActivities("http://schemas.google.com/AddActivity",
"http://schemas.google.com/DiscoverActivity")
.setScopes(Scopes.PLUS_LOGIN, Scopes.PLUS_PROFILE).build();
And here is my code when I use "writeMoment" :
ItemScope target = new ItemScope.Builder()
.setId(monSujet.getMid())
.setName(
monSujet.getName() + " - "
+ monSujet.getNotablename())
.setDescription(dialoguedescription).setImage(urlimage)
.setType("http://schema.org/Person").build();
Moment moment = new Moment.Builder()
.setType("http://schemas.google.com/AddActivity")
.setTarget(target).build();
if (monPlusClient.isConnected()) {
monPlusClient.writeMoment(moment);
}
Understanding the logcat is difficult for me :
05-09 12:00:32.380: I/ElegantRequestDirector(27290): I/O exception (org.apache.http.NoHttpResponseException) caught when processing request: The target server failed to respond
05-09 12:00:32.380: I/ElegantRequestDirector(27290): Retrying request
05-09 12:00:33.000: E/Volley(27290): [3428] BasicNetwork.performRequest: Unexpected response code 400 for https://www.googleapis.com/plus/v1/people/me/moments/vault
05-09 12:00:33.050: D/SyncManager(295): failed sync operation XXXXXXX#gmail.com (com.google), com.google.android.gms.plus.action, USER, earliestRunTime 140603923, SyncResult: stats [ numIoExceptions: 1]
05-09 12:00:33.050: D/SyncSetupManager(16157): setState: sync = true, wantedSyncState = true
05-09 12:00:33.090: D/SyncSetupManager(16157): Enabling sync
If you're having problems debugging issues while writing app activities, you should try enabling debug for GooglePlusPlatform:
adb shell setprop log.tag.GooglePlusPlatform VERBOSE
Which is also described here - https://developers.google.com/+/mobile/android/getting-started#frequently_asked_questions
Running your code with debugging enabled writes the following to logcat:
D/GooglePlusPlatform(8133): Unexpected response code (400) when requesting: writeMoment
D/GooglePlusPlatform(8133): Error response: {
D/GooglePlusPlatform(8133): "error": {
D/GooglePlusPlatform(8133): "errors": [
D/GooglePlusPlatform(8133): {
D/GooglePlusPlatform(8133): "domain": "global",
D/GooglePlusPlatform(8133): "reason": "badRequest",
D/GooglePlusPlatform(8133): "message": "Missing metadata field: http://schema.org/url."
D/GooglePlusPlatform(8133): }
D/GooglePlusPlatform(8133): ],
D/GooglePlusPlatform(8133): "code": 400,
D/GooglePlusPlatform(8133): "message": "Missing metadata field: http://schema.org/url."
D/GooglePlusPlatform(8133): }
D/GooglePlusPlatform(8133): }
You cannot supply a Person object without supplying a public target URL which has appropriate markup (instead of an explicit name and description). Running your code with http://schema.org/Thing instead of http://schema.org/Person worked for me.