Skip to content

Commit 3c192cb

Browse files
authored
DUNA flow: Resume Auth , Fixes AB#3079802 (#2557)
This PR addresses step 16 of the [Switch browser flow](https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview/pullrequest/15199?_a=files&path=/%5BAndroid%5D%20DUNA/DUNA%20implementation%20on%20android%20broker.md) - [BrokerBrowserRedirectActivity will re-start the WebViewAuthorizationFragment](AzureAD/ad-accounts-for-android#3021) so we update the intent data and handle the activity being resumed on WebViewAuthorizationFragment.onResume - The switch browser resume endpoint requires the client ID, so we need to pass this value at the moment we start the WebAuthorizaFragment. - Refactor AuthorizationActivityFactory to work with one data parameter to create the auth intent. [AB#3079802](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3079802)
1 parent aac5d78 commit 3c192cb

20 files changed

Lines changed: 885 additions & 441 deletions

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Add handler for resume switch_browser flow on WebViewAuthorizationFragment (#2557)
34
- [PATCH] Track MAM flow in telemetry (#2608)
45
- [PATCH] Fix multiple prompts issue in cross cloud request (#2599)
56
- [PATCH] Corrected error handling in cross cloud scenario (#2602)

common/src/main/java/com/microsoft/identity/common/adal/internal/AuthenticationConstants.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,10 +535,20 @@ public static final class AAD {
535535
@NoArgsConstructor(access = AccessLevel.PRIVATE)
536536
public static final class SWITCH_BROWSER {
537537

538+
/**
539+
* Path used on the broker redirect url to indicate the resume of the switch browser flow.
540+
*/
541+
public static final String RESUME_PATH = "switch_browser_resume";
542+
543+
/**
544+
* Path used on the broker redirect url to indicate the start of the switch browser flow.
545+
*/
546+
public static final String REQUEST_PATH = "switch_browser";
547+
538548
/**
539549
* String Query parameter key to indicate support for SWITCH_BROWSER protocol.
540550
*/
541-
public static final String PATH = "switch_browser";
551+
public static final String CLIENT_SUPPORTS_FLOW = "switch_browser";
542552

543553
/**
544554
* String Query parameter key for the purpose token.

common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivity.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ public class AuthorizationActivity extends DualScreenActivity {
4343
@Getter
4444
@Accessors(prefix = "m")
4545
private SpanContext mSpanContext;
46-
47-
@Getter
48-
@Accessors(prefix = "m")
4946
private AuthorizationFragment mFragment;
5047

48+
public AuthorizationFragment getFragment() {
49+
return mFragment;
50+
}
51+
5152
@Override
5253
public void onCreate(@Nullable Bundle savedInstanceState) {
5354
super.onCreate(savedInstanceState);

common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactory.java

Lines changed: 0 additions & 316 deletions
This file was deleted.
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal.providers.oauth2
24+
25+
import android.content.Intent
26+
import androidx.fragment.app.Fragment
27+
import com.microsoft.identity.common.adal.internal.AuthenticationConstants
28+
import com.microsoft.identity.common.internal.msafederation.getIdProviderExtraQueryParamForAuthorization
29+
import com.microsoft.identity.common.internal.msafederation.getIdProviderHeadersForAuthorization
30+
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleApi.Companion.getInstance
31+
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential
32+
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleParameters
33+
import com.microsoft.identity.common.internal.util.CommonMoshiJsonAdapter
34+
import com.microsoft.identity.common.internal.util.ProcessUtil
35+
import com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.PRODUCT
36+
import com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION
37+
import com.microsoft.identity.common.java.configuration.LibraryConfiguration
38+
import com.microsoft.identity.common.java.exception.ClientException
39+
import com.microsoft.identity.common.java.logging.DiagnosticContext
40+
import com.microsoft.identity.common.java.opentelemetry.SerializableSpanContext
41+
import com.microsoft.identity.common.java.opentelemetry.SpanExtension
42+
import com.microsoft.identity.common.java.ui.AuthorizationAgent
43+
import com.microsoft.identity.common.java.util.CommonURIBuilder
44+
import java.net.URISyntaxException
45+
46+
47+
/**
48+
* Constructs intents and/or fragments for interactive requests based on library configuration and current request.
49+
*/
50+
object AuthorizationActivityFactory {
51+
/**
52+
* Return the correct authorization activity based on library configuration.
53+
*
54+
* @param parameters The parameters to use to create the intent.
55+
* @return An android Intent which will be used by Android to create an AuthorizationActivity
56+
*/
57+
@JvmStatic
58+
fun getAuthorizationActivityIntent(parameters: AuthorizationActivityParameters): Intent {
59+
val intent: Intent
60+
val libraryConfig = LibraryConfiguration.getInstance()
61+
if (ProcessUtil.isBrokerProcess(parameters.context)) {
62+
intent = Intent(parameters.context, BrokerAuthorizationActivity::class.java)
63+
if (parameters.requestUrl.contains(AuthenticationConstants.SWITCH_BROWSER.CLIENT_SUPPORTS_FLOW)) {
64+
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
65+
// In the case of a SwitchBrowser protocol, we need to transition from the browser to the WebView.
66+
// These flags ensure that we have a new task stack that allows for this transition.
67+
}
68+
} else if (libraryConfig.isAuthorizationInCurrentTask && parameters.authorizationAgent != AuthorizationAgent.WEBVIEW) {
69+
// We exclude the case when the authorization agent is already selected as WEBVIEW because of confusion
70+
// that results from attempting to use the CurrentTaskAuthorizationActivity in that case, because as webview
71+
// already uses the current task, attempting to manually simulate that behavior ends up supplying an incorrect
72+
// Fragment to the activity.
73+
intent = Intent(parameters.context, CurrentTaskAuthorizationActivity::class.java)
74+
} else {
75+
intent = Intent(parameters.context, AuthorizationActivity::class.java)
76+
}
77+
78+
intent.apply {
79+
putExtra(
80+
AuthenticationConstants.AuthorizationIntentKey.AUTH_INTENT,
81+
parameters.authIntent
82+
)
83+
putExtra(
84+
AuthenticationConstants.AuthorizationIntentKey.REQUEST_URL,
85+
parameters.requestUrl
86+
)
87+
putExtra(
88+
AuthenticationConstants.AuthorizationIntentKey.REDIRECT_URI,
89+
parameters.redirectUri
90+
)
91+
putExtra(
92+
AuthenticationConstants.AuthorizationIntentKey.REQUEST_HEADERS,
93+
parameters.requestHeader
94+
)
95+
putExtra(
96+
AuthenticationConstants.AuthorizationIntentKey.AUTHORIZATION_AGENT,
97+
parameters.authorizationAgent
98+
)
99+
putExtra(
100+
AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_CONTROLS_ENABLED,
101+
parameters.webViewZoomControlsEnabled
102+
)
103+
putExtra(
104+
AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_ENABLED,
105+
parameters.webViewZoomEnabled
106+
)
107+
putExtra(
108+
DiagnosticContext.CORRELATION_ID,
109+
DiagnosticContext.INSTANCE.requestContext[DiagnosticContext.CORRELATION_ID]
110+
)
111+
putExtra(
112+
SerializableSpanContext.SERIALIZABLE_SPAN_CONTEXT, CommonMoshiJsonAdapter().toJson(
113+
SerializableSpanContext.builder()
114+
.traceId(SpanExtension.current().spanContext.traceId)
115+
.spanId(SpanExtension.current().spanContext.spanId)
116+
.traceFlags(SpanExtension.current().spanContext.traceFlags.asByte())
117+
.build()
118+
)
119+
)
120+
if (parameters.sourceLibraryName != null) {
121+
putExtra(PRODUCT, parameters.sourceLibraryName)
122+
}
123+
if (parameters.sourceLibraryVersion != null) {
124+
putExtra(VERSION, parameters.sourceLibraryVersion)
125+
}
126+
}
127+
return intent
128+
}
129+
130+
/**
131+
* Returns the correct authorization fragment for local (non-broker) authorization flows.
132+
* Fragments include:
133+
* [WebViewAuthorizationFragment]
134+
* [BrowserAuthorizationFragment]
135+
* [CurrentTaskBrowserAuthorizationFragment]
136+
*
137+
* @param intent The intent used to start the authorization flow.
138+
* @return returns an Fragment that's used as to authorize a token request.
139+
*/
140+
@JvmStatic
141+
fun getAuthorizationFragmentFromStartIntent(intent: Intent): Fragment {
142+
val fragment: Fragment
143+
val authorizationAgent =
144+
intent.getSerializableExtra(AuthenticationConstants.AuthorizationIntentKey.AUTHORIZATION_AGENT) as AuthorizationAgent?
145+
146+
val libraryConfig = LibraryConfiguration.getInstance()
147+
148+
fragment =
149+
if (authorizationAgent == AuthorizationAgent.WEBVIEW) {
150+
WebViewAuthorizationFragment()
151+
} else {
152+
if (libraryConfig.isAuthorizationInCurrentTask) {
153+
CurrentTaskBrowserAuthorizationFragment()
154+
} else {
155+
BrowserAuthorizationFragment()
156+
}
157+
}
158+
159+
return fragment
160+
}
161+
162+
/**
163+
* This method first starts sign in with google flow displaying UX for user add/select a google account
164+
* and after success creates intent with result obtained from successful google sign in and other input
165+
* parameters.
166+
*
167+
* @param authorizationActivityParameters Parameters to create the auth intent
168+
* @param signInWithGoogleParameters Parameters to first start sign in with google flow before creating the intent
169+
* @return An android Intent which will be used by Android to create an AuthorizationActivity
170+
*/
171+
@JvmStatic
172+
fun signInWithGoogleAndGetAuthorizationActivityIntent(
173+
authorizationActivityParameters: AuthorizationActivityParameters,
174+
signInWithGoogleParameters: SignInWithGoogleParameters
175+
): Intent {
176+
return getAuthorizationActivityIntent(
177+
authorizationActivityParameters,
178+
getInstance().signInSync(signInWithGoogleParameters)
179+
)
180+
}
181+
182+
/**
183+
* This method first starts sign in with google flow displaying UX for user add/select a google account
184+
* and after success creates intent with result obtained from successful google sign in and other input
185+
* parameters.
186+
*
187+
* @param authorizationActivityParameters Parameters to create the auth intent
188+
* @return An android Intent which will be used by Android to create an AuthorizationActivity
189+
*/
190+
@JvmStatic
191+
@Throws(ClientException::class)
192+
fun getAuthorizationActivityIntent(
193+
authorizationActivityParameters: AuthorizationActivityParameters,
194+
signInWithGoogleCredential: SignInWithGoogleCredential
195+
): Intent {
196+
197+
// add header
198+
val requestHeadersWithGoogleAuthCredential =
199+
if (authorizationActivityParameters.requestHeader.isNullOrEmpty()) {
200+
HashMap()
201+
} else {
202+
HashMap(authorizationActivityParameters.requestHeader)
203+
}
204+
requestHeadersWithGoogleAuthCredential.putAll(signInWithGoogleCredential.getIdProviderHeadersForAuthorization())
205+
206+
// add id provider query parameter
207+
val requestUrlWithIdProvider: String
208+
try {
209+
val uriBuilder = CommonURIBuilder(authorizationActivityParameters.requestUrl)
210+
val extraQueryParamForAuthorization =
211+
signInWithGoogleCredential.getIdProviderExtraQueryParamForAuthorization()
212+
uriBuilder.addParameterIfAbsent(
213+
extraQueryParamForAuthorization.key,
214+
extraQueryParamForAuthorization.value
215+
)
216+
requestUrlWithIdProvider = uriBuilder.build().toString()
217+
} catch (e: URISyntaxException) {
218+
throw ClientException(
219+
ClientException.MALFORMED_URL,
220+
"Failed to add id provider query parameter to request URL",
221+
e
222+
)
223+
}
224+
val newAuthorizationActivityParameters = authorizationActivityParameters.copy(
225+
requestUrl = requestUrlWithIdProvider,
226+
requestHeader = requestHeadersWithGoogleAuthCredential
227+
)
228+
return getAuthorizationActivityIntent(newAuthorizationActivityParameters)
229+
}
230+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal.providers.oauth2
24+
25+
import android.content.Context
26+
import android.content.Intent
27+
import com.microsoft.identity.common.java.ui.AuthorizationAgent
28+
29+
/**
30+
* Parameters for the authorization activity.
31+
*
32+
* @param context Android application context
33+
* @param authIntent Android intent used by the authorization activity to launch the specific implementation of authorization (BROWSER, EMBEDDED)
34+
* @param requestUrl The authorization request in URL format
35+
* @param redirectUri The expected redirect URI associated with the authorization request
36+
* @param requestHeader Additional HTTP headers included with the authorization request
37+
* @param authorizationAgent The means by which authorization should be performed (EMBEDDED, WEBVIEW) NOTE: This should move to library configuration
38+
* @param webViewZoomEnabled This parameter is specific to embedded and controls whether webview zoom is enabled... NOTE: Needs refactoring
39+
* @param webViewZoomControlsEnabled This parameter is specific to embedded and controls whether webview zoom controls are enabled... NOTE: Needs refactoring
40+
* @param sourceLibraryName Product name to be of library making the request
41+
* @param sourceLibraryVersion Product version to be of library making the request
42+
*/
43+
data class AuthorizationActivityParameters @JvmOverloads constructor(
44+
val context: Context,
45+
val authIntent: Intent?,
46+
val requestUrl: String,
47+
val redirectUri: String,
48+
val requestHeader: HashMap<String, String>?,
49+
val authorizationAgent: AuthorizationAgent,
50+
val webViewZoomEnabled: Boolean = true,
51+
val webViewZoomControlsEnabled: Boolean = true,
52+
val sourceLibraryName: String? = null,
53+
val sourceLibraryVersion: String? = null
54+
)

common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/BrokerAuthorizationActivity.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,26 @@
2222
// THE SOFTWARE.
2323
package com.microsoft.identity.common.internal.providers.oauth2;
2424

25+
import android.content.Intent;
26+
2527
/**
2628
* Declares as a separate class so that we can specify attributes exclusively to :auth process
2729
* in AndroidManifest without overriding MSAL's (In case where MSAL and broker is shipped together).
2830
*/
2931
public class BrokerAuthorizationActivity extends AuthorizationActivity {
32+
33+
/**
34+
* Refreshes the WebView with new intent data after the user completes authentication in the browser.
35+
*
36+
* <p>In the Switch browser flow, once the user finishes authentication in the browser, ETS will send a request
37+
* to the broker containing a code and an action URI. The broker will then send this request data back to the
38+
* WebView authorization activity via an intent. This method is used to refresh the WebView with the new intent
39+
* data that includes the code and action URI.
40+
* see {@link WebViewAuthorizationFragment#onResume()}
41+
*/
42+
@Override
43+
protected void onNewIntent(final Intent intent) {
44+
super.onNewIntent(intent);
45+
setIntent(intent);
46+
}
3047
}

0 commit comments

Comments
 (0)