Skip to content

Commit 13d62af

Browse files
authored
Add Nested App Auth API sections to the teams test app (#2641)
* Add Nested App Auth API sections to the Teams test app * Update NestedAppAuthAPIs.tsx * Add input validator * Update the casing of the variables
1 parent d787b47 commit 13d62af

File tree

1 file changed

+197
-2
lines changed

1 file changed

+197
-2
lines changed

apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx

Lines changed: 197 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,69 @@
11
import { nestedAppAuth } from '@microsoft/teams-js';
2-
import React, { ReactElement } from 'react';
2+
import React, { ReactElement, useState } from 'react';
33

4-
import { ApiWithoutInput } from './utils';
4+
import { ApiWithoutInput, ApiWithTextInput } from './utils';
55
import { ModuleWrapper } from './utils/ModuleWrapper';
66

7+
const NestedAppAuthRequest = JSON.stringify({
8+
messageType: 'NestedAppAuthRequest',
9+
method: 'GetToken',
10+
sendTime: 1732269811006,
11+
clientLibrary: 'testOS',
12+
clientLibraryVersion: '1.0.0',
13+
requestId: '684352c2-7ab7-4def-b7e1-XXXXXXXXXXX',
14+
tokenParams: {
15+
correlationId: '39dc85fe-9054-11ed-a1eb-XXXXXXXXXXX',
16+
},
17+
});
18+
19+
const validateNAARequestInput = (input): void => {
20+
if (!input) {
21+
throw new Error('Input is required.');
22+
}
23+
24+
if (input.messageType !== 'NestedAppAuthRequest') {
25+
throw new Error('Invalid or missing messageType. Expected "NestedAppAuthRequest".');
26+
}
27+
28+
if (!input.method) {
29+
throw new Error('Method name is required in payload');
30+
}
31+
32+
if (!input.requestId) {
33+
throw new Error('RequestId is required in payload');
34+
}
35+
};
36+
37+
const validateTopWindowNAARequestInput = (input): void => {
38+
if (!input) {
39+
throw new Error('Input is required.');
40+
}
41+
42+
if (!input.id) {
43+
throw new Error('"id" is required.');
44+
}
45+
46+
if (!input.func) {
47+
throw new Error('"func" is required.');
48+
}
49+
50+
if (!input.data) {
51+
throw new Error('"data" is required with NAA payload');
52+
}
53+
54+
try {
55+
validateNAARequestInput(JSON.parse(input.data));
56+
} catch (error) {
57+
throw new Error('NAA payload must be a valid JSON');
58+
}
59+
};
60+
61+
type NestedAppAuthBridge = {
62+
postMessage: (message: string) => void;
63+
addEventListener: (type: string, listener: (response: unknown) => void) => void;
64+
removeEventListener: (type: string, listener: (response: unknown) => void) => void;
65+
};
66+
767
const NestedAppAuthAPIs = (): ReactElement => {
868
const CheckIsNAAChannelRecommended = (): ReactElement =>
969
ApiWithoutInput({
@@ -12,9 +72,144 @@ const NestedAppAuthAPIs = (): ReactElement => {
1272
onClick: async () => `NAA channel ${nestedAppAuth.isNAAChannelRecommended() ? 'is' : 'is not'} recommended`,
1373
});
1474

75+
const SendMessageToNestedAppAuthBridge = (): React.ReactElement =>
76+
ApiWithTextInput({
77+
name: 'sendMessageToNestedAppAuthBridge',
78+
title: 'Send NAA Message to NestedAppAuth Bridge',
79+
onClick: {
80+
validateInput: validateNAARequestInput,
81+
submit: async (input, setResult) => {
82+
const bridge = (window as Window & { nestedAppAuthBridge?: NestedAppAuthBridge }).nestedAppAuthBridge;
83+
if (!bridge) {
84+
setResult('Bridge not available');
85+
return 'Bridge not available';
86+
}
87+
88+
// Define listener for responses
89+
const listener = (response: unknown): void => {
90+
setResult(JSON.stringify(response, null, 2));
91+
bridge.removeEventListener?.('message', listener);
92+
};
93+
94+
// Add event listener
95+
bridge.addEventListener?.('message', listener);
96+
bridge.postMessage?.(JSON.stringify(input));
97+
98+
setResult('Message sent successfully, awaiting response...');
99+
return 'Message sent successfully';
100+
},
101+
},
102+
defaultInput: NestedAppAuthRequest,
103+
});
104+
105+
const SendMessageToTopWindow = (): React.ReactElement =>
106+
ApiWithTextInput({
107+
name: 'sendMessageToTopWindow',
108+
title: 'Send NAA Message to Top Window',
109+
onClick: {
110+
validateInput: validateTopWindowNAARequestInput,
111+
submit: async (input, setResult) => {
112+
try {
113+
const targetOrigin = 'https://local.teams.office.com:8080';
114+
115+
// Check if window.top is accessible
116+
if (!window.top) {
117+
setResult('Top window not accessible');
118+
return 'Top window not accessible';
119+
}
120+
121+
// Define listener for responses
122+
const listener = (event: MessageEvent): void => {
123+
// Ensure the message comes from the expected origin
124+
if (event.origin !== targetOrigin) {
125+
console.warn('Received message from an unexpected origin:', event.origin);
126+
return;
127+
}
128+
129+
console.log('Received response from top window:', event.data);
130+
setResult(JSON.stringify(event.data, null, 2)); // Pretty-print response
131+
window.removeEventListener('message', listener);
132+
};
133+
134+
// Add event listener for messages
135+
window.addEventListener('message', listener);
136+
window.top.postMessage(input, targetOrigin);
137+
138+
setResult('Message sent to top window, awaiting response...');
139+
return 'Message sent to top window';
140+
} catch (error) {
141+
console.error('Error sending message to top window:', error);
142+
setResult(`Error: ${error}`);
143+
return `Error: ${error}`;
144+
}
145+
},
146+
},
147+
defaultInput: JSON.stringify({
148+
id: '2',
149+
func: 'nestedAppAuth.execute',
150+
args: [],
151+
data: NestedAppAuthRequest,
152+
}),
153+
});
154+
155+
const AddChildIframeSection = (): React.ReactElement | null => {
156+
const [iframeAdded, setIframeAdded] = useState(false);
157+
158+
const addChildIframe = (): void => {
159+
if (iframeAdded) {
160+
console.log('Iframe already added.');
161+
return;
162+
}
163+
164+
const iframeContainer = document.getElementById('nestedChildIframeContainer');
165+
if (!iframeContainer) {
166+
console.error('Container not found: nestedChildIframeContainer');
167+
return;
168+
}
169+
170+
const childIframe = document.createElement('iframe');
171+
childIframe.src = `${window.location.href}?appInitializationTest=true&groupedMode=NestedAppAuthAPIs`;
172+
childIframe.id = 'nestedAuthChildIframe';
173+
childIframe.width = '100%';
174+
childIframe.height = '400px';
175+
childIframe.style.border = 'none';
176+
177+
iframeContainer.appendChild(childIframe);
178+
setIframeAdded(true);
179+
};
180+
181+
return (
182+
<div style={{ border: '5px solid black', padding: '2px', margin: '2px' }}>
183+
<h2>Add Nested Child Iframe</h2>
184+
<input
185+
name="button_addNestedChildIframe"
186+
type="button"
187+
value="Add Child Iframe"
188+
onClick={addChildIframe}
189+
disabled={iframeAdded}
190+
/>
191+
<div
192+
id="nestedChildIframeContainer"
193+
style={{
194+
marginTop: '2px',
195+
height: '400px',
196+
border: '2px solid red',
197+
overflow: 'hidden',
198+
display: 'flex',
199+
justifyContent: 'center',
200+
alignItems: 'center',
201+
}}
202+
></div>
203+
</div>
204+
);
205+
};
206+
15207
return (
16208
<ModuleWrapper title="NestedAppAuth">
17209
<CheckIsNAAChannelRecommended />
210+
<SendMessageToNestedAppAuthBridge />
211+
<SendMessageToTopWindow />
212+
<AddChildIframeSection />
18213
</ModuleWrapper>
19214
);
20215
};

0 commit comments

Comments
 (0)