Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to browser on DUNA redirect URL., Fixes AB#3079799 #2553

Open
wants to merge 74 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
560d0fb
draft
p3dr0rv Dec 3, 2024
7632017
draft 2
p3dr0rv Dec 4, 2024
f30b5bc
nits
p3dr0rv Dec 4, 2024
82251aa
nits
p3dr0rv Dec 5, 2024
e7636cb
javadoc
p3dr0rv Dec 5, 2024
a8720b9
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Dec 5, 2024
ca4f0eb
add tests
p3dr0rv Dec 6, 2024
19e0498
update names
p3dr0rv Dec 7, 2024
76155d9
fix tests
p3dr0rv Dec 7, 2024
a389026
Refactor startActivity call in WebViewAuthorizationFragment
p3dr0rv Dec 11, 2024
6178335
Add redirect URI query parameter
p3dr0rv Dec 11, 2024
744d212
create snippets
p3dr0rv Dec 11, 2024
e3da415
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Dec 11, 2024
d569aa6
update tests
p3dr0rv Dec 11, 2024
c404af1
Merge branch 'pedroro/switch-to-browser' of https://github.com/AzureA…
p3dr0rv Dec 11, 2024
9385aba
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Dec 17, 2024
58c771c
used defined browsers for duna flow
p3dr0rv Dec 18, 2024
fae212f
feedback
p3dr0rv Dec 18, 2024
1f3402b
fix pmd issue
p3dr0rv Dec 18, 2024
762d67c
fix compilation error
p3dr0rv Dec 18, 2024
7f266eb
avoid double run
p3dr0rv Dec 18, 2024
5baaba0
fix tests
p3dr0rv Dec 18, 2024
c380714
add NON_FUNCTIONAL_BROWSER_SELECTOR
p3dr0rv Dec 18, 2024
d6bf4a8
fix test
p3dr0rv Dec 18, 2024
f15596c
trigger only once
p3dr0rv Dec 18, 2024
960c7c4
nit
p3dr0rv Dec 18, 2024
b948e9f
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Dec 18, 2024
4284ad5
implement feedback
p3dr0rv Dec 19, 2024
25ad56f
rework switchBrowser
p3dr0rv Dec 31, 2024
4584e13
fix testing
p3dr0rv Dec 31, 2024
c8f6328
spotbugs
p3dr0rv Dec 31, 2024
b8d0515
nit
p3dr0rv Dec 31, 2024
a56a813
feedback
p3dr0rv Dec 31, 2024
8c33115
Merge branch 'pedroro/duna-rework' into pedroro/switch-to-browser
p3dr0rv Dec 31, 2024
8a5a96e
draft
p3dr0rv Jan 3, 2025
003fe74
Merge branch 'dev' into pedroro/browser-selector
p3dr0rv Jan 3, 2025
98deb1c
RESTORE util
p3dr0rv Jan 3, 2025
c9b5f83
Merge branch 'pedroro/browser-selector' of https://github.com/AzureAD…
p3dr0rv Jan 3, 2025
755d9da
move to 4j
p3dr0rv Jan 3, 2025
4691ad6
add noopBrowserSelector
p3dr0rv Jan 3, 2025
1e624de
Convert to Java to maintain compatibility with older tests
p3dr0rv Jan 3, 2025
33740e2
Merge branch 'dev' into pedroro/browser-selector
p3dr0rv Jan 3, 2025
859d677
update changelog
p3dr0rv Jan 3, 2025
e410152
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Jan 4, 2025
d5a8697
Merge branch 'pedroro/browser-selector' into pedroro/switch-to-browser
p3dr0rv Jan 4, 2025
5c22a55
remove unused strings in ErrorString
p3dr0rv Jan 4, 2025
a697720
clean changes
p3dr0rv Jan 4, 2025
db8b608
nit
p3dr0rv Jan 4, 2025
82cc1c2
Merge branch 'pedroro/browser-selector' into pedroro/switch-to-browser
p3dr0rv Jan 4, 2025
20a8143
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Jan 24, 2025
ec3fd80
nits
p3dr0rv Jan 24, 2025
ebfaabc
update switchBrowserChallenge
p3dr0rv Jan 24, 2025
5c5b2fa
eof
p3dr0rv Jan 24, 2025
ca6e112
update java doc
p3dr0rv Jan 25, 2025
2b5a4f9
test need to be fixed
p3dr0rv Jan 25, 2025
8c7ae24
update test
p3dr0rv Jan 28, 2025
b7a104a
update chnagelog
p3dr0rv Jan 28, 2025
b0343eb
add SwitchBrowserUriHelper
p3dr0rv Jan 28, 2025
914856b
update constants
p3dr0rv Jan 29, 2025
9ea834a
license
p3dr0rv Jan 29, 2025
15511ee
remove action
p3dr0rv Feb 19, 2025
ceeb3f0
Merge branch 'dev' into pedroro/switch-to-browser
p3dr0rv Feb 19, 2025
d8c2d4e
Update SwitchBrowserUriHelper.kt
p3dr0rv Feb 19, 2025
2e47d40
update flag
p3dr0rv Feb 19, 2025
75b14e6
update test
p3dr0rv Feb 19, 2025
273a4cc
Update common/src/main/java/com/microsoft/identity/common/adal/intern…
p3dr0rv Feb 19, 2025
c045005
Update common/src/main/java/com/microsoft/identity/common/internal/ui…
p3dr0rv Feb 19, 2025
86c8fee
throw exceptions instead of null
p3dr0rv Feb 19, 2025
d793623
Merge branch 'pedroro/switch-to-browser' of https://github.com/AzureA…
p3dr0rv Feb 19, 2025
2b6c296
update comment
p3dr0rv Feb 19, 2025
f28b129
Update common/src/main/java/com/microsoft/identity/common/internal/ui…
p3dr0rv Feb 20, 2025
a742d65
update error code
p3dr0rv Feb 20, 2025
635b5ac
Merge branch 'pedroro/switch-to-browser' of https://github.com/AzureA…
p3dr0rv Feb 21, 2025
0f59cb5
check error message
p3dr0rv Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
// THE SOFTWARE.
package com.microsoft.identity.common.adal.internal;

import android.net.Uri;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.microsoft.identity.common.internal.logging.Logger;
import com.microsoft.identity.common.java.broker.BrokerAccountDataName;

import java.nio.charset.Charset;
import java.util.Set;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -529,6 +533,45 @@ public static final class AAD {
public static final String APP_VERSION = "x-app-ver";
}

/**
* Represents the constants value for the DUNA flow.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static final class SWITCH_BROWSER {

/**
* String Query parameter key for the session token.
*/
public static final String CODE = "code";

/**
* String Query parameter key for the endpoint.
*/
public static final String ACTION_URI = "action_uri";

/**
* String Query parameter key for the endpoint.
*/
public static final String ACTION = "action";

/**
* Check if the request is to switch the browser.
* <p>
* The request is considered "switch_browser" if the URL contains
* the action URI, code, and action parameters.
*
* @param uri The URI of the request.
* @return True if the request contains the required parameters, false otherwise.
*/
public static boolean isSwitchBrowserRequest(@Nullable final Uri uri) {
if (uri == null) {
return false;
}
final Set<String> requiredParams = Set.of(ACTION_URI, CODE, ACTION);
return uri.getQueryParameterNames().containsAll(requiredParams);
}
}

/**
* Represents the constants for broker.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.microsoft.identity.common.java.ui.PreferredAuthMethod;

import java.io.Serializable;
import java.util.List;

import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -79,6 +80,7 @@ private static final class SerializedNames {
final static String PREFERRED_AUTH_METHOD = "preferred_auth_method";
final static String ACCOUNT_TRANSFER_TOKEN = "account_transfer_token";
final static String SUPPRESS_ACCOUNT_PICKER = "suppress_account_picker";
final static String BROWSER_SAFE_LIST = "browser_safe_list";
}

/**
Expand Down Expand Up @@ -185,7 +187,6 @@ private static final class SerializedNames {
/**
* Boolean if set, will try to refresh the token instead of using it from cache.
*/
@Nullable
@SerializedName(SerializedNames.FORCE_REFRESH)
private boolean mForceRefresh;

Expand Down Expand Up @@ -227,7 +228,6 @@ private static final class SerializedNames {
/**
* Boolean indicated whether app supports multiple clouds.
*/
@NonNull
@SerializedName(SerializedNames.MULTIPLE_CLOUDS_SUPPORTED)
private boolean mMultipleCloudsSupported;

Expand All @@ -239,7 +239,6 @@ private static final class SerializedNames {
@SerializedName(SerializedNames.AUTHENTICATION_SCHEME)
private AbstractAuthenticationScheme mAuthenticationScheme;

@Nullable
@SerializedName(SerializedNames.POWER_OPT_CHECK_ENABLED)
private boolean mPowerOptCheckEnabled;

Expand All @@ -266,4 +265,10 @@ private static final class SerializedNames {
*/
@SerializedName(SerializedNames.SUPPRESS_ACCOUNT_PICKER)
private boolean mSuppressAccountPicker;

/**
* List of browsers that are safe to use for the request.
*/
@SerializedName(SerializedNames.BROWSER_SAFE_LIST)
private List<BrowserDescriptor> mBrowserSafeList;
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ public static Intent getAuthorizationActivityIntent(final Context context,
final LibraryConfiguration libraryConfig = LibraryConfiguration.getInstance();
if (ProcessUtil.isBrokerProcess(context)) {
intent = new Intent(context, BrokerAuthorizationActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
// In the case of a DUNA flow, we need to transition from the browser to the WebView.
// These flags ensure that we have a new task stack that allows for this transition.
} else if (libraryConfig.isAuthorizationInCurrentTask() && !authorizationAgent.equals(AuthorizationAgent.WEBVIEW)) {
// We exclude the case when the authorization agent is already selected as WEBVIEW because of confusion
// that results from attempting to use the CurrentTaskAuthorizationActivity in that case, because as webview
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -459,6 +460,46 @@ public ActivityResultLauncher<LegacyFido2ApiObject> getFidoLauncher() {
return mFidoLauncher;
}

/**
* Launches the web browser intent for the given uri.
*
* @param uri The uri to launch the web browser intent.
*/
public boolean launchWebBrowserIntent(@NonNull final Uri uri) {
final String methodTag = TAG + ":launchWebBrowserIntent";
if (mAuthIntent != null) {
Logger.info(methodTag, "Launching web browser intent for DUNA flow.");
mAuthIntent.setData(uri);
startActivity(mAuthIntent);
return true;
}
return false;
}

/**
* Constructs the switch browser uri with the given action uri and parameters.
*
* @param action_uri The action uri.
* @param params The parameters.
* @return The constructed uri.
*/
public Uri constructSwithBrowserUri(@NonNull final String action_uri, @Nullable final HashMap<String, String> params) {
final String[] paths = action_uri.split("/");
final String authority = paths[0];
final Uri.Builder uriBuilder = new Uri.Builder()
.scheme("https")
.encodedAuthority(authority);
for (int i = 1; i < paths.length; i++) {
uriBuilder.appendPath(paths[i]);
}
if (params != null) {
for (HashMap.Entry<String, String> entry : params.entrySet()) {
uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
}
}
return uriBuilder.build();
}

/**
* Helper method to check if the authorization request is being made through broker.
* Done by checking for broker version key in the url
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public BrokerRequest brokerRequestFromAcquireTokenParameters(@NonNull final Inte
.preferredAuthMethod(parameters.getPreferredAuthMethod())
.accountTransferToken(parameters.getAccountTransferToken())
.suppressAccountPicker(parameters.isSuppressBrokerAccountPicker())
.browserSafeList(parameters.getBrowserSafeList())
.build();

return brokerRequest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import com.microsoft.identity.common.internal.ui.browser.Browser;
import com.microsoft.identity.common.internal.ui.browser.DefaultBrowserAuthorizationStrategy;
import com.microsoft.identity.common.java.WarningType;
import com.microsoft.identity.common.java.exception.ClientException;
import com.microsoft.identity.common.java.exception.ErrorStrings;
import com.microsoft.identity.common.java.commands.parameters.BrokerInteractiveTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;
Expand All @@ -41,11 +40,9 @@
import com.microsoft.identity.common.internal.ui.browser.BrowserSelector;
import com.microsoft.identity.common.internal.ui.webview.EmbeddedWebViewAuthorizationStrategy;
import com.microsoft.identity.common.java.ui.AuthorizationAgent;
import com.microsoft.identity.common.java.ui.BrowserDescriptor;
import com.microsoft.identity.common.logging.Logger;
import com.microsoft.identity.common.java.strategies.IAuthorizationStrategyFactory;

import java.util.List;

import lombok.Builder;
import lombok.experimental.Accessors;
Expand All @@ -61,62 +58,79 @@ public class AndroidAuthorizationStrategyFactory implements IAuthorizationStrate
private final Activity mActivity;
private final Fragment mFragment;

/**
* Get the authorization strategy.
*
* @param parameters The parameters for the command.
* @return The authorization strategy.
*/
@Override
public IAuthorizationStrategy getAuthorizationStrategy(
@NonNull final InteractiveTokenCommandParameters parameters) {
final String methodTag = TAG + ":getAuthorizationStrategy";
//Valid if available browser installed. Will fallback to embedded webView if no browser available.

if (parameters.getAuthorizationAgent() == AuthorizationAgent.WEBVIEW) {
Logger.info(methodTag, "Use webView for authorization.");
return getGenericAuthorizationStrategy();
}

Browser browser;
try {
final Browser browser = BrowserSelector.select(
browser = BrowserSelector.select(
mContext,
parameters.getBrowserSafeList(),
parameters.getPreferredBrowser());
} catch (final Throwable throwable) {
Logger.warn(methodTag, ErrorStrings.NO_AVAILABLE_BROWSER_FOUND);
browser = null;
}

Logger.info(methodTag, "Use browser for authorization.");
return getBrowserAuthorizationStrategy(
browser,
(parameters instanceof BrokerInteractiveTokenCommandParameters));
} catch (final ClientException e) {
Logger.info(methodTag, "Unable to use browser to do the authorization because "
+ ErrorStrings.NO_AVAILABLE_BROWSER_FOUND + " Use embedded webView instead.");
return getGenericAuthorizationStrategy();
// Use embedded webView if no browser available or authorization agent is webView
if (parameters.getAuthorizationAgent() == AuthorizationAgent.WEBVIEW || browser == null) {
Logger.info(methodTag, "Use webView for authorization.");
return getGenericAuthorizationStrategy(browser);
}

Logger.info(methodTag, "Use browser for authorization.");
return getBrowserAuthorizationStrategy(
browser,
(parameters instanceof BrokerInteractiveTokenCommandParameters));

}

/**
* Get current task browser authorization strategy or default browser authorization strategy.
* If the authorization is in current task, use current task browser authorization strategy.
*
* @param browser The browser to use for authorization.
* @param isBrokerRequest True if the request is from broker.
* @return The browser authorization strategy.
*/
private IAuthorizationStrategy getBrowserAuthorizationStrategy(@NonNull final Browser browser,
final boolean isBrokerRequest) {
if (LibraryConfiguration.getInstance().isAuthorizationInCurrentTask()) {
final CurrentTaskBrowserAuthorizationStrategy currentTaskBrowserAuthorizationStrategy =
new CurrentTaskBrowserAuthorizationStrategy(
mContext,
mActivity,
mFragment);
currentTaskBrowserAuthorizationStrategy.setBrowser(browser);
return currentTaskBrowserAuthorizationStrategy;
return new CurrentTaskBrowserAuthorizationStrategy(
mContext,
mActivity,
mFragment,
browser);
} else {
final DefaultBrowserAuthorizationStrategy defaultBrowserAuthorizationStrategy = new DefaultBrowserAuthorizationStrategy(
return new DefaultBrowserAuthorizationStrategy(
mContext,
mActivity,
mFragment,
isBrokerRequest
isBrokerRequest,
browser
);
defaultBrowserAuthorizationStrategy.setBrowser(browser);
return defaultBrowserAuthorizationStrategy;
}
}

// Suppressing unchecked warnings due to casting of EmbeddedWebViewAuthorizationStrategy to GenericAuthorizationStrategy
@SuppressWarnings(WarningType.unchecked_warning)
private IAuthorizationStrategy getGenericAuthorizationStrategy() {
/**
* Get the generic authorization strategy.
*
* @param browser The browser to use in case we need to use a browser.
* @return The embedded web view authorization strategy.
*/
private IAuthorizationStrategy getGenericAuthorizationStrategy(@Nullable final Browser browser) {
return new EmbeddedWebViewAuthorizationStrategy(
mContext,
mActivity,
mFragment);
mFragment,
browser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.microsoft.identity.common.internal.ui.browser.Browser;
import com.microsoft.identity.common.internal.ui.browser.BrowserAuthorizationStrategy;
import com.microsoft.identity.common.java.WarningType;
import com.microsoft.identity.common.java.providers.oauth2.AuthorizationRequest;
Expand All @@ -43,8 +44,9 @@ public class CurrentTaskBrowserAuthorizationStrategy<
extends BrowserAuthorizationStrategy<GenericOAuth2Strategy, GenericAuthorizationRequest> {
public CurrentTaskBrowserAuthorizationStrategy(@NonNull Context applicationContext,
@NonNull Activity activity,
@Nullable Fragment fragment) {
super(applicationContext, activity, fragment);
@Nullable Fragment fragment,
@NonNull final Browser browser) {
super(applicationContext, activity, fragment, browser);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,17 @@ public abstract class BrowserAuthorizationStrategy<

private CustomTabsManager mCustomTabManager;
private ResultFuture<AuthorizationResult> mAuthorizationResultFuture;
private Browser mBrowser;
private boolean mDisposed;
private GenericOAuth2Strategy mOAuth2Strategy; //NOPMD
private GenericAuthorizationRequest mAuthorizationRequest; //NOPMD

private final Browser mBrowser;

public BrowserAuthorizationStrategy(@NonNull Context applicationContext,
@NonNull Activity activity,
@Nullable Fragment fragment) {
@Nullable Fragment fragment,
@NonNull Browser browser) {
super(applicationContext, activity, fragment);
}

public void setBrowser(final Browser browser) {
mBrowser = browser;
}

Expand Down Expand Up @@ -169,7 +168,7 @@ public void completeAuthorization(int requestCode, @NonNull final RawAuthorizati
/**
* Disposes state that will not normally be handled by garbage collection. This should be
* called when the authorization service is no longer required, including when any owning
* activity is paused or destroyed (i.e. in {@link android.app.Activity#onStop()}).
* activity is paused or destroyed (i.e. in Activity#onStop()).
*/
public void dispose() {
if (mDisposed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ private CustomTabsSession createSession(@Nullable final CustomTabsCallback callb
* if available when the {@link CustomTabsServiceConnection} is connected or the
* {@link CustomTabsManager#CUSTOM_TABS_MAX_CONNECTION_TIMEOUT} is timed out.
*/
public CustomTabsClient getClient() {
private CustomTabsClient getClient() {
final String methodTag = TAG + ":getClient";
try {
mClientLatch.await(CUSTOM_TABS_MAX_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ public class DefaultBrowserAuthorizationStrategy<
public DefaultBrowserAuthorizationStrategy(@NonNull Context applicationContext,
@NonNull Activity activity,
@Nullable Fragment fragment,
boolean isRequestFromBroker) {
super(applicationContext, activity, fragment);
boolean isRequestFromBroker,
@NonNull final Browser browser) {
super(applicationContext, activity, fragment, browser);
mIsRequestFromBroker = isRequestFromBroker;
}

Expand Down
Loading
Loading