Skip to content

Commit

Permalink
feat(mobile-client): hosted-ui auth response handler is now built int…
Browse files Browse the repository at this point in the history
…o redirect activity (#2473)

* feat(mobile-client): auth response handler is now built into redirect activity

* add javadocs for redirect activities

* add signout latch conditionally

* add no history flag to auth signout flow
  • Loading branch information
raphkim authored May 28, 2021
1 parent 8a2f805 commit 74209c4
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import androidx.browser.customtabs.CustomTabsSession;
import android.text.TextUtils;
import android.util.Log;

import com.amazonaws.cognito.clientcontext.data.UserContextDataProvider;
import com.amazonaws.mobileconnectors.cognitoauth.activities.CustomTabsManagerActivity;
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthClientException;
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthInvalidGrantException;
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthNavigationException;
import com.amazonaws.mobileconnectors.cognitoauth.exceptions.AuthServiceException;
Expand All @@ -44,8 +49,11 @@
import java.net.URL;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
* Local client for {@link Auth}.
Expand All @@ -64,6 +72,21 @@ public class AuthClient {
*/
public static final int CUSTOM_TABS_ACTIVITY_CODE = 49281;

/**
* Namespace for logging client activities
*/
private static final String TAG = AuthClient.class.getSimpleName();

/**
* Name of redirect activity in charge of handling auth responses.
*/
private static final String REDIRECT_ACTIVITY_NAME = "HostedUIRedirectActivity";

/**
* Default timeout duration for auth redirects.
*/
private static final long REDIRECT_TIMEOUT_SECONDS = 10;

/**
* Specifies what browser package to default to if one isn't specified.
*/
Expand Down Expand Up @@ -104,12 +127,20 @@ public class AuthClient {
*/
private AuthHandler userHandler;

/**
* Remembers whether redirect activity was found in manifest or not.
*/
private boolean isRedirectActivityDeclared;


// - Chrome Custom Tabs Controls
private CustomTabsClient mCustomTabsClient;
private CustomTabsSession mCustomTabsSession;
private CustomTabsIntent mCustomTabsIntent;
private CustomTabsServiceConnection mCustomTabsServiceConnection;

private CountDownLatch cookiesCleared;

/**
* Constructs {@link AuthClient} with no user name.
* @param context Required: The android application {@link Context}.
Expand All @@ -129,6 +160,7 @@ protected AuthClient(final Context context, final Auth pool, final String userna
this.context = context;
this.pool = pool;
this.userId = username;
this.isRedirectActivityDeclared = false;
preWarmChrome();
}

Expand Down Expand Up @@ -250,8 +282,7 @@ public void signOut() {
* @param browserPackage String specifying the browser package to launch the specified url.
*/
public void signOut(String browserPackage) {
LocalDataManager.clearCache(pool.awsKeyValueStore, context, pool.getAppId(), userId);
launchSignOut(pool.getSignOutRedirectUri(), browserPackage);
signOut(false, browserPackage);
}

/**
Expand All @@ -271,18 +302,42 @@ public void signOut(final boolean clearLocalTokensOnly) {
/**
* Signs-out a user.
* <p>
* Clears cached tokens for the user. Launches the sign-out Cognito web end-point to
* clear all Cognito Auth cookies stored by Chrome.
* Launches the sign-out Cognito web end-point to clear all Cognito Auth cookies stored
* by Chrome. Cached tokens will be deleted if sign-out redirect is completed.
* </p>
*
* @param clearLocalTokensOnly true if signs out the user from the client,
* but the session may still be alive from the browser.
* @param browserPackage String specifying the browser package to launch the specified url.
*/
public void signOut(final boolean clearLocalTokensOnly, final String browserPackage) {
LocalDataManager.clearCache(pool.awsKeyValueStore, context, pool.getAppId(), userId);
if (!clearLocalTokensOnly) {
endSession(browserPackage);
}

// Delete local cache
LocalDataManager.clearCache(pool.awsKeyValueStore, context, pool.getAppId(), userId);
}

/**
* Ends current browser session.
* @param browserPackage browser package to launch sign-out endpoint from.
* @throws AuthClientException if sign-out redirect fails to resolve.
*/
private void endSession(final String browserPackage) throws AuthClientException {
boolean redirectReceived;
try {
cookiesCleared = new CountDownLatch(1);
launchSignOut(pool.getSignOutRedirectUri(), browserPackage);
if (!isRedirectActivityDeclared()) {
cookiesCleared.countDown();
}
redirectReceived = cookiesCleared.await(REDIRECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new AuthNavigationException("User cancelled sign-out.");
}
if (!redirectReceived) {
throw new AuthServiceException("Timed out while waiting for sign-out redirect response.");
}
}

Expand Down Expand Up @@ -415,6 +470,11 @@ public void run() {
}
}
} else {
if (cookiesCleared != null) {
cookiesCleared.countDown();
Log.d(TAG, "Sign-out was successful.");
}

// User sign-out.
returnCallback = new Runnable() {
@Override
Expand Down Expand Up @@ -572,7 +632,7 @@ private Map<String, String> generateTokenRefreshRequest(final String redirectUri
if (userContextData != null) {
httpBodyParams.put(ClientConstants.DOMAIN_QUERY_PARAM_USERCONTEXTDATA, userContextData);
}
return httpBodyParams;
return httpBodyParams;
}

/**
Expand Down Expand Up @@ -671,9 +731,9 @@ private void launchCustomTabs(final Uri uri, final Activity activity, final Stri
CUSTOM_TABS_ACTIVITY_CODE
);
} else {
context.startActivity(
CustomTabsManagerActivity.createStartIntent(context, mCustomTabsIntent.intent)
);
Intent startIntent = CustomTabsManagerActivity.createStartIntent(context, mCustomTabsIntent.intent);
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
context.startActivity(startIntent);
}
} catch (final Exception e) {
userHandler.onFailure(e);
Expand Down Expand Up @@ -708,4 +768,36 @@ public void onServiceDisconnected(final ComponentName name) {
}
};
}

// Inspects context to determine whether HostedUIRedirectActivity is declared in
// customer's AndroidManifest.xml.
private boolean isRedirectActivityDeclared() {
// If the activity was found at least once, then don't bother searching again.
if (isRedirectActivityDeclared) {
return true;
}
if (context == null) {
Log.w(TAG, "Context is null. Failed to inspect packages.");
return false;
}
try {
List<PackageInfo> packages = context.getPackageManager()
.getInstalledPackages(PackageManager.GET_ACTIVITIES);
for (PackageInfo packageInfo : packages) {
if (packageInfo.activities == null) {
continue;
}
for (ActivityInfo activityInfo : packageInfo.activities) {
if (activityInfo.name.contains(REDIRECT_ACTIVITY_NAME)) {
isRedirectActivityDeclared = true;
return true;
}
}
}
Log.w(TAG, REDIRECT_ACTIVITY_NAME + " is not declared in AndroidManifest.");
} catch (Exception error) {
Log.w(TAG, "Failed to inspect packages.");
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import android.app.Activity;
import android.os.Bundle;

/**
* Handles auth redirect for sign-in and sign-out.
*
* If cognitoauth module is being used in conjunction with AWSMobileClient, then use
* com.amazonaws.mobile.client.activities.HostedUIRedirectActivity instead of this.
*/
public final class CustomTabsRedirectActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceBundle) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.amazonaws.mobile.client.activities;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import com.amazonaws.mobile.client.AWSMobileClient;
import com.amazonaws.mobileconnectors.cognitoauth.activities.CustomTabsManagerActivity;

/**
* Handles auth redirect for sign-in and sign-out.
*/
public final class HostedUIRedirectActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceBundle) {
super.onCreate(savedInstanceBundle);
startActivity(CustomTabsManagerActivity.createResponseHandlingIntent(
this, getIntent().getData()));
}

@Override
public void onResume() {
super.onResume();
Log.d("AuthClient", "Handling auth redirect response");
AWSMobileClient.getInstance().handleAuthResponse(getIntent());
finish();
}
}

0 comments on commit 74209c4

Please sign in to comment.