1
1
import { nestedAppAuth } from '@microsoft/teams-js' ;
2
- import React , { ReactElement } from 'react' ;
2
+ import React , { ReactElement , useState } from 'react' ;
3
3
4
- import { ApiWithoutInput } from './utils' ;
4
+ import { ApiWithoutInput , ApiWithTextInput } from './utils' ;
5
5
import { ModuleWrapper } from './utils/ModuleWrapper' ;
6
6
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
+
7
67
const NestedAppAuthAPIs = ( ) : ReactElement => {
8
68
const CheckIsNAAChannelRecommended = ( ) : ReactElement =>
9
69
ApiWithoutInput ( {
@@ -12,9 +72,144 @@ const NestedAppAuthAPIs = (): ReactElement => {
12
72
onClick : async ( ) => `NAA channel ${ nestedAppAuth . isNAAChannelRecommended ( ) ? 'is' : 'is not' } recommended` ,
13
73
} ) ;
14
74
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
+
15
207
return (
16
208
< ModuleWrapper title = "NestedAppAuth" >
17
209
< CheckIsNAAChannelRecommended />
210
+ < SendMessageToNestedAppAuthBridge />
211
+ < SendMessageToTopWindow />
212
+ < AddChildIframeSection />
18
213
</ ModuleWrapper >
19
214
) ;
20
215
} ;
0 commit comments