Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [MINOR] Add telemetry for the switch browser protocol (#2612)
- [MINOR] Enable the new Broker discovery on MSAL by default (#2618)

Version 21.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.PRODUCT;
import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION;

import io.opentelemetry.api.trace.SpanContext;

/**
* Authorization fragment with embedded webview.
*/
Expand Down Expand Up @@ -536,7 +538,8 @@ public void setPKeyAuthStatus(final boolean status) {

private SwitchBrowserProtocolCoordinator getSwitchBrowserCoordinator() {
if (mSwitchBrowserProtocolCoordinator == null) {
mSwitchBrowserProtocolCoordinator = new SwitchBrowserProtocolCoordinator(requireActivity());
final SpanContext spanContext = requireActivity() instanceof AuthorizationActivity ? ((AuthorizationActivity) requireActivity()).getSpanContext() : null;
mSwitchBrowserProtocolCoordinator = new SwitchBrowserProtocolCoordinator(requireActivity(), spanContext);
}
return mSwitchBrowserProtocolCoordinator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ import com.microsoft.identity.common.internal.ui.browser.CustomTabsManager
import com.microsoft.identity.common.internal.ui.webview.switchbrowser.SwitchBrowserUriHelper.isSwitchBrowserRedirectUrl
import com.microsoft.identity.common.java.browser.IBrowserSelector
import com.microsoft.identity.common.java.exception.ClientException
import com.microsoft.identity.common.java.opentelemetry.AttributeName
import com.microsoft.identity.common.java.opentelemetry.OTelUtility
import com.microsoft.identity.common.java.opentelemetry.SpanExtension
import com.microsoft.identity.common.java.opentelemetry.SpanName
import com.microsoft.identity.common.java.ui.BrowserDescriptor
import com.microsoft.identity.common.logging.Logger
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanContext
import io.opentelemetry.api.trace.StatusCode

/**
* SwitchBrowserRequestHandler is a challenge handler for SwitchBrowserChallenge.
Expand All @@ -42,9 +49,13 @@ class SwitchBrowserRequestHandler(
private val activity: Activity,
private val context: Context,
private val customTabsManager: CustomTabsManager,
private val browserSelector: IBrowserSelector
private val browserSelector: IBrowserSelector,
private val spanContext: SpanContext?
) : IChallengeHandler<SwitchBrowserChallenge, Unit> {

val span: Span by lazy {
OTelUtility.createSpanFromParent(SpanName.SwitchBrowserProcess.name, spanContext)
}

var isChallengeHandled: Boolean = false
private set
Expand All @@ -53,11 +64,12 @@ class SwitchBrowserRequestHandler(
private val TAG = SwitchBrowserRequestHandler::class.simpleName
}

constructor(activity: Activity) : this(
constructor(activity: Activity, spanContext: SpanContext?) : this(
activity,
activity.applicationContext,
CustomTabsManager(activity.applicationContext),
AndroidBrowserSelector(activity.applicationContext)
AndroidBrowserSelector(activity.applicationContext),
spanContext
)

/**
Expand All @@ -69,49 +81,68 @@ class SwitchBrowserRequestHandler(
*/
@Throws(ClientException::class)
override fun processChallenge(switchBrowserChallenge: SwitchBrowserChallenge) {
val methodTag = "$TAG:processChallenge"
SpanExtension.makeCurrentSpan(span).use {
val methodTag = "$TAG:processChallenge"

// Select a browser to handle the switch browser challenge
val browser = browserSelector.selectBrowser(
BrowserDescriptor.getBrowserSafeListForSwitchBrowser(),
null
)
if (browser == null) {
val exception = ClientException(
ClientException.NO_BROWSERS_AVAILABLE,
"No browser found for SwitchBrowserChallenge."
)
Logger.error(
methodTag,
"No browser found for SwitchBrowserChallenge.",
exception
// Select a browser to handle the switch browser challenge
val browser = browserSelector.selectBrowser(
BrowserDescriptor.getBrowserSafeListForSwitchBrowser(),
null
)
throw exception
}
if (browser == null) {
val exception = ClientException(
ClientException.NO_BROWSERS_AVAILABLE,
"No browser found for SwitchBrowserChallenge."
)
Logger.error(
methodTag,
"No browser found for SwitchBrowserChallenge.",
exception
)
span.setStatus(StatusCode.ERROR)
span.recordException(exception)
span.end()
throw exception
}

// Create an intent to launch the browser
val browserIntent: Intent
if (browser.isCustomTabsServiceSupported) {
Logger.info(methodTag, "CustomTabsService is supported.")
//create customTabsIntent
if (!customTabsManager.bind(context, browser.packageName)) {
Logger.warn(methodTag, "Failed to bind CustomTabsService.")
browserIntent = Intent(Intent.ACTION_VIEW)
// Create an intent to launch the browser
val browserIntent: Intent
if (browser.isCustomTabsServiceSupported) {
Logger.info(methodTag, "CustomTabsService is supported.")
//create customTabsIntent
if (!customTabsManager.bind(context, browser.packageName)) {
Logger.warn(methodTag, "Failed to bind CustomTabsService.")
browserIntent = Intent(Intent.ACTION_VIEW)
} else {
browserIntent = customTabsManager.customTabsIntent.intent
}
} else {
browserIntent = customTabsManager.customTabsIntent.intent
Logger.warn(methodTag, "CustomTabsService is NOT supported")
browserIntent = Intent(Intent.ACTION_VIEW)
}
} else {
Logger.warn(methodTag, "CustomTabsService is NOT supported")
browserIntent = Intent(Intent.ACTION_VIEW)
Logger.info(
methodTag,
"Launching switch browser request on browser: ${browser.packageName}"
)
browserIntent.setPackage(browser.packageName)
browserIntent.setData(switchBrowserChallenge.uri)
activity.startActivity(browserIntent)
isChallengeHandled = true
span.setAttribute(
AttributeName.is_switch_browser_request_handled.name,
isChallengeHandled
)
span.setAttribute(
AttributeName.browser_package_name.name,
browser.packageName
)
span.setAttribute(
AttributeName.is_custom_tabs_supported.name,
browser.isCustomTabsServiceSupported
)
span.setStatus(StatusCode.OK)
span.end()
}
Logger.info(
methodTag,
"Launching switch browser request on browser: ${browser.packageName}"
)
browserIntent.setPackage(browser.packageName)
browserIntent.setData(switchBrowserChallenge.uri)
activity.startActivity(browserIntent)
isChallengeHandled = true
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,29 @@ import com.microsoft.identity.common.internal.ui.webview.switchbrowser.SwitchBro
import com.microsoft.identity.common.internal.ui.webview.switchbrowser.SwitchBrowserUriHelper.isSwitchBrowserRedirectUrl
import com.microsoft.identity.common.java.AuthenticationConstants.AAD.AUTHORIZATION
import com.microsoft.identity.common.java.exception.ClientException
import com.microsoft.identity.common.java.opentelemetry.AttributeName
import com.microsoft.identity.common.java.opentelemetry.OTelUtility
import com.microsoft.identity.common.java.opentelemetry.SpanExtension
import com.microsoft.identity.common.java.opentelemetry.SpanName
import com.microsoft.identity.common.java.ui.AuthorizationAgent
import com.microsoft.identity.common.logging.Logger
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanContext
import io.opentelemetry.api.trace.StatusCode

/**
* SwitchBrowserProtocolCoordinator is responsible for coordinating the switch browser protocol.
* Contains the handler to process the switch browser request and resume action.
*/
class SwitchBrowserProtocolCoordinator(
val switchBrowserRequestHandler: SwitchBrowserRequestHandler) {
constructor(activity: Activity) : this(SwitchBrowserRequestHandler(activity))
val switchBrowserRequestHandler: SwitchBrowserRequestHandler,
private val spanContext: SpanContext? = null) {

constructor(activity: Activity, spanContext: SpanContext?) : this(SwitchBrowserRequestHandler(activity, spanContext), spanContext)

val span: Span by lazy {
OTelUtility.createSpanFromParent(SpanName.SwitchBrowserResume.name, spanContext)
}

companion object {
private const val TAG = "SwitchBrowserProtocolCoordinator"
Expand Down Expand Up @@ -99,21 +112,30 @@ class SwitchBrowserProtocolCoordinator(
extras: Bundle,
onSuccessAction: (Uri, HashMap<String, String>) -> Unit
) {
val methodTag = "$TAG:processSwitchBrowserResume"
val actionUri = extras.getString(SWITCH_BROWSER.ACTION_URI)
val code = extras.getString(SWITCH_BROWSER.CODE)
if (actionUri.isNullOrEmpty() || code.isNullOrEmpty()) {
throw ClientException(
ClientException.MISSING_PARAMETER,
"Action URI is null/empty: ${actionUri == null}, code is null/empty: ${code == null}"
)
SpanExtension.makeCurrentSpan(span).use {
val methodTag = "$TAG:processSwitchBrowserResume"
val actionUri = extras.getString(SWITCH_BROWSER.ACTION_URI)
val code = extras.getString(SWITCH_BROWSER.CODE)
if (actionUri.isNullOrEmpty() || code.isNullOrEmpty()) {
val clientException = ClientException(
ClientException.MISSING_PARAMETER,
"Action URI is null/empty: ${actionUri == null}, code is null/empty: ${code == null}"
)
span.setStatus(StatusCode.ERROR)
span.recordException(clientException)
span.end()
throw clientException
}
val resumeUri = buildResumeUri(actionUri)
val headers = hashMapOf(AUTHORIZATION to code)
onSuccessAction(resumeUri, headers)
// Reset the challenge state after processing the resume action
switchBrowserRequestHandler.resetChallengeState()
Logger.info(methodTag, "Switch browser resume action processed successfully.")
span.setAttribute(AttributeName.is_switch_browser_resume_handled.name, true)
span.setStatus(StatusCode.OK)
span.end()
}
val resumeUri = buildResumeUri(actionUri)
val headers = hashMapOf(AUTHORIZATION to code)
onSuccessAction(resumeUri, headers)
// Reset the challenge state after processing the resume action
switchBrowserRequestHandler.resetChallengeState()
Logger.info(methodTag, "Switch browser resume action processed successfully.")
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class SwitchBrowserRequestHandlerTest {
`when`(challenge.uri).thenReturn(Uri.parse("https://example.com"))
val browserSelector = // Browser available
IBrowserSelector { _, _ -> Browser("fakeBrowser", emptySet(), "browser", false) }
val handler = SwitchBrowserRequestHandler(mockActivity, context, customTabsManager, browserSelector)
val handler = SwitchBrowserRequestHandler(mockActivity, context, customTabsManager, browserSelector, null)
handler.processChallenge(challenge)
Assert.assertTrue(activityExecuted)
}
Expand All @@ -75,7 +75,7 @@ class SwitchBrowserRequestHandlerTest {
val challenge = mock(SwitchBrowserChallenge::class.java)
`when`(challenge.uri).thenReturn(Uri.parse("https://example.com"))
val browserSelector = IBrowserSelector { _, _ -> null } // No browser available
val handler = SwitchBrowserRequestHandler(activity, context, customTabsManager, browserSelector)
val handler = SwitchBrowserRequestHandler(activity, context, customTabsManager, browserSelector, null)
val exception = Assert.assertThrows(ClientException::class.java) {
handler.processChallenge(challenge)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,5 +338,30 @@ public enum AttributeName {
/**
* Records if current flow is a mam flow.
*/
is_mam_flow
is_mam_flow,

/**
* Records if current flow is a switch browser protocol.
*/
is_switch_browser_protocol,

/**
* Records the browser package name.
*/
browser_package_name,

/**
* Records the if browser package name supports custom tabs.
*/
is_custom_tabs_supported,

/**
* Records the if the broker handled a switch browser request,
*/
is_switch_browser_request_handled,

/**
* Records the if the broker handled a switch browser resume,
*/
is_switch_browser_resume_handled
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,7 @@ public enum SpanName {
ProcessNonceFromEstsRedirect,
DataStoreCorruptionException,
KeyPairGeneration,
ProcessCrossCloudRedirect
ProcessCrossCloudRedirect,
SwitchBrowserResume,
SwitchBrowserProcess,
}
Loading