Skip to content

Commit 93994b0

Browse files
author
Prithvi Kanherkar
authored
Merge pull request #2190 from AzureAD/broker-request-popup
Broker #3: Popup requests from embedded application
2 parents 6f7129c + d0309fc commit 93994b0

17 files changed

+408
-133
lines changed

lib/msal-browser/src/app/PublicClientApplication.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import { DEFAULT_REQUEST } from "../utils/BrowserConstants";
88
import { IPublicClientApplication } from "./IPublicClientApplication";
99
import { RedirectRequest } from "../request/RedirectRequest";
1010
import { PopupRequest } from "../request/PopupRequest";
11-
import { BrokerManager } from "../broker/BrokerManager";
12-
import { BrokerClient } from "../broker/BrokerClient";
1311
import { ClientApplication } from "./ClientApplication";
14-
import { version } from "../../package.json";
12+
import { BrokerClientApplication } from "../broker/BrokerClientApplication";
13+
import { EmbeddedClientApplication } from "../broker/EmbeddedClientApplication";
1514

1615
/**
1716
* The PublicClientApplication class is the object exposed by the library to perform authentication and authorization functions in Single Page Applications
@@ -20,8 +19,8 @@ import { version } from "../../package.json";
2019
export class PublicClientApplication extends ClientApplication implements IPublicClientApplication {
2120

2221
// Broker Objects
23-
private embeddedApp: BrokerClient;
24-
private broker: BrokerManager;
22+
protected embeddedApp: EmbeddedClientApplication;
23+
protected broker: BrokerClientApplication;
2524

2625
/**
2726
* @constructor
@@ -49,15 +48,20 @@ export class PublicClientApplication extends ClientApplication implements IPubli
4948
this.initializeBrokering();
5049
}
5150

51+
/**
52+
*
53+
*/
5254
private initializeBrokering(): void {
5355
if (this.config.system.brokerOptions.actAsBroker) {
54-
this.broker = new BrokerManager(this.config.system.brokerOptions, this.logger, version);
55-
this.logger.verbose("Acting as Broker");
56-
this.broker.listenForMessage();
56+
this.broker = new BrokerClientApplication(this.config);
5757
} else if (this.config.system.brokerOptions.allowBrokering) {
58-
this.embeddedApp = new BrokerClient(this.config.system.brokerOptions, this.logger, this.config.auth.clientId, version);
58+
this.embeddedApp = new EmbeddedClientApplication(this.config, this.logger, this.browserStorage);
5959
this.logger.verbose("Acting as child");
60-
this.embeddedApp.initiateHandshake();
60+
try {
61+
this.embeddedApp.initiateHandshake();
62+
} catch (e) {
63+
this.logger.error(`Broker handshake failed: ${e}`);
64+
}
6165
}
6266
}
6367

@@ -85,7 +89,7 @@ export class PublicClientApplication extends ClientApplication implements IPubli
8589
*/
8690
async acquireTokenRedirect(request: RedirectRequest): Promise<void> {
8791
// Check for brokered request
88-
if (this.embeddedApp && this.embeddedApp.brokeringEnabled) {
92+
if (this.embeddedApp && this.embeddedApp.brokerConnectionEstablished) {
8993
return this.embeddedApp.sendRedirectRequest(request);
9094
}
9195
return super.acquireTokenRedirect(request);
@@ -113,7 +117,7 @@ export class PublicClientApplication extends ClientApplication implements IPubli
113117
* @returns {Promise.<AuthenticationResult>} - a promise that is fulfilled when this function has completed, or rejected if an error was raised. Returns the {@link AuthResponse} object
114118
*/
115119
acquireTokenPopup(request: PopupRequest): Promise<AuthenticationResult> {
116-
if (this.embeddedApp && this.embeddedApp.brokeringEnabled) {
120+
if (this.embeddedApp && this.embeddedApp.brokerConnectionEstablished) {
117121
return this.embeddedApp.sendPopupRequest(request);
118122
}
119123
return super.acquireTokenPopup(request);

lib/msal-browser/src/broker/BrokerAuthRequest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class BrokerAuthRequest extends BrokerMessage {
99
public request: RedirectRequest | PopupRequest;
1010

1111
constructor(embeddedClientId: string, interactionType: InteractionType, request: RedirectRequest | PopupRequest) {
12-
super("BrokerAuthRequest");
12+
super(BrokerMessageType.AUTH_REQUEST);
1313
this.embeddedClientId = embeddedClientId;
1414
this.interactionType = interactionType;
1515
this.request = request;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { BrokerMessage } from "./BrokerMessage";
2+
import { BrokerAuthenticationResult } from "@azure/msal-common";
3+
import { InteractionType, BrokerMessageType } from "../utils/BrowserConstants";
4+
5+
export class BrokerAuthResult extends BrokerMessage {
6+
public interactionType: InteractionType;
7+
public result: BrokerAuthenticationResult;
8+
public error: Error;
9+
10+
constructor(interactionType: InteractionType, authResult: BrokerAuthenticationResult, authError?: Error) {
11+
super(BrokerMessageType.AUTH_RESULT);
12+
this.interactionType = interactionType;
13+
this.result = authResult;
14+
this.error = authError;
15+
}
16+
17+
static validate(message: MessageEvent): BrokerAuthResult | null {
18+
if (message.data &&
19+
message.data.messageType === BrokerMessageType.AUTH_RESULT &&
20+
message.data.interactionType &&
21+
(message.data.result || message.data.error)) {
22+
23+
// TODO: verify version compat
24+
25+
return new BrokerAuthResult(message.data.interactionType, message.data.result);
26+
}
27+
28+
return null;
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
import { version } from "../../package.json";
6+
import { BrokerAuthenticationResult, ServerTelemetryManager, AuthorizationCodeClient, BrokerAuthorizationCodeClient } from "@azure/msal-common";
7+
import { BrokerMessage } from "./BrokerMessage";
8+
import { BrokerMessageType, InteractionType } from "../utils/BrowserConstants";
9+
import { Configuration } from "../config/Configuration";
10+
import { BrokerHandshakeRequest } from "./BrokerHandshakeRequest";
11+
import { BrokerHandshakeResponse } from "./BrokerHandshakeResponse";
12+
import { BrokerAuthRequest } from "./BrokerAuthRequest";
13+
import { BrokerRedirectResponse } from "./BrokerRedirectResponse";
14+
import { RedirectRequest } from "../request/RedirectRequest";
15+
import { BrokerAuthResult } from "./BrokerAuthResult";
16+
import { ClientApplication } from "../app/ClientApplication";
17+
import { PopupRequest } from "../request/PopupRequest";
18+
19+
/**
20+
* Broker Application class to manage brokered requests.
21+
*/
22+
export class BrokerClientApplication extends ClientApplication {
23+
24+
constructor(configuration: Configuration) {
25+
super(configuration);
26+
this.logger.verbose("Acting as Broker");
27+
this.listenForBrokerMessage();
28+
}
29+
30+
/**
31+
*
32+
*/
33+
private listenForBrokerMessage(): void {
34+
window.addEventListener("message", this.handleBrokerMessage.bind(this));
35+
}
36+
37+
/**
38+
*
39+
* @param message
40+
*/
41+
private async handleBrokerMessage(message: MessageEvent): Promise<void> {
42+
// Check that message is a BrokerHandshakeRequest
43+
const clientMessage = BrokerMessage.validateMessage(message);
44+
if (clientMessage) {
45+
switch (clientMessage.data.messageType) {
46+
case BrokerMessageType.HANDSHAKE_REQUEST:
47+
this.logger.verbose("Broker handshake request received");
48+
return await this.handleBrokerHandshake(clientMessage);
49+
case BrokerMessageType.AUTH_REQUEST:
50+
this.logger.verbose("Broker auth request received");
51+
return await this.handleBrokerAuthRequest(clientMessage);
52+
default:
53+
return;
54+
}
55+
}
56+
}
57+
58+
/* eslint-disable */
59+
/**
60+
* Handle a broker handshake request from a child.
61+
* @param clientMessage
62+
*/
63+
private async handleBrokerHandshake(clientMessage: MessageEvent): Promise<void> {
64+
const validMessage = BrokerHandshakeRequest.validate(clientMessage);
65+
this.logger.verbose(`Broker handshake validated: ${validMessage}`);
66+
const brokerHandshakeResponse = new BrokerHandshakeResponse(version);
67+
68+
// @ts-ignore
69+
clientMessage.source.postMessage(brokerHandshakeResponse, clientMessage.origin);
70+
this.logger.info(`Sending handshake response: ${brokerHandshakeResponse}`);
71+
}
72+
73+
/**
74+
* Handle a brokered auth request from the child.
75+
* @param clientMessage
76+
*/
77+
private async handleBrokerAuthRequest(clientMessage: MessageEvent): Promise<void> {
78+
const validMessage = BrokerAuthRequest.validate(clientMessage);
79+
if (validMessage) {
80+
this.logger.verbose(`Broker auth request validated: ${validMessage}`);
81+
switch (validMessage.interactionType) {
82+
case InteractionType.REDIRECT:
83+
return this.brokeredRedirectRequest(validMessage, clientMessage.ports[0]);
84+
case InteractionType.POPUP:
85+
return this.brokeredPopupRequest(validMessage, clientMessage.ports[0]);
86+
default:
87+
return;
88+
}
89+
}
90+
}
91+
92+
private async brokeredRedirectRequest(validMessage: BrokerAuthRequest, clientPort: MessagePort): Promise<void> {
93+
const brokerRedirectResp = new BrokerRedirectResponse();
94+
// @ts-ignore
95+
clientPort.postMessage(brokerRedirectResp);
96+
this.logger.info(`Sending redirect response: ${brokerRedirectResp}`);
97+
98+
// Call loginRedirect
99+
this.acquireTokenRedirect(validMessage.request as RedirectRequest);
100+
}
101+
102+
private async brokeredPopupRequest(validMessage: BrokerAuthRequest, clientPort: MessagePort): Promise<void> {
103+
try {
104+
const response: BrokerAuthenticationResult = (await this.acquireTokenPopup(validMessage.request as PopupRequest)) as BrokerAuthenticationResult;
105+
const brokerAuthResponse: BrokerAuthResult = new BrokerAuthResult(InteractionType.POPUP, response);
106+
this.logger.info(`Sending auth response: ${brokerAuthResponse}`);
107+
clientPort.postMessage(brokerAuthResponse);
108+
} catch (err) {
109+
const brokerAuthResponse = new BrokerAuthResult(InteractionType.POPUP, null, err);
110+
this.logger.info(`Found auth error: ${err}`);
111+
clientPort.postMessage(brokerAuthResponse);
112+
}
113+
}
114+
115+
/**
116+
* Creates an Broker Authorization Code Client with the given authority, or the default authority.
117+
* @param authorityUrl
118+
*/
119+
protected async createAuthCodeClient(serverTelemetryManager: ServerTelemetryManager, authorityUrl?: string): Promise<AuthorizationCodeClient> {
120+
// Create auth module.
121+
const clientConfig = await this.getClientConfiguration(serverTelemetryManager, authorityUrl);
122+
123+
return new BrokerAuthorizationCodeClient(clientConfig);
124+
}
125+
}

lib/msal-browser/src/broker/BrokerManager.ts

-77
This file was deleted.

lib/msal-browser/src/broker/BrokerMessage.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ export abstract class BrokerMessage {
1616

1717
static validateMessage(message: MessageEvent): MessageEvent|null {
1818
if (message.data && message.data.messageType) {
19-
if (message.data.messageType in BrokerMessageType) {
20-
return message;
21-
} else {
22-
throw(BrowserAuthError.createInvalidBrokerMessageError());
19+
switch(message.data.messageType) {
20+
case BrokerMessageType.HANDSHAKE_REQUEST:
21+
case BrokerMessageType.HANDSHAKE_RESPONSE:
22+
case BrokerMessageType.AUTH_REQUEST:
23+
case BrokerMessageType.AUTH_RESULT:
24+
case BrokerMessageType.REDIRECT_RESPONSE:
25+
return message;
26+
default:
27+
return null;
2328
}
2429
} else {
2530
return null;

0 commit comments

Comments
 (0)