amplify-android: AwsMobileClient is not initialising automatically in 2.7.1

Before opening, please confirm:

Language and Async Model

Kotlin

Amplify Categories

Authentication, REST API, DataStore

Gradle script dependencies

// Amplify dependencies
implementation 'com.amplifyframework:core:2.7.1'
implementation 'com.amplifyframework:aws-api:2.7.1'
implementation 'com.amplifyframework:aws-datastore:2.7.1'
implementation 'com.amplifyframework:aws-auth-cognito:2.7.1'
implementation 'com.amplifyframework:aws-storage-s3:2.7.1'

// AWS MQTT dependencies
implementation 'com.amazonaws:aws-android-sdk-iot:2.65.0'
implementation 'com.amazonaws:aws-android-sdk-mobile-client:2.65.0'

// Support for Java 8 features
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'

Environment information

    classpath 'com.android.tools.build:gradle:7.3.1'
    classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20'

    // Google Services plugin
    classpath 'com.google.gms:google-services:4.3.10'
    // Add the Crashlytics Gradle plugin
    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'

Please include any relevant guides or documentation you’re referencing

No response

Describe the bug

I was using 1.30.0 version of amplify and every thing works fine, when i move to 2.7.1 and updated the code amplify works fine i just get the data and ready state is triggered but when it tries to connect to MQTT i got the following error

MQTT broker: a3jmi0po6ccx5n-ats.iot.ap-southeast-1.amazonaws.com:443 MQTT I Connection Lost com.amazonaws.AmazonClientException: Cognito Identity not configured System.err W com.amazonaws.AmazonClientException: Cognito Identity not configured System.err W at com.amazonaws.mobile.client.AWSMobileClient.getCredentials(AWSMobileClient.java:391) System.err W at com.amazonaws.mobileconnectors.iot.AWSIotMqttManager$1.run(AWSIotMqttManager.java:993) System.err W at java.lang.Thread.run(Thread.java:1012)

What i have observed is AwsMobileClient is not initialised at the start of app when i use 2.7.1 and in case of 1.30.0 it is initialised properly at the start with the logs that its initialise is called.

Reproduction steps (if applicable)

Simply update to 2.7.1 version of amplify from 1.30.0

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line


amplifyconfiguration.json

No response

GraphQL Schema

// Put your schema below this line


Additional information and screenshots

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 15 (8 by maintainers)

Most upvoted comments

@ZubairAkber I believe this provides everything you need. If you experience any other issues, please let us know. I’ll mark as closing soon to give some time.

@ininmm I have replied in the new issue that you created and will follow up with any additional questions there.

@ZubairAkber Please try the below credentials provider. I have verified this to work on an IoT project sample I have set up. IoT requires a sessionToken that was not present in BasicAWSCredentials. Instead, we can pass BasicSessionCredentials.

class MyCredentialsProvider: AWSCredentialsProvider {

    override fun getCredentials(): AWSCredentials {
        // Need to make fetchAuthSessionBlocking
        val latch = CountDownLatch(1)
        var sdkCredentials: AWSCredentials? = null

        Amplify.Auth.fetchAuthSession(
            { authSession ->
                val awsTemporaryCredentials = (authSession as? AWSCognitoAuthSession)
                    ?.awsCredentialsResult?.value as? AWSTemporaryCredentials
                
                sdkCredentials = awsTemporaryCredentials?.let {
                    BasicSessionCredentials(it.accessKeyId, it.secretAccessKey, it.sessionToken)
                }
                
                latch.countDown()
            },
            {
                // can better handle this exception and pass it down to the throwing call below
                latch.countDown()
            }
        )

        // wait for fetchAuthSession to return
        latch.await()

        // return captured credentials or throw
        return sdkCredentials ?: throw IllegalStateException("Failed to get credentials")
    }

    override fun refresh() {
        // Call refresh but we don't need to store or capture result here
        Amplify.Auth.fetchAuthSession(
            AuthFetchSessionOptions.builder().forceRefresh(true).build(),
            { /* do nothing on success */ },
            { /* do nothing on failure */ },
        )
    }
}

@ZubairAkber Please check that the time on your device is accurate. Please see this thread for more details: https://github.com/aws-amplify/aws-sdk-android/issues/2674.

@ZubairAkber

connect looks like this:

public void connect(
  AWSCredentialsProvider credentialsProvider,
  final AWSIotMqttClientStatusCallback statusCallback
)

AWSCredentialsProvider is just an interface:

public interface AWSCredentialsProvider {
    public AWSCredentials getCredentials();
    public void refresh();
}

We can implement our own version of this interface by using Amplify v2 to fetch credentials.

class MyCredentialsProvider: AWSCredentialsProvider {

    override fun getCredentials(): AWSCredentials {
        // Need to make fetchAuthSessionBlocking
        val latch = CountDownLatch(1)
        var sdkCredentials: AWSCredentials? = null

        Amplify.Auth.fetchAuthSession(
            { authSession ->
                val awsTemporaryCredentials = (authSession as? AWSCognitoAuthSession)
                    ?.awsCredentialsResult?.value as? AWSTemporaryCredentials
                
                sdkCredentials = awsTemporaryCredentials?.let {
                    BasicSessionCredentials(it.accessKeyId, it.secretAccessKey, it.sessionToken)
                }
                
                latch.countDown()
            },
            {
                // can better handle this exception and pass it down to the throwing call below
                latch.countDown()
            }
        )

        // wait for fetchAuthSession to return
        latch.await()

        // return captured credentials or throw
        return sdkCredentials ?: throw IllegalStateException("Failed to get credentials")
    }

    override fun refresh() {
        // Call refresh but we don't need to store or capture result here
        Amplify.Auth.fetchAuthSession(
            AuthFetchSessionOptions.builder().forceRefresh(true).build(),
            { /* do nothing on success */ },
            { /* do nothing on failure */ },
        )
    }
}

Your connect call would then take MyCredentialsProvider() instead of AWSMobileClient.getInstance().

note: I’ve updated the snippet in this comment to the corrected one suggested later in the thread to prevent confusion.

@ZubairAkber Looking at the iOT implementation, it just requires an AWSCredentialsProvider. Instead of using AWSMobileClient (mobile client dependency needs to be removed completely), you will want to create an implementation for the AWSCredentialsProvider interface.

You can see examples of how to do this from a similar interface we have on Amplify v2 (https://github.com/aws-amplify/amplify-android/blob/d0dd5e6b5b8dd388a24850f016adc3ab73993075/aws-core/src/main/java/com/amplifyframework/auth/CognitoCredentialsProvider.kt). It doesn’t translate directly the the AWSCredentialsProvider you will need to implement but should give a rough guide on how to do so.

In summary, you should be able to use aws-android-sdk-iot with Amplify v2, as long as you remove the mobile client sdk.