Skip to content

Commit 7150965

Browse files
authored
Merge branch 'dev' into SJAIN/add-2s-timeout-to-IMDS-call
2 parents 6d850b1 + 92eace8 commit 7150965

File tree

6 files changed

+64
-22
lines changed

6 files changed

+64
-22
lines changed

msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,15 @@ public void acquireTokenWithAuthorizationCode_B2C_Local(String environment) {
7474
cfg = new Config(environment);
7575

7676
User user = labUserProvider.getB2cUser(cfg.azureEnvironment, B2CProvider.LOCAL);
77-
assertAcquireTokenB2C(user);
77+
assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY);
78+
}
79+
80+
@Test(dataProvider = "environments", dataProviderClass = EnvironmentsProvider.class)
81+
public void acquireTokenWithAuthorizationCode_B2C_LegacyFormat(String environment) {
82+
cfg = new Config(environment);
83+
84+
User user = labUserProvider.getB2cUser(cfg.azureEnvironment, B2CProvider.LOCAL);
85+
assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT);
7886
}
7987

8088
@Test
@@ -126,13 +134,13 @@ private void assertAcquireTokenADFS2019(User user) {
126134
Assert.assertEquals(user.getUpn(), result.account().username());
127135
}
128136

129-
private void assertAcquireTokenB2C(User user) {
137+
private void assertAcquireTokenB2C(User user, String authority) {
130138

131139
PublicClientApplication pca;
132140
try {
133141
pca = PublicClientApplication.builder(
134142
user.getAppId()).
135-
b2cAuthority(TestConstants.B2C_AUTHORITY_SIGN_IN).
143+
b2cAuthority(authority + TestConstants.B2C_SIGN_IN_POLICY).
136144
build();
137145
} catch (MalformedURLException ex) {
138146
throw new RuntimeException(ex.getMessage());

msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ public class TestConstants {
3838
public final static String ARLINGTON_TENANT_SPECIFIC_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + ARLINGTON_AUTHORITY_TENANT;
3939
public final static String ARLINGTON_GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.us/.default";
4040

41+
public final static String B2C_AUTHORITY = "https://msidlabb2c.b2clogin.com/msidlabb2c.onmicrosoft.com/";
42+
public final static String B2C_AUTHORITY_LEGACY_FORMAT = "https://msidlabb2c.b2clogin.com/tfp/msidlabb2c.onmicrosoft.com/";
4143

42-
public final static String B2C_AUTHORITY = "https://msidlabb2c.b2clogin.com/tfp/msidlabb2c.onmicrosoft.com/";
4344
public final static String B2C_ROPC_POLICY = "B2C_1_ROPC_Auth";
4445
public final static String B2C_SIGN_IN_POLICY = "B2C_1_SignInPolicy";
4546
public final static String B2C_AUTHORITY_SIGN_IN = B2C_AUTHORITY + B2C_SIGN_IN_POLICY;

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,18 @@ public T authority(String val) throws MalformedURLException {
373373
return self();
374374
}
375375

376+
/**
377+
* Set URL of the authenticating B2C authority from which MSAL will acquire tokens
378+
*
379+
* Valid B2C authorities should look like: https://<something.b2clogin.com/<tenant>/<policy>
380+
*
381+
* MSAL Java also supports a legacy B2C authority format, which looks like: https://<host>/tfp/<tenant>/<policy>
382+
*
383+
* However, MSAL Java will eventually stop supporting the legacy format. See here for information on how to migrate to the new format: https://aka.ms/msal4j-b2c
384+
*
385+
* @param val a boolean value for validateAuthority
386+
* @return instance of the Builder on which method was called
387+
*/
376388
public T b2cAuthority(String val) throws MalformedURLException {
377389
authority = Authority.enforceTrailingSlash(val);
378390

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/Authority.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ abstract class Authority {
2020

2121
private static final String ADFS_PATH_SEGMENT = "adfs";
2222
private static final String B2C_PATH_SEGMENT = "tfp";
23+
private static final String B2C_HOST_SEGMENT = "b2clogin.com";
2324

2425
private final static String USER_REALM_ENDPOINT = "common/userrealm";
2526
private final static String userRealmEndpointFormat = "https://%s/" + USER_REALM_ENDPOINT + "/%s?api-version=1.0";
@@ -79,9 +80,10 @@ static AuthorityType detectAuthorityType(URL authorityUrl) {
7980
"authority Uri should have at least one segment in the path (i.e. https://<host>/<path>/...)");
8081
}
8182

83+
final String host = authorityUrl.getHost();
8284
final String firstPath = path.substring(0, path.indexOf("/"));
8385

84-
if (isB2CAuthority(firstPath)) {
86+
if (isB2CAuthority(host, firstPath)) {
8587
return AuthorityType.B2C;
8688
} else if (isAdfsAuthority(firstPath)) {
8789
return AuthorityType.ADFS;
@@ -131,7 +133,11 @@ static void validateAuthority(URL authorityUrl) {
131133
static String getTenant(URL authorityUrl, AuthorityType authorityType) {
132134
String[] segments = authorityUrl.getPath().substring(1).split("/");
133135
if (authorityType == AuthorityType.B2C) {
134-
return segments[1];
136+
if (segments.length < 3){
137+
return segments[0];
138+
} else {
139+
return segments[1];
140+
}
135141
}
136142
return segments[0];
137143
}
@@ -144,8 +150,8 @@ private static boolean isAdfsAuthority(final String firstPath) {
144150
return firstPath.compareToIgnoreCase(ADFS_PATH_SEGMENT) == 0;
145151
}
146152

147-
private static boolean isB2CAuthority(final String firstPath) {
148-
return firstPath.compareToIgnoreCase(B2C_PATH_SEGMENT) == 0;
153+
private static boolean isB2CAuthority(final String host, final String firstPath) {
154+
return host.contains(B2C_HOST_SEGMENT) || firstPath.compareToIgnoreCase(B2C_PATH_SEGMENT) == 0;
149155
}
150156

151157
String deviceCodeEndpoint() {

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/B2CAuthority.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,42 @@ class B2CAuthority extends Authority {
2626
}
2727

2828
private void validatePathSegments(String[] segments) {
29-
if (segments.length < 3) {
29+
if (segments.length < 2) {
3030
throw new IllegalArgumentException(
31-
"B2C 'authority' Uri should have at least 3 segments in the path " +
32-
"(i.e. https://<host>/tfp/<tenant>/<policy>/...)");
31+
"Valid B2C 'authority' URLs should follow either of these formats: https://<host>/<tenant>/<policy>/... or https://<host>/something/<tenant>/<policy>/...");
3332
}
3433
}
3534

3635
private void setAuthorityProperties() {
3736
String[] segments = canonicalAuthorityUrl.getPath().substring(1).split("/");
3837

38+
// In the early days of MSAL, the only way for the library to identify a B2C authority was whether or not the authority
39+
// had three segments in the path, and the first segment was 'tfp'. Valid B2C authorities looked like: https://<host>/tfp/<tenant>/<policy>/...
40+
//
41+
// More recent changes to B2C should ensure that any new B2C authorities have 'b2clogin.com' in the host of the URL,
42+
// so app developers shouldn't need to add 'tfp' and the first path segment should just be the tenant: https://<something>.b2clogin.com/<tenant>/<policy>/...
43+
//
44+
// However, legacy URLs using the old format must still be supported by these sorts of checks here and elsewhere, so for the near
45+
// future at least we must consider both formats as valid until we're either sure all customers are swapped,
46+
// or until we're comfortable with a potentially breaking change
3947
validatePathSegments(segments);
4048

41-
policy = segments[2];
42-
43-
final String b2cAuthorityFormat = "https://%s/%s/%s/%s/";
44-
this.authority = String.format(
45-
b2cAuthorityFormat,
46-
canonicalAuthorityUrl.getAuthority(),
47-
segments[0],
48-
segments[1],
49-
segments[2]);
49+
try {
50+
policy = segments[2];
51+
this.authority = String.format(
52+
"https://%s/%s/%s/%s/",
53+
canonicalAuthorityUrl.getAuthority(),
54+
segments[0],
55+
segments[1],
56+
segments[2]);
57+
} catch (IndexOutOfBoundsException e){
58+
policy = segments[1];
59+
this.authority = String.format(
60+
"https://%s/%s/%s/",
61+
canonicalAuthorityUrl.getAuthority(),
62+
segments[0],
63+
segments[1]);
64+
}
5065

5166
this.authorizationEndpoint = String.format(B2C_AUTHORIZATION_ENDPOINT_FORMAT, host, tenant, policy);
5267
this.tokenEndpoint = String.format(B2C_TOKEN_ENDPOINT_FORMAT, host, tenant, policy);

msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/AuthorityTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ public void testDetectAuthorityType_B2C() throws Exception {
3636

3737
@Test(expectedExceptions = IllegalArgumentException.class,
3838
expectedExceptionsMessageRegExp =
39-
"B2C 'authority' Uri should have at least 3 segments in the path \\(i.e. https://<host>/tfp/<tenant>/<policy>/...\\)")
39+
"Valid B2C 'authority' URLs should follow either of these formats.*")
4040
public void testB2CAuthorityConstructor_NotEnoughSegments() throws MalformedURLException {
41-
new B2CAuthority(new URL("https://something.com/tfp/somethingelse/"));
41+
new B2CAuthority(new URL("https://something.com/somethingelse/"));
4242
}
4343

4444
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "authority should use the 'https' scheme")

0 commit comments

Comments
 (0)