Skip to content

Commit bbc64c9

Browse files
sameeragCopilotCopilot
authored
Enable Edge API for Platform brokering (#8171)
This pull request: * Introduces support for configuring DOM API usage for platform broker authentication in MSAL.js, specifically targeting Edge browser environments. * Updates documentation to clarify platform broker support across Windows and Mac, adds a new configuration flag (`allowPlatformBrokerWithDOM`), and introduces validation to prevent misconfiguration. * Error handling and messaging are also improved for invalid configuration scenarios. --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c87be73 commit bbc64c9

12 files changed

+130
-100
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Add DOM API configuration, #8171",
4+
"packageName": "@azure/msal-browser",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Add DOM API configuration, #8171",
4+
"packageName": "@azure/msal-common",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

lib/msal-browser/apiReview/msal-browser.api.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ export type BrowserSystemOptions = SystemOptions & {
586586
asyncPopups?: boolean;
587587
allowRedirectInIframe?: boolean;
588588
allowPlatformBroker?: boolean;
589+
allowPlatformBrokerWithDOM?: boolean;
589590
nativeBrokerHandshakeTimeout?: number;
590591
pollIntervalMilliseconds?: number;
591592
};
@@ -1165,7 +1166,7 @@ function isInPopup(): boolean;
11651166
// Warning: (ae-missing-release-tag) "isPlatformBrokerAvailable" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
11661167
//
11671168
// @public
1168-
export function isPlatformBrokerAvailable(loggerOptions?: LoggerOptions, perfClient?: IPerformanceClient, correlationId?: string): Promise<boolean>;
1169+
export function isPlatformBrokerAvailable(loggerOptions?: LoggerOptions, perfClient?: IPerformanceClient, correlationId?: string, domConfig?: boolean): Promise<boolean>;
11691170

11701171
// Warning: (ae-missing-release-tag) "ITokenCache" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
11711172
//
@@ -1827,7 +1828,7 @@ export type WrapperSKU = (typeof WrapperSKU)[keyof typeof WrapperSKU];
18271828
// src/cache/LocalStorage.ts:363:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
18281829
// src/cache/LocalStorage.ts:426:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
18291830
// src/cache/LocalStorage.ts:457:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
1830-
// src/config/Configuration.ts:260:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts
1831+
// src/config/Configuration.ts:264:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts
18311832
// src/event/EventHandler.ts:113:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
18321833
// src/event/EventHandler.ts:139:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
18331834
// src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"

lib/msal-browser/docs/device-bound-tokens.md

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Acquiring Device Bound Tokens using Web Account Manager (WAM) on Windows
1+
# Acquiring Device Bound Tokens using platform brokers
22

3-
MSAL.js supports acquiring tokens from the Web Account Manager (WAM) on Windows. These tokens are bound to the device they were acquired on and are not cached in the browser's localStorage or sessionStorage.
3+
MSAL.js supports acquiring tokens from the platform broker, say Web Account Manager (WAM) on Windows and Mac Broker on Mac. These tokens are bound to the device they were acquired on and are not cached in the browser's localStorage or sessionStorage.
44

55
## Supported Environment
66

77
This feature is currently only supported in the following environment:
88

9-
- A machine running a Windows build that supports the feature (more to come on this)
9+
- A machine running a Windows build that supports the feature (more to come on this) or a Company Portal managed Mac with a specific Mac OS version (more to come on this).
1010
- Chrome and Edge browsers or Teams
1111
- [Windows Accounts extension](https://chrome.google.com/webstore/detail/windows-accounts/ppnbnpeolgkicgegkbkbjmhlideopiji) (version 1.0.5 or higher) is installed if using Chrome or Edge
1212
- App must be hosted on `https`
@@ -47,10 +47,35 @@ No other changes are needed to support this new feature. Any user accessing your
4747

4848
A working sample can be found [here](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/samples/msal-browser-samples/VanillaJSTestApp2.0/app/wamBroker)
4949

50-
## Differences when using WAM to acquire tokens
50+
## Differences when using Platform Broker to acquire tokens
5151

52-
There are a few things that may behave a little differently when acquiring tokens through WAM.
52+
There are a few things that may behave a little differently when acquiring tokens through the platform broker.
5353

54-
- All cache related configuration applies only to MSAL's local cache. The native broker controls its own, more secure, cache which is used instead of browser storage and it does not support configuration of its cache behavior. This means you may receive a cached token regardless of the value of request parameters such as: `forceRefresh`, `cacheLookupPolicy` or `storeInCache`. In addition, tokens received from the native broker are _not_ stored in local or session storage regardless of what you have configured on PublicClientApplication.
55-
- If WAM needs to prompt the user for interaction a system prompt will be opened. This prompt looks a bit different from the browser popup windows you may be used to.
56-
- Switching your account in the WAM prompt is not supported and MSAL.js will throw an error (Error Code: user_switch) if this happens. It is your app's responsibility to catch this error and handle it in a way that makes sense for your scenarios (e.g. Show an error page, retry with the new account, retry with the original account, etc.)
54+
- All cache related configuration applies only to MSAL's local cache. The platform broker controls its own, more secure, cache which is used instead of browser storage and it does not support configuration of its cache behavior. This means you may receive a cached token regardless of the value of request parameters such as: `forceRefresh`, `cacheLookupPolicy` or `storeInCache`. In addition, tokens received from the platform broker are _not_ stored in local or session storage regardless of what you have configured on PublicClientApplication.
55+
- If the platform broker needs to prompt the user for interaction a system prompt will be opened. This prompt looks a bit different from the browser popup windows you may be used to.
56+
- Switching your account in the platform broker prompt is not supported and MSAL.js will throw an error (Error Code: user_switch) if this happens. It is your app's responsibility to catch this error and handle it in a way that makes sense for your scenarios (e.g. Show an error page, retry with the new account, retry with the original account, etc.)
57+
58+
# Acquiring Device Bound Tokens using DOM API
59+
60+
MSAL.js also supports acquiring tokens from the platform broker using DOM APIs in Edge. Instead of using a browser extension to communicate with the platform broker, MSAL.js can directly call a DOM API in the Edge browser, which in turn manages to invoke the platform broker to acquire tokens.
61+
62+
- This feature is currently only supported in the Edge browser and all other OS requirements mentioned above still apply. (more details to come).
63+
- This feature is currently only in private-preview and requires special enablement. Please reach out to your Microsoft representative for more details.
64+
65+
To enable this feature, set the `allowPlatformBrokerWithDOM` flag to true in your configuration object like so:
66+
67+
```javascript
68+
const msalConfig = {
69+
auth: {
70+
clientId: "insert-clientId",
71+
},
72+
system: {
73+
allowPlatformBroker: true,
74+
allowPlatformBrokerWithDOM: true,
75+
},
76+
};
77+
```
78+
79+
Note: The `allowPlatformBroker` flag must also be set to true in order to use this feature. There will be a configuration error - `invalid_platform_broker_configuration` if `allowPlatformBrokerWithDOM` is set to true while `allowPlatformBroker` is false.
80+
81+
Eventually, in a future major version, this flag will be merged with `allowPlatformBroker` and MSAL.js will make the decision to use either the browser extension or DOM APIs based on the environment automatically.

lib/msal-browser/src/broker/nativeBroker/PlatformAuthProvider.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
Logger,
1010
AuthenticationScheme,
1111
StubPerformanceClient,
12+
createClientConfigurationError,
13+
ClientConfigurationErrorCodes,
1214
} from "@azure/msal-common/browser";
1315
import { name, version } from "../../packageMetadata.js";
1416
import {
@@ -19,8 +21,6 @@ import { PlatformAuthExtensionHandler } from "./PlatformAuthExtensionHandler.js"
1921
import { IPlatformAuthHandler } from "./IPlatformAuthHandler.js";
2022
import { PlatformAuthDOMHandler } from "./PlatformAuthDOMHandler.js";
2123
import { createNewGuid } from "../../crypto/BrowserCrypto.js";
22-
import { BrowserCacheLocation } from "../../utils/BrowserConstants.js";
23-
import { PLATFORM_AUTH_DOM_SUPPORT } from "../../cache/CacheKeys.js";
2424

2525
/**
2626
* Checks if the platform broker is available in the current environment.
@@ -31,7 +31,8 @@ import { PLATFORM_AUTH_DOM_SUPPORT } from "../../cache/CacheKeys.js";
3131
export async function isPlatformBrokerAvailable(
3232
loggerOptions?: LoggerOptions,
3333
perfClient?: IPerformanceClient,
34-
correlationId?: string
34+
correlationId?: string,
35+
domConfig?: boolean
3536
): Promise<boolean> {
3637
const logger = new Logger(loggerOptions || {}, name, version);
3738

@@ -47,24 +48,26 @@ export async function isPlatformBrokerAvailable(
4748
return !!(await getPlatformAuthProvider(
4849
logger,
4950
performanceClient,
50-
correlationId || createNewGuid()
51+
correlationId || createNewGuid(),
52+
undefined,
53+
domConfig
5154
));
5255
}
5356

5457
export async function getPlatformAuthProvider(
5558
logger: Logger,
5659
performanceClient: IPerformanceClient,
5760
correlationId: string,
58-
nativeBrokerHandshakeTimeout?: number
61+
nativeBrokerHandshakeTimeout?: number,
62+
enablePlatformBrokerDOMSupport?: boolean
5963
): Promise<IPlatformAuthHandler | undefined> {
6064
logger.trace("getPlatformAuthProvider called", correlationId);
6165

62-
const enablePlatformBrokerDOMSupport = isDomEnabledForPlatformAuth();
63-
6466
logger.trace(
6567
"Has client allowed platform auth via DOM API: " +
6668
enablePlatformBrokerDOMSupport
6769
);
70+
6871
let platformAuthProvider: IPlatformAuthHandler | undefined;
6972
try {
7073
if (enablePlatformBrokerDOMSupport) {
@@ -97,22 +100,6 @@ export async function getPlatformAuthProvider(
97100
return platformAuthProvider;
98101
}
99102

100-
/**
101-
* Returns true if the DOM API support for platform auth is enabled in session storage
102-
* @returns boolean
103-
* @deprecated
104-
*/
105-
export function isDomEnabledForPlatformAuth(): boolean {
106-
let sessionStorage: Storage | undefined;
107-
try {
108-
sessionStorage = window[BrowserCacheLocation.SessionStorage];
109-
// Mute errors if it's a non-browser environment or cookies are blocked.
110-
return sessionStorage?.getItem(PLATFORM_AUTH_DOM_SUPPORT) === "true";
111-
} catch (e) {
112-
return false;
113-
}
114-
}
115-
116103
/**
117104
* Returns boolean indicating whether or not the request should attempt to use native broker
118105
* @param logger
@@ -127,6 +114,17 @@ export function isPlatformAuthAllowed(
127114
authenticationScheme?: AuthenticationScheme
128115
): boolean {
129116
logger.trace("isPlatformAuthAllowed called");
117+
118+
// throw an error if allowPlatformBroker is not enabled and allowPlatformBrokerWithDOM is enabled
119+
if (
120+
!config.system.allowPlatformBroker &&
121+
config.system.allowPlatformBrokerWithDOM
122+
) {
123+
throw createClientConfigurationError(
124+
ClientConfigurationErrorCodes.invalidPlatformBrokerConfiguration
125+
);
126+
}
127+
130128
if (!config.system.allowPlatformBroker) {
131129
logger.trace(
132130
"isPlatformAuthAllowed: allowPlatformBroker is not enabled, returning false"

lib/msal-browser/src/config/Configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ export type BrowserSystemOptions = SystemOptions & {
209209
* Flag to enable native broker support (e.g. acquiring tokens from WAM on Windows, MacBroker on Mac)
210210
*/
211211
allowPlatformBroker?: boolean;
212+
/**
213+
* Flag to enable native broker support through DOM APIs in Edge
214+
*/
215+
allowPlatformBrokerWithDOM?: boolean;
212216
/**
213217
* Sets the timeout for waiting for the native broker handshake to resolve
214218
*/
@@ -357,6 +361,7 @@ export function buildConfiguration(
357361
asyncPopups: false,
358362
allowRedirectInIframe: false,
359363
allowPlatformBroker: false,
364+
allowPlatformBrokerWithDOM: false,
360365
nativeBrokerHandshakeTimeout:
361366
userInputSystem?.nativeBrokerHandshakeTimeout ||
362367
DEFAULT_NATIVE_BROKER_HANDSHAKE_TIMEOUT_MS,

lib/msal-browser/src/controllers/StandardController.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ export class StandardController implements IController {
356356
this.logger,
357357
this.performanceClient,
358358
initCorrelationId,
359-
this.config.system.nativeBrokerHandshakeTimeout
359+
this.config.system.nativeBrokerHandshakeTimeout,
360+
this.config.system.allowPlatformBrokerWithDOM
360361
);
361362
} catch (e) {
362363
this.logger.verbose(e as string);

lib/msal-browser/test/app/PublicClientApplication.spec.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -496,13 +496,6 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
496496
},
497497
};
498498

499-
jest.spyOn(
500-
PlatformAuthProvider,
501-
"isDomEnabledForPlatformAuth"
502-
).mockImplementation(() => {
503-
return false;
504-
});
505-
506499
const getPlatformAuthProviderSpy = jest.spyOn(
507500
PlatformAuthProvider,
508501
"getPlatformAuthProvider"
@@ -555,7 +548,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
555548
expect(pca.platformAuthProvider).toBeUndefined();
556549
});
557550

558-
it("creates platform auth dom handler if allowPlatformBroker is true and dom APIs are present", async () => {
551+
it("creates platform auth dom handler if allowPlatformBrokerWithDOM is true and dom APIs are present", async () => {
559552
const getPlatformAuthProviderSpy = jest.spyOn(
560553
PlatformAuthProvider,
561554
"getPlatformAuthProvider"
@@ -567,14 +560,11 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
567560
},
568561
system: {
569562
allowPlatformBroker: true,
563+
allowPlatformBrokerWithDOM: true,
570564
},
571565
};
572566

573567
pca = new PublicClientApplication(config);
574-
window.sessionStorage.setItem(
575-
"msal.browser.platform.auth.dom",
576-
"true"
577-
);
578568

579569
const createDOMProviderSpy = stubDOMProvider(config);
580570
await pca.initialize();

0 commit comments

Comments
 (0)