Ad

How Can I Get AWS Cognito Login/logout To Work Properly With AWS API Gateway On Android?

I'm writing a simple Android app that currently offers the following features:

  1. Register/login with email and password, against an AWS Cognito back end
  2. Logout (against the same Cognito back end)
  3. Call a protected web API on AWS API Gateway

I'm building my own very simple login/logout UI, rather than using the AWS Cognito UI library:

Login and logged-in screens

Login screen (left); logged-in screen (right)

The web API should return 200 (and a body) when the user is logged in, and a 403 error when they are logged out. The server setup all appears to be correct, and an equivalent app on iOS is working as described.

The problem on Android is as follows:

  • When I start the app, don't log in, and call the API: I get a 403 error (expected).
  • When I log in and call the API again: I still get a 403 error (unexpected).
  • When I close the app, swipe it from the recent apps list, and re-open it: I'm still logged in (expected)
  • Now when I call the API: I get a 200 response (expected).

The same happens now if I log out: I still get a 200 response after logging out until I kill the app and restart it.

How can I get the API Gateway call to respect my current authentication state immediately, without recycling the app?

I wondered if the AWS API Gateway Android library might be to blame, so I've tried an alternative implementation using OkHttp but I get exactly the same result. I've based my Cognito code on the AmazonCognitoYourUserPools demo.

Here's the part of my build.gradle that imports the AWS libraries:

// AWS Mobile Client
implementation('com.amazonaws:aws-android-sdk-mobile-client:[email protected]') { transitive = true }

// Cognito UserPools for SignIn
implementation 'com.android.support:support-v4:27.1.1'
implementation('com.amazonaws:aws-android-sdk-auth-userpools:[email protected]') { transitive = true }

// AWS API gateway
implementation 'com.amazonaws:aws-android-sdk-apigateway-core:2.6.18'

It probably won't help to paste large amounts of code here, but I've created gists for the main classes involved (with logging etc. removed to keep them as small and relevant as possible):

The AwsInterceptor class referenced in ApiCaller.java came from here.

Ad

Answer

The solution my colleague and I have arrived at is as follows:

Login

After login, you need to re-initialise the AWSMobileClient before you call any API Gateway endpoints. Here's a snippet from my AuthenticationHandler in LoginActivity.java:

private AuthenticationHandler authenticationHandler = new AuthenticationHandler()
{
    @Override
    public void onSuccess(final CognitoUserSession userSession,
                          final CognitoDevice newDevice)
    {
        // Initialise the client again
        AWSMobileClient.getInstance().initialize(LoginActivity.this, new AWSStartupHandler()
        {
            @Override
            public void onComplete(final AWSStartupResult awsStartupResult)
            {
                switchTo(LoggedInActivity.class);
            }
        }).execute();
    }

    // ...
}

Logout

After logout, we found we needed to clear the credentials from the IdentityManager. This snippet is from my LoggedInActivity.java:

private void logout()
{
    final CognitoUser currentUser = getCurrentUser();
    currentUser.signOut();

    // Clear the IdentityManager credentials
    final IdentityManager idm = new IdentityManager(this, new AWSConfiguration(this));
    IdentityManager.setDefaultIdentityManager(idm);
    idm.getUnderlyingProvider().clearCredentials();
    idm.getUnderlyingProvider().clear();
    idm.getUnderlyingProvider().setLogins(null);

    switchTo(LoginActivity.class);
}
Ad
source: stackoverflow.com
Ad