@microsoft/teamsfx-core 3.0.4 → 3.0.5-alpha.1d492cee1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/build/client/graphClient.d.ts +8 -4
  2. package/build/client/graphClient.d.ts.map +1 -1
  3. package/build/client/graphClient.js +35 -8
  4. package/build/client/graphClient.js.map +1 -1
  5. package/build/client/interfaces/GetGroupResponse.d.ts +6 -0
  6. package/build/client/interfaces/GetGroupResponse.d.ts.map +1 -0
  7. package/build/client/interfaces/GetGroupResponse.js +5 -0
  8. package/build/client/interfaces/GetGroupResponse.js.map +1 -0
  9. package/build/client/interfaces/GetUserResponse.d.ts +7 -0
  10. package/build/client/interfaces/GetUserResponse.d.ts.map +1 -0
  11. package/build/client/interfaces/GetUserResponse.js +5 -0
  12. package/build/client/interfaces/GetUserResponse.js.map +1 -0
  13. package/build/client/teamsDevPortalClient.js +1 -1
  14. package/build/client/teamsDevPortalClient.js.map +1 -1
  15. package/build/common/constants.d.ts +2 -0
  16. package/build/common/constants.d.ts.map +1 -1
  17. package/build/common/constants.js +3 -1
  18. package/build/common/constants.js.map +1 -1
  19. package/build/common/daSpecParser.js +1 -1
  20. package/build/common/daSpecParser.js.map +1 -1
  21. package/build/common/featureFlags.js +4 -4
  22. package/build/common/featureFlags.js.map +1 -1
  23. package/build/common/kiotaClient.d.ts.map +1 -1
  24. package/build/common/kiotaClient.js +17 -7
  25. package/build/common/kiotaClient.js.map +1 -1
  26. package/build/common/permissionInterface.d.ts +6 -0
  27. package/build/common/permissionInterface.d.ts.map +1 -1
  28. package/build/common/projectTypeChecker.d.ts +1 -0
  29. package/build/common/projectTypeChecker.d.ts.map +1 -1
  30. package/build/common/projectTypeChecker.js +14 -1
  31. package/build/common/projectTypeChecker.js.map +1 -1
  32. package/build/common/stringUtils.d.ts.map +1 -1
  33. package/build/common/stringUtils.js +2 -1
  34. package/build/common/stringUtils.js.map +1 -1
  35. package/build/common/templates-config.json +8 -6
  36. package/build/common/tools.d.ts +4 -0
  37. package/build/common/tools.d.ts.map +1 -1
  38. package/build/common/tools.js +58 -1
  39. package/build/common/tools.js.map +1 -1
  40. package/build/common/wrappedAxiosClient.d.ts.map +1 -1
  41. package/build/common/wrappedAxiosClient.js +0 -1
  42. package/build/common/wrappedAxiosClient.js.map +1 -1
  43. package/build/component/configManager/validator.d.ts.map +1 -1
  44. package/build/component/configManager/validator.js +2 -0
  45. package/build/component/configManager/validator.js.map +1 -1
  46. package/build/component/coordinator/index.d.ts +0 -1
  47. package/build/component/coordinator/index.d.ts.map +1 -1
  48. package/build/component/coordinator/index.js +54 -58
  49. package/build/component/coordinator/index.js.map +1 -1
  50. package/build/component/deps-checker/constant/helpLink.d.ts +2 -0
  51. package/build/component/deps-checker/constant/helpLink.d.ts.map +1 -1
  52. package/build/component/deps-checker/constant/helpLink.js +3 -1
  53. package/build/component/deps-checker/constant/helpLink.js.map +1 -1
  54. package/build/component/deps-checker/constant/message.d.ts +44 -11
  55. package/build/component/deps-checker/constant/message.d.ts.map +1 -1
  56. package/build/component/deps-checker/constant/message.js +48 -13
  57. package/build/component/deps-checker/constant/message.js.map +1 -1
  58. package/build/component/deps-checker/internal/dotnetChecker.d.ts.map +1 -1
  59. package/build/component/deps-checker/internal/dotnetChecker.js +4 -1
  60. package/build/component/deps-checker/internal/dotnetChecker.js.map +1 -1
  61. package/build/component/deps-checker/internal/funcToolChecker.d.ts.map +1 -1
  62. package/build/component/deps-checker/internal/funcToolChecker.js +8 -5
  63. package/build/component/deps-checker/internal/funcToolChecker.js.map +1 -1
  64. package/build/component/deps-checker/internal/testToolChecker.d.ts.map +1 -1
  65. package/build/component/deps-checker/internal/testToolChecker.js +16 -10
  66. package/build/component/deps-checker/internal/testToolChecker.js.map +1 -1
  67. package/build/component/driver/deploy/azure/azureStaticWebAppGetDeploymentTokenDriver.d.ts.map +1 -1
  68. package/build/component/driver/deploy/azure/azureStaticWebAppGetDeploymentTokenDriver.js +10 -12
  69. package/build/component/driver/deploy/azure/azureStaticWebAppGetDeploymentTokenDriver.js.map +1 -1
  70. package/build/component/driver/index.d.ts +19 -20
  71. package/build/component/driver/index.d.ts.map +1 -1
  72. package/build/component/driver/index.js +19 -20
  73. package/build/component/driver/index.js.map +1 -1
  74. package/build/component/driver/m365/acquire.d.ts +3 -0
  75. package/build/component/driver/m365/acquire.d.ts.map +1 -1
  76. package/build/component/driver/m365/acquire.js +29 -19
  77. package/build/component/driver/m365/acquire.js.map +1 -1
  78. package/build/component/driver/share/utils.d.ts +5 -1
  79. package/build/component/driver/share/utils.d.ts.map +1 -1
  80. package/build/component/driver/share/utils.js +23 -22
  81. package/build/component/driver/share/utils.js.map +1 -1
  82. package/build/component/driver/teamsApp/createAppPackage.d.ts.map +1 -1
  83. package/build/component/driver/teamsApp/createAppPackage.js +81 -80
  84. package/build/component/driver/teamsApp/createAppPackage.js.map +1 -1
  85. package/build/component/driver/teamsApp/teamsappMgr.d.ts.map +1 -1
  86. package/build/component/driver/teamsApp/teamsappMgr.js +3 -0
  87. package/build/component/driver/teamsApp/teamsappMgr.js.map +1 -1
  88. package/build/component/driver/teamsApp/utils/CopilotGptManifestUtils.d.ts +3 -3
  89. package/build/component/driver/teamsApp/utils/CopilotGptManifestUtils.d.ts.map +1 -1
  90. package/build/component/driver/teamsApp/utils/CopilotGptManifestUtils.js +8 -13
  91. package/build/component/driver/teamsApp/utils/CopilotGptManifestUtils.js.map +1 -1
  92. package/build/component/driver/teamsApp/utils/ManifestUtils.d.ts +2 -2
  93. package/build/component/driver/teamsApp/utils/ManifestUtils.d.ts.map +1 -1
  94. package/build/component/driver/teamsApp/utils/ManifestUtils.js +10 -5
  95. package/build/component/driver/teamsApp/utils/ManifestUtils.js.map +1 -1
  96. package/build/component/driver/teamsApp/utils/PluginManifestUtils.d.ts +1 -1
  97. package/build/component/driver/teamsApp/utils/PluginManifestUtils.d.ts.map +1 -1
  98. package/build/component/driver/teamsApp/utils/PluginManifestUtils.js +5 -2
  99. package/build/component/driver/teamsApp/utils/PluginManifestUtils.js.map +1 -1
  100. package/build/component/driver/teamsApp/validate.d.ts +3 -3
  101. package/build/component/driver/teamsApp/validate.d.ts.map +1 -1
  102. package/build/component/driver/teamsApp/validate.js +67 -86
  103. package/build/component/driver/teamsApp/validate.js.map +1 -1
  104. package/build/component/driver/util/utils.d.ts.map +1 -1
  105. package/build/component/driver/util/utils.js +6 -4
  106. package/build/component/driver/util/utils.js.map +1 -1
  107. package/build/component/feature/collaboration.d.ts +7 -1
  108. package/build/component/feature/collaboration.d.ts.map +1 -1
  109. package/build/component/feature/collaboration.js +84 -1
  110. package/build/component/feature/collaboration.js.map +1 -1
  111. package/build/component/generator/combinedProject/generator.d.ts.map +1 -1
  112. package/build/component/generator/combinedProject/generator.js +2 -1
  113. package/build/component/generator/combinedProject/generator.js.map +1 -1
  114. package/build/component/generator/declarativeAgent/generator.d.ts.map +1 -1
  115. package/build/component/generator/declarativeAgent/generator.js +2 -1
  116. package/build/component/generator/declarativeAgent/generator.js.map +1 -1
  117. package/build/component/generator/defaultGenerator.d.ts.map +1 -1
  118. package/build/component/generator/defaultGenerator.js +6 -5
  119. package/build/component/generator/defaultGenerator.js.map +1 -1
  120. package/build/component/generator/generator.d.ts +1 -1
  121. package/build/component/generator/generator.d.ts.map +1 -1
  122. package/build/component/generator/generator.js +4 -3
  123. package/build/component/generator/generator.js.map +1 -1
  124. package/build/component/generator/generatorAction.d.ts +2 -1
  125. package/build/component/generator/generatorAction.d.ts.map +1 -1
  126. package/build/component/generator/generatorAction.js +1 -1
  127. package/build/component/generator/generatorAction.js.map +1 -1
  128. package/build/component/generator/officeAddin/generator.js.map +1 -1
  129. package/build/component/generator/officeAddin/metaOSHelper.d.ts.map +1 -1
  130. package/build/component/generator/officeAddin/metaOSHelper.js +44 -5
  131. package/build/component/generator/officeAddin/metaOSHelper.js.map +1 -1
  132. package/build/component/generator/openApiSpec/common.d.ts.map +1 -1
  133. package/build/component/generator/openApiSpec/common.js +2 -1
  134. package/build/component/generator/openApiSpec/common.js.map +1 -1
  135. package/build/component/generator/openApiSpec/helper.d.ts.map +1 -1
  136. package/build/component/generator/openApiSpec/helper.js +132 -98
  137. package/build/component/generator/openApiSpec/helper.js.map +1 -1
  138. package/build/component/generator/other/ssrTabGenerator.d.ts.map +1 -1
  139. package/build/component/generator/other/ssrTabGenerator.js +2 -1
  140. package/build/component/generator/other/ssrTabGenerator.js.map +1 -1
  141. package/build/component/generator/other/tdpGenerator.d.ts.map +1 -1
  142. package/build/component/generator/other/tdpGenerator.js +2 -1
  143. package/build/component/generator/other/tdpGenerator.js.map +1 -1
  144. package/build/component/generator/spfx/spfxGenerator.d.ts +1 -1
  145. package/build/component/generator/spfx/spfxGenerator.d.ts.map +1 -1
  146. package/build/component/generator/spfx/spfxGenerator.js +12 -9
  147. package/build/component/generator/spfx/spfxGenerator.js.map +1 -1
  148. package/build/component/generator/spfx/utils/constants.d.ts +2 -0
  149. package/build/component/generator/spfx/utils/constants.d.ts.map +1 -1
  150. package/build/component/generator/spfx/utils/constants.js +2 -0
  151. package/build/component/generator/spfx/utils/constants.js.map +1 -1
  152. package/build/component/generator/templates/metadata/tab.js +4 -4
  153. package/build/component/generator/templates/metadata/tab.js.map +1 -1
  154. package/build/component/generator/templates/metadata/vs.d.ts.map +1 -1
  155. package/build/component/generator/templates/metadata/vs.js +0 -12
  156. package/build/component/generator/templates/metadata/vs.js.map +1 -1
  157. package/build/component/generator/templates/templateNames.d.ts +1 -3
  158. package/build/component/generator/templates/templateNames.d.ts.map +1 -1
  159. package/build/component/generator/templates/templateNames.js +1 -3
  160. package/build/component/generator/templates/templateNames.js.map +1 -1
  161. package/build/component/generator/templates/templateReplaceMap.d.ts.map +1 -1
  162. package/build/component/generator/templates/templateReplaceMap.js +8 -6
  163. package/build/component/generator/templates/templateReplaceMap.js.map +1 -1
  164. package/build/component/generator/utils.d.ts +3 -3
  165. package/build/component/generator/utils.d.ts.map +1 -1
  166. package/build/component/generator/utils.js +24 -7
  167. package/build/component/generator/utils.js.map +1 -1
  168. package/build/component/m365/constants.d.ts +1 -0
  169. package/build/component/m365/constants.d.ts.map +1 -1
  170. package/build/component/m365/constants.js +2 -1
  171. package/build/component/m365/constants.js.map +1 -1
  172. package/build/component/m365/interface.d.ts +6 -5
  173. package/build/component/m365/interface.d.ts.map +1 -1
  174. package/build/component/m365/interface.js +6 -5
  175. package/build/component/m365/interface.js.map +1 -1
  176. package/build/component/m365/packageService.d.ts +13 -3
  177. package/build/component/m365/packageService.d.ts.map +1 -1
  178. package/build/component/m365/packageService.js +175 -15
  179. package/build/component/m365/packageService.js.map +1 -1
  180. package/build/component/resource/botService/errors.d.ts +1 -1
  181. package/build/component/resource/botService/errors.d.ts.map +1 -1
  182. package/build/component/resource/botService/errors.js +2 -2
  183. package/build/component/resource/botService/errors.js.map +1 -1
  184. package/build/component/resource/botService/messages.d.ts +1 -1
  185. package/build/component/resource/botService/messages.d.ts.map +1 -1
  186. package/build/component/resource/botService/messages.js +6 -1
  187. package/build/component/resource/botService/messages.js.map +1 -1
  188. package/build/core/FxCore.d.ts +0 -3
  189. package/build/core/FxCore.d.ts.map +1 -1
  190. package/build/core/FxCore.js +40 -65
  191. package/build/core/FxCore.js.map +1 -1
  192. package/build/core/collaborator.d.ts +1 -0
  193. package/build/core/collaborator.d.ts.map +1 -1
  194. package/build/core/collaborator.js +55 -3
  195. package/build/core/collaborator.js.map +1 -1
  196. package/build/core/middleware/utils/debug/taskMigrator.js +1 -1
  197. package/build/core/share.d.ts +9 -0
  198. package/build/core/share.d.ts.map +1 -0
  199. package/build/core/share.js +118 -0
  200. package/build/core/share.js.map +1 -0
  201. package/build/error/depCheck.d.ts +4 -1
  202. package/build/error/depCheck.d.ts.map +1 -1
  203. package/build/error/depCheck.js +3 -3
  204. package/build/error/depCheck.js.map +1 -1
  205. package/build/index.d.ts +1 -0
  206. package/build/index.d.ts.map +1 -1
  207. package/build/index.js +3 -1
  208. package/build/index.js.map +1 -1
  209. package/build/question/collaborator.d.ts +6 -0
  210. package/build/question/collaborator.d.ts.map +1 -0
  211. package/build/question/collaborator.js +166 -0
  212. package/build/question/collaborator.js.map +1 -0
  213. package/build/question/constants.d.ts +1 -0
  214. package/build/question/constants.d.ts.map +1 -1
  215. package/build/question/constants.js +8 -19
  216. package/build/question/constants.js.map +1 -1
  217. package/build/question/generator.d.ts.map +1 -1
  218. package/build/question/generator.js +2 -0
  219. package/build/question/generator.js.map +1 -1
  220. package/build/question/index.d.ts.map +1 -1
  221. package/build/question/index.js +6 -4
  222. package/build/question/index.js.map +1 -1
  223. package/build/question/inputs/AddPluginInputs.d.ts +1 -5
  224. package/build/question/inputs/AddPluginInputs.d.ts.map +1 -1
  225. package/build/question/inputs/RegeneratePluginInputs.d.ts +16 -0
  226. package/build/question/inputs/RegeneratePluginInputs.d.ts.map +1 -0
  227. package/build/question/inputs/RegeneratePluginInputs.js +5 -0
  228. package/build/question/inputs/RegeneratePluginInputs.js.map +1 -0
  229. package/build/question/inputs/ShareInputs.d.ts +6 -4
  230. package/build/question/inputs/ShareInputs.d.ts.map +1 -1
  231. package/build/question/inputs/index.d.ts +12 -11
  232. package/build/question/inputs/index.d.ts.map +1 -1
  233. package/build/question/inputs/index.js +12 -11
  234. package/build/question/inputs/index.js.map +1 -1
  235. package/build/question/options/AddPluginOptions.d.ts.map +1 -1
  236. package/build/question/options/AddPluginOptions.js +1 -11
  237. package/build/question/options/AddPluginOptions.js.map +1 -1
  238. package/build/question/options/CreateProjectOptions.js +1 -1
  239. package/build/question/options/CreateProjectOptions.js.map +1 -1
  240. package/build/question/options/RegeneratePluginOptions.d.ts +10 -0
  241. package/build/question/options/RegeneratePluginOptions.d.ts.map +1 -0
  242. package/build/question/options/RegeneratePluginOptions.js +28 -0
  243. package/build/question/options/RegeneratePluginOptions.js.map +1 -0
  244. package/build/question/options/SPFxAddWebpartOptions.js +1 -1
  245. package/build/question/options/SPFxAddWebpartOptions.js.map +1 -1
  246. package/build/question/options/ShareOptions.d.ts.map +1 -1
  247. package/build/question/options/ShareOptions.js +12 -5
  248. package/build/question/options/ShareOptions.js.map +1 -1
  249. package/build/question/options/index.d.ts +1 -0
  250. package/build/question/options/index.d.ts.map +1 -1
  251. package/build/question/options/index.js +1 -0
  252. package/build/question/options/index.js.map +1 -1
  253. package/build/question/other.d.ts +2 -8
  254. package/build/question/other.d.ts.map +1 -1
  255. package/build/question/other.js +12 -279
  256. package/build/question/other.js.map +1 -1
  257. package/build/question/questionNames.d.ts +3 -4
  258. package/build/question/questionNames.d.ts.map +1 -1
  259. package/build/question/questionNames.js +3 -4
  260. package/build/question/questionNames.js.map +1 -1
  261. package/build/question/scaffold/vs/createRootNode.d.ts +0 -1
  262. package/build/question/scaffold/vs/createRootNode.d.ts.map +1 -1
  263. package/build/question/scaffold/vs/createRootNode.js +0 -11
  264. package/build/question/scaffold/vs/createRootNode.js.map +1 -1
  265. package/build/question/scaffold/vsc/CapabilityOptions.d.ts +0 -1
  266. package/build/question/scaffold/vsc/CapabilityOptions.d.ts.map +1 -1
  267. package/build/question/scaffold/vsc/CapabilityOptions.js +1 -9
  268. package/build/question/scaffold/vsc/CapabilityOptions.js.map +1 -1
  269. package/build/question/scaffold/vsc/daProjectTypeNode.d.ts.map +1 -1
  270. package/build/question/scaffold/vsc/daProjectTypeNode.js +0 -14
  271. package/build/question/scaffold/vsc/daProjectTypeNode.js.map +1 -1
  272. package/build/question/share.d.ts +22 -0
  273. package/build/question/share.d.ts.map +1 -0
  274. package/build/question/share.js +165 -0
  275. package/build/question/share.js.map +1 -0
  276. package/build/tsconfig.tsbuildinfo +1 -1
  277. package/package.json +5 -5
  278. package/resource/package.nls.cs.json +68 -57
  279. package/resource/package.nls.de.json +68 -57
  280. package/resource/package.nls.es.json +68 -57
  281. package/resource/package.nls.fr.json +68 -57
  282. package/resource/package.nls.it.json +68 -57
  283. package/resource/package.nls.ja.json +68 -57
  284. package/resource/package.nls.json +29 -23
  285. package/resource/package.nls.ko.json +68 -57
  286. package/resource/package.nls.pl.json +68 -57
  287. package/resource/package.nls.pt-BR.json +68 -57
  288. package/resource/package.nls.ru.json +68 -57
  289. package/resource/package.nls.tr.json +68 -57
  290. package/resource/package.nls.zh-Hans.json +68 -57
  291. package/resource/package.nls.zh-Hant.json +68 -57
  292. package/resource/yaml-schema/v1.10/yaml.schema.json +2058 -0
  293. package/resource/yaml-schema/v1.9/yaml.schema.json +2107 -0
  294. package/resource/yaml-schema/yaml.schema.json +6 -55
  295. package/templates/fallback/common.zip +0 -0
  296. package/templates/fallback/csharp.zip +0 -0
  297. package/templates/fallback/js.zip +0 -0
  298. package/templates/fallback/python.zip +0 -0
  299. package/templates/fallback/ts.zip +0 -0
  300. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/BotAuthenticationOptions.cs +44 -0
  301. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/IIdentityClientAdapter.cs +21 -0
  302. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/ITeamsInfo.cs +24 -0
  303. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/IdentityClientAdapter.cs +30 -0
  304. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/SsoDialog.cs +12 -9
  305. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/SsoOperations.cs +2 -2
  306. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/TeamsBotSsoPrompt.cs +423 -0
  307. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/TeamsBotSsoPromptSettings.cs +41 -0
  308. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/TeamsBotSsoPromptTokenResponse.cs +21 -0
  309. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/TeamsInfoWrapper.cs +20 -0
  310. package/templates/plugins/resource/aad/auth/V3/Bot/SSO/TeamsSsoBot.cs +6 -5
  311. package/templates/plugins/resource/aad/auth/bot/js/public/auth-end.html +60 -49
  312. package/templates/plugins/resource/aad/auth/bot/ts/public/auth-end.html +60 -49
  313. package/build/component/driver/share/shareToOthers.d.ts +0 -17
  314. package/build/component/driver/share/shareToOthers.d.ts.map +0 -1
  315. package/build/component/driver/share/shareToOthers.js +0 -127
  316. package/build/component/driver/share/shareToOthers.js.map +0 -1
@@ -0,0 +1,423 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ using Antlr4.Runtime.Misc;
5
+ using Azure.Core;
6
+ using Microsoft.Agents.Builder;
7
+ using Microsoft.Agents.Builder.Dialogs;
8
+ using Microsoft.Agents.Builder.Dialogs.Prompts;
9
+ using Microsoft.Agents.Core.Models;
10
+ using Microsoft.Agents.Extensions.Teams.Models;
11
+ using Microsoft.Identity.Client;
12
+ using System.Net;
13
+ using System.Text.RegularExpressions;
14
+ using System.IdentityModel.Tokens.Jwt;
15
+ using {{YOUR_NAMESPACE}}.Configuration;
16
+ using {{YOUR_NAMESPACE}}.SSO;
17
+ using System.Text.Json;
18
+ using Json.More;
19
+
20
+ namespace {{YOUR_NAMESPACE}};
21
+
22
+ /// <summary>
23
+ /// Creates a new prompt that leverage Teams Single Sign On (SSO) support for bot to automatically sign in user and
24
+ /// help receive oauth token, asks the user to consent if needed.
25
+ /// </summary>
26
+ /// <remarks>
27
+ /// The prompt will attempt to retrieve the user's current token of the desired scopes.
28
+ /// User will be automatically signed in leveraging Teams support of Bot Single Sign On(SSO):
29
+ /// https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots
30
+ /// </remarks>
31
+ ///
32
+ /// <example>
33
+ /// ## Prompt Usage
34
+ ///
35
+ /// When used with your bot's <see cref="DialogSet"/> you can simply add a new instance of the prompt as a named
36
+ /// dialog using <see cref="DialogSet.Add(Dialog)"/>. You can then start the prompt from a waterfall step using either
37
+ /// <see cref="DialogContext.BeginDialogAsync(string, object, CancellationToken)"/> or
38
+ /// <see cref="DialogContext.PromptAsync(string, PromptOptions, CancellationToken)"/>. The user
39
+ /// will be prompted to signin as needed and their access token will be passed as an argument to
40
+ /// the caller's next waterfall step.
41
+ ///
42
+ /// <code>
43
+ /// var convoState = new ConversationState(new MemoryStorage());
44
+ /// var dialogState = convoState.CreateProperty&lt;DialogState&gt;("dialogState");
45
+ /// var dialogs = new DialogSet(dialogState);
46
+ /// var botAuthOptions = new BotAuthenticationOptions {
47
+ /// ClientId = "{client_id_guid_value}",
48
+ /// ClientSecret = "{client_secret_value}",
49
+ /// TenantId = "{tenant_id_guid_value}",
50
+ /// ApplicationIdUri = "{application_id_uri_value}",
51
+ /// OAuthAuthority = "https://login.microsoftonline.com/{tenant_id_guid_value}",
52
+ /// LoginStartPageEndpoint = "https://{bot_web_app_domain}/bot-auth-start"
53
+ /// };
54
+ ///
55
+ /// var scopes = new string[] { "User.Read" };
56
+ /// var teamsBotSsoPromptSettings = new TeamsBotSsoPromptSettings(botAuthOptions, scopes);
57
+ ///
58
+ /// dialogs.Add(new TeamsBotSsoPrompt("{unique_id_for_the_prompt}", teamsBotSsoPromptSettings));
59
+ /// dialogs.Add(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
60
+ /// {
61
+ /// async(WaterfallStepContext stepContext, CancellationToken cancellationToken) => {
62
+ /// return await stepContext.BeginDialogAsync(nameof(TeamsBotSsoPrompt), null, cancellationToken);
63
+ /// },
64
+ /// async(WaterfallStepContext stepContext, CancellationToken cancellationToken) => {
65
+ /// var tokenResponse = (TeamsBotSsoPromptTokenResponse)stepContext.Result;
66
+ /// if (tokenResponse?.Token != null)
67
+ /// {
68
+ /// // ... continue with task needing access token ...
69
+ /// }
70
+ /// else
71
+ /// {
72
+ /// await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);
73
+ /// }
74
+ /// return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
75
+ /// }
76
+ /// }));
77
+ /// </code>
78
+ ///
79
+ /// </example>
80
+ public class TeamsBotSsoPrompt : Dialog
81
+ {
82
+ private readonly TeamsBotSsoPromptSettings _settings;
83
+ private const string PersistedExpires = "expires";
84
+ internal IIdentityClientAdapter _identityClientAdapter { private get; set; }
85
+ internal ITeamsInfo _teamsInfo { private get; set; }
86
+
87
+
88
+ /// <summary>
89
+ /// Initializes a new instance of the <see cref="TeamsBotSsoPrompt"/> class.
90
+ /// </summary>
91
+ /// <param name="dialogId">The ID to assign to this prompt.</param>
92
+ /// <param name="settings">Additional OAuth settings to use with this instance of the prompt.</param>
93
+ /// <remarks>The value of <paramref name="dialogId"/> must be unique within the
94
+ /// <see cref="DialogSet"/> or <see cref="ComponentDialog"/> to which the prompt is added.</remarks>
95
+ /// <exception cref="ExceptionCode.InvalidParameter">When input parameters is null.</exception>
96
+ public TeamsBotSsoPrompt(string dialogId, TeamsBotSsoPromptSettings settings) : base(dialogId)
97
+ {
98
+ if (string.IsNullOrWhiteSpace(dialogId))
99
+ {
100
+ throw new Exception($"Parameter {nameof(dialogId)} is null or empty.");
101
+ }
102
+ _settings = settings ?? throw new Exception($"Parameter {nameof(settings)} is null or empty.");
103
+
104
+ var confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(_settings.BotAuthOptions.ClientId)
105
+ .WithClientSecret(_settings.BotAuthOptions.ClientSecret)
106
+ .WithAuthority(_settings.BotAuthOptions.OAuthAuthority)
107
+ .Build();
108
+ _identityClientAdapter = new IdentityClientAdapter(confidentialClientApplication);
109
+ _teamsInfo = new TeamsInfoWrapper();
110
+ }
111
+
112
+ /// <summary>
113
+ /// Called when the dialog is started and pushed onto the dialog stack.
114
+ /// </summary>
115
+ /// <param name="dialogContext">The Microsoft.Bot.Builder.Dialogs.DialogContext for the current turn of conversation.</param>
116
+ /// <param name="options">Optional, initial information to pass to the dialog.</param>
117
+ /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
118
+ /// <returns> A System.Threading.Tasks.Task representing the asynchronous operation.</returns>
119
+ /// <exception cref="ExceptionCode.InvalidParameter">if dialog context argument is null</exception>
120
+ public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dialogContext, object options = null, CancellationToken cancellationToken = default)
121
+ {
122
+ if (dialogContext == null)
123
+ {
124
+ throw new Exception($"Parameter {nameof(dialogContext)} is null or empty.");
125
+ }
126
+
127
+ EnsureMsTeamsChannel(dialogContext);
128
+
129
+ var state = dialogContext.ActiveDialog?.State;
130
+ state[PersistedExpires] = DateTime.UtcNow.AddMilliseconds(_settings.Timeout);
131
+
132
+ // Send OAuthCard that tells Teams to obtain an authentication token for the bot application.
133
+ await SendOAuthCardToObtainTokenAsync(dialogContext.Context, cancellationToken).ConfigureAwait(false);
134
+ return EndOfTurn;
135
+ }
136
+
137
+ /// <summary>
138
+ /// Called when a prompt dialog is the active dialog and the user replied with a new activity.
139
+ /// </summary>
140
+ /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
141
+ /// <param name="cancellationToken">A cancellation token that can be used by other objects
142
+ /// or threads to receive notice of cancellation.</param>
143
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
144
+ /// <remarks>If the task is successful, the result indicates whether the dialog is still
145
+ /// active after the turn has been processed by the dialog.
146
+ /// <para>The prompt generally ends on invalid message from user's reply.</para></remarks>
147
+ /// <exception cref="ExceptionCode.InternalError">When failed to login with unknown error.</exception>
148
+ /// <exception cref="ExceptionCode.ServiceError">When failed to get access token from identity server(AAD).</exception>
149
+ public override async Task<DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
150
+ {
151
+ EnsureMsTeamsChannel(dc);
152
+
153
+ // Check for timeout
154
+ var state = dc.ActiveDialog?.State;
155
+ bool isMessage = (dc.Context.Activity.Type == ActivityTypes.Message);
156
+ bool isTimeoutActivityType =
157
+ isMessage ||
158
+ IsTeamsVerificationInvoke(dc.Context) ||
159
+ IsTokenExchangeRequestInvoke(dc.Context);
160
+
161
+ // If the incoming Activity is a message, or an Activity Type normally handled by TeamsBotSsoPrompt,
162
+ // check to see if this TeamsBotSsoPrompt Expiration has elapsed, and end the dialog if so.
163
+ bool hasTimedOut = isTimeoutActivityType && DateTime.Compare(DateTime.UtcNow, (DateTime)state[PersistedExpires]) > 0;
164
+ if (hasTimedOut)
165
+ {
166
+ return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
167
+ }
168
+ else
169
+ {
170
+ if (IsTeamsVerificationInvoke(dc.Context) || IsTokenExchangeRequestInvoke(dc.Context))
171
+ {
172
+ // Recognize token
173
+ PromptRecognizerResult<TeamsBotSsoPromptTokenResponse> recognized = await RecognizeTokenAsync(dc, cancellationToken).ConfigureAwait(false);
174
+
175
+ if (recognized.Succeeded)
176
+ {
177
+ return await dc.EndDialogAsync(recognized.Value, cancellationToken).ConfigureAwait(false);
178
+ }
179
+ }
180
+ else if (isMessage)
181
+ {
182
+ return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
183
+ }
184
+
185
+ return EndOfTurn;
186
+ }
187
+ }
188
+
189
+ /// <summary>
190
+ /// This is intended for internal use.
191
+ /// </summary>
192
+ /// <param name="dc">DialogContext.</param>
193
+ /// <param name="cancellationToken">CancellationToken.</param>
194
+ /// <returns>PromptRecognizerResult.</returns>
195
+ /// <exception cref="ExceptionCode.InternalError">When failed to login with unknown error.</exception>
196
+ /// <exception cref="ExceptionCode.ServiceError">When failed to get access token from identity server(AAD).</exception>
197
+ private async Task<PromptRecognizerResult<TeamsBotSsoPromptTokenResponse>> RecognizeTokenAsync(DialogContext dc, CancellationToken cancellationToken)
198
+ {
199
+
200
+ ITurnContext context = dc.Context;
201
+ var result = new PromptRecognizerResult<TeamsBotSsoPromptTokenResponse>();
202
+ TeamsBotSsoPromptTokenResponse tokenResponse = null;
203
+
204
+ if (IsTokenExchangeRequestInvoke(context))
205
+ {
206
+ var tokenResponseObject = context.Activity.Value.ToJsonDocument();
207
+ string ssoToken = tokenResponseObject.RootElement.GetProperty("token").ToString();
208
+ // Received activity is not a token exchange request
209
+ if (String.IsNullOrEmpty(ssoToken))
210
+ {
211
+ var warningMsg =
212
+ "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.";
213
+ await SendInvokeResponseAsync(context, HttpStatusCode.BadRequest, warningMsg, cancellationToken).ConfigureAwait(false);
214
+ }
215
+ else
216
+ {
217
+ try
218
+ {
219
+ var exchangedToken = await GetToken(ssoToken, _settings.Scopes).ConfigureAwait(false);
220
+
221
+ var ssoTokenObj = ParseJwt(ssoToken);
222
+ var ssoExpiration = DateTimeOffset.FromUnixTimeSeconds(long.Parse(ssoTokenObj.Payload["exp"].ToString()));
223
+ tokenResponse = new TeamsBotSsoPromptTokenResponse
224
+ {
225
+ SsoToken = ssoToken,
226
+ SsoTokenExpiration = ssoExpiration.ToString(),
227
+ Token = exchangedToken.Token,
228
+ Expiration = exchangedToken.ExpiresOn.ToString(),
229
+ ConnectionName = "fakeConnectionName"
230
+ };
231
+
232
+ await SendInvokeResponseAsync(context, HttpStatusCode.OK, null, cancellationToken).ConfigureAwait(false);
233
+ }
234
+ catch (MsalUiRequiredException) // Need user interaction
235
+ {
236
+ var warningMsg = "The bot is unable to exchange token. Ask for user consent first.";
237
+ await SendInvokeResponseAsync(context, HttpStatusCode.PreconditionFailed, new TokenExchangeInvokeResponse
238
+ {
239
+ Id = context.Activity.Id,
240
+ FailureDetail = warningMsg,
241
+ }, cancellationToken).ConfigureAwait(false);
242
+ }
243
+ catch (MsalServiceException ex) // Errors that returned from AAD service
244
+ {
245
+ throw new Exception($"Failed to get access token from OAuth identity server with error: {ex.ResponseBody}");
246
+ }
247
+ catch (MsalClientException ex) // Exceptions that are local to the MSAL library
248
+ {
249
+ throw new Exception($"Failed to get access token with error: {ex.Message}");
250
+ }
251
+
252
+ }
253
+ }
254
+ else if (IsTeamsVerificationInvoke(context))
255
+ {
256
+ await SendOAuthCardToObtainTokenAsync(context, cancellationToken).ConfigureAwait(false);
257
+ await SendInvokeResponseAsync(context, HttpStatusCode.OK, null, cancellationToken).ConfigureAwait(false);
258
+ }
259
+
260
+ if (tokenResponse != null)
261
+ {
262
+ result.Succeeded = true;
263
+ result.Value = tokenResponse;
264
+ }
265
+ else
266
+ {
267
+ result.Succeeded = false;
268
+ }
269
+ return result;
270
+ }
271
+
272
+ private async Task<AccessToken> GetToken(string ssoToken, string[] scopes)
273
+ {
274
+ AccessToken result;
275
+ var ssoTokenObj = ParseJwt(ssoToken);
276
+ var ssoTokenExpiration = DateTimeOffset.FromUnixTimeSeconds(long.Parse(ssoTokenObj.Payload["exp"].ToString()));
277
+
278
+ // Get sso token
279
+ if (scopes.Length == 0)
280
+ {
281
+ if (DateTimeOffset.Compare(DateTimeOffset.UtcNow, ssoTokenExpiration) > 0)
282
+ {
283
+ throw new Exception("SSO token has already expired.");
284
+ }
285
+ result = new AccessToken(ssoToken, ssoTokenExpiration);
286
+ }
287
+ else
288
+ {
289
+ var authenticationResult = await _identityClientAdapter.GetAccessToken(ssoToken, scopes).ConfigureAwait(false);
290
+ result = new AccessToken(authenticationResult.AccessToken, authenticationResult.ExpiresOn);
291
+ }
292
+ return result;
293
+ }
294
+
295
+ private static async Task SendInvokeResponseAsync(ITurnContext turnContext, HttpStatusCode statusCode, object body, CancellationToken cancellationToken)
296
+ {
297
+ await turnContext.SendActivityAsync(
298
+ new Activity
299
+ {
300
+ Type = ActivityTypes.InvokeResponse,
301
+ Value = new InvokeResponse
302
+ {
303
+ Status = (int)statusCode,
304
+ Body = body,
305
+ },
306
+ }, cancellationToken).ConfigureAwait(false);
307
+ }
308
+
309
+ private bool IsTeamsVerificationInvoke(ITurnContext context)
310
+ {
311
+ return (context.Activity.Type == ActivityTypes.Invoke) && (context.Activity.Name == SignInConstants.VerifyStateOperationName);
312
+ }
313
+ private bool IsTokenExchangeRequestInvoke(ITurnContext context)
314
+ {
315
+ return (context.Activity.Type == ActivityTypes.Invoke) && (context.Activity.Name == SignInConstants.TokenExchangeOperationName);
316
+ }
317
+
318
+ /// <summary>
319
+ /// Send OAuthCard that tells Teams to obtain an authentication token for the bot application.
320
+ /// For details see https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots.
321
+ /// </summary>
322
+ /// <param name="context">ITurnContext</param>
323
+ /// <param name="cancellationToken">CancellationToken.</param>
324
+ /// <returns>The task to await.</returns>
325
+ private async Task SendOAuthCardToObtainTokenAsync(ITurnContext context, CancellationToken cancellationToken)
326
+ {
327
+ TeamsChannelAccount account = await _teamsInfo.GetTeamsMemberAsync(context, context.Activity.From.Id, cancellationToken).ConfigureAwait(false);
328
+
329
+ string loginHint = account.UserPrincipalName ?? "";
330
+ if (String.IsNullOrEmpty(account.TenantId))
331
+ {
332
+ throw new Exception("Failed to get tenant id through bot framework.");
333
+ }
334
+ string tenantId = account.TenantId ?? "";
335
+ SignInResource signInResource = GetSignInResource(loginHint, tenantId);
336
+
337
+ // Ensure prompt initialized
338
+ IActivity prompt = Activity.CreateMessageActivity();
339
+ prompt.Attachments = new List<Attachment>();
340
+ prompt.Attachments.Add(new Attachment
341
+ {
342
+ ContentType = OAuthCard.ContentType,
343
+ Content = new OAuthCard
344
+ {
345
+ Text = "Sign In",
346
+ ConnectionName = "fakeConnectionName",
347
+ Buttons = new[]
348
+ {
349
+ new CardAction
350
+ {
351
+ Title = "Teams SSO Sign In",
352
+ Value = signInResource.SignInLink,
353
+ Type = ActionTypes.Signin,
354
+ },
355
+ },
356
+ TokenExchangeResource = signInResource.TokenExchangeResource,
357
+ },
358
+ });
359
+ // Send prompt
360
+ await context.SendActivityAsync(prompt, cancellationToken).ConfigureAwait(false);
361
+ }
362
+
363
+
364
+ /// <summary>
365
+ /// Get sign in authentication configuration
366
+ /// </summary>
367
+ /// <param name="loginHint">login hint</param>
368
+ /// <param name="tenantId">tenant id</param>
369
+ /// <returns>sign in resource</returns>
370
+ private SignInResource GetSignInResource(string loginHint, string tenantId)
371
+ {
372
+ string signInLink = $"{_settings.BotAuthOptions.InitiateLoginEndpoint}?scope={Uri.EscapeDataString(string.Join(" ", _settings.Scopes))}&clientId={_settings.BotAuthOptions.ClientId}&tenantId={tenantId}&loginHint={loginHint}";
373
+
374
+ SignInResource signInResource = new SignInResource
375
+ {
376
+ SignInLink = signInLink,
377
+ TokenExchangeResource = new TokenExchangeResource
378
+ {
379
+ Id = Guid.NewGuid().ToString(),
380
+ Uri = Regex.Replace(_settings.BotAuthOptions.ApplicationIdUri, @"/\/$/", "") + "/access_as_user"
381
+ }
382
+ };
383
+
384
+ return signInResource;
385
+ }
386
+
387
+ /// <summary>
388
+ /// Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
389
+ /// </summary>
390
+ /// <param name="dc">dialog context</param>
391
+ /// <exception cref="ExceptionCode.ChannelNotSupported"> if bot channel is not MS Teams </exception>
392
+ private void EnsureMsTeamsChannel(DialogContext dc)
393
+ {
394
+ if (dc.Context.Activity.ChannelId != Channels.Msteams)
395
+ {
396
+ var errorMessage = "Teams Bot SSO Prompt is only supported in MS Teams Channel";
397
+ throw new Exception(errorMessage);
398
+ }
399
+ }
400
+
401
+ private static JwtSecurityToken ParseJwt(string token)
402
+ {
403
+ if (string.IsNullOrEmpty(token))
404
+ {
405
+ throw new Exception("SSO token is null or empty.");
406
+ }
407
+ var handler = new JwtSecurityTokenHandler();
408
+ try
409
+ {
410
+ var jsonToken = handler.ReadToken(token);
411
+ if (jsonToken is not JwtSecurityToken tokenS || string.IsNullOrEmpty(tokenS.Payload["exp"].ToString()))
412
+ {
413
+ throw new Exception("Decoded token is null or exp claim does not exists.");
414
+ }
415
+ return tokenS;
416
+ }
417
+ catch (ArgumentException e)
418
+ {
419
+ var errorMessage = $"Parse jwt token failed with error: {e.Message}";
420
+ throw new Exception(errorMessage);
421
+ }
422
+ }
423
+ }
@@ -0,0 +1,41 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ using {{YOUR_NAMESPACE}}.Configuration;
5
+
6
+ namespace {{YOUR_NAMESPACE}}.SSO;
7
+
8
+ /// <summary>
9
+ /// Contains settings for an <see cref="TeamsBotSsoPrompt"/>.
10
+ /// </summary>
11
+ public class TeamsBotSsoPromptSettings
12
+ {
13
+
14
+ /// <summary>
15
+ /// Constructor of TeamsBotSsoPromptSettings
16
+ /// </summary>
17
+ public TeamsBotSsoPromptSettings(BotAuthenticationOptions botAuthOptions, string[] scopes, int timeout = 900000)
18
+ {
19
+ BotAuthOptions = botAuthOptions;
20
+ Scopes = scopes;
21
+ Timeout = timeout;
22
+ }
23
+
24
+ /// <summary>
25
+ /// Gets or sets the array of strings that declare the desired permissions and the resources requested.
26
+ /// </summary>
27
+ /// <value>The array of strings that declare the desired permissions and the resources requested.</value>
28
+ public string[] Scopes { get; set; }
29
+
30
+ /// <summary>
31
+ /// Gets or sets the number of milliseconds the prompt waits for the user to authenticate.
32
+ /// Default is 900,000 (15 minutes).
33
+ /// </summary>
34
+ /// <value>The number of milliseconds the prompt waits for the user to authenticate.</value>
35
+ public int Timeout { get; set; }
36
+
37
+ /// <summary>
38
+ /// Gets or sets bot related authentication options.
39
+ /// </summary>
40
+ public BotAuthenticationOptions BotAuthOptions { get; set; }
41
+ }
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ using Microsoft.Agents.Core.Models;
5
+ namespace {{YOUR_NAMESPACE}}.SSO;
6
+
7
+ /// <summary>
8
+ /// Token response provided by Teams Bot SSO prompt
9
+ /// </summary>
10
+ public class TeamsBotSsoPromptTokenResponse : TokenResponse
11
+ {
12
+ /// <summary>
13
+ /// SSO token for user
14
+ /// </summary>
15
+ public string SsoToken { get; set; }
16
+
17
+ /// <summary>
18
+ /// Expiration time of SSO token
19
+ /// </summary>
20
+ public string SsoTokenExpiration { get; set; }
21
+ }
@@ -0,0 +1,20 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ using Microsoft.Agents.Builder;
5
+ using Microsoft.Agents.Extensions.Teams.Connector;
6
+ using Microsoft.Agents.Extensions.Teams.Models;
7
+
8
+ namespace {{YOUR_NAMESPACE}}.SSO
9
+ {
10
+ /// <summary>
11
+ /// Helper class used to wrap static method and simplify unit test.
12
+ /// </summary>
13
+ internal class TeamsInfoWrapper : ITeamsInfo
14
+ {
15
+ public Task<TeamsChannelAccount> GetTeamsMemberAsync(ITurnContext context, string userId, CancellationToken cancellationToken = default)
16
+ {
17
+ return TeamsInfo.GetMemberAsync(context, userId, cancellationToken);
18
+ }
19
+ }
20
+ }
@@ -1,14 +1,15 @@
1
- using Microsoft.Bot.Builder;
2
- using Microsoft.Bot.Builder.Teams;
3
- using Microsoft.Bot.Schema;
4
- using Microsoft.Bot.Builder.Dialogs;
1
+ using Microsoft.Agents.Extensions.Teams.Compat;
2
+ using Microsoft.Agents.Builder.Dialogs;
3
+ using Microsoft.Agents.Builder.State;
4
+ using Microsoft.Agents.Builder;
5
+ using Microsoft.Agents.Core.Models;
5
6
 
6
7
  namespace {{YOUR_NAMESPACE}}.SSO;
7
8
 
8
9
  public class TeamsSsoBot<T> : TeamsActivityHandler where T : Dialog
9
10
  {
10
11
  private readonly ILogger<TeamsSsoBot<T>> _logger;
11
- private readonly BotState _conversationState;
12
+ private readonly AgentState _conversationState;
12
13
  private readonly Dialog _dialog;
13
14
  private readonly IStatePropertyAccessor<DialogState> _dialogState;
14
15
 
@@ -2,68 +2,79 @@
2
2
  <!--This file is used during the Teams Bot authentication flow to assist with retrieval of the access token.-->
3
3
  <!--If you're not familiar with this, do not alter or remove this file from your project.-->
4
4
  <html>
5
- <head>
6
- <title>Login End Page</title>
7
- <meta charset="utf-8" />
8
- </head>
9
5
 
10
- <body>
11
- <script
12
- src="https://res.cdn.office.net/teams-js/2.31.1/js/MicrosoftTeams.min.js"
13
- integrity="sha384-ihAqYgEJz9hzEU+HaYodG1aTzjebC/wKXQi1nWKZG7OLAUyOL9ZrzD/SfZu79Jeu"
14
- crossorigin="anonymous"
15
- ></script>
16
- <div id="divError"></div>
17
- <script type="text/javascript">
18
- microsoftTeams.app.initialize().then(() => {
19
- let hashParams = getHashParameters();
6
+ <head>
7
+ <title>Login End Page</title>
8
+ <meta charset="utf-8" />
9
+ </head>
20
10
 
21
- if (hashParams.get("error")) {
22
- // Authentication failed
23
- handleAuthError(hashParams.get("error"), hashParams);
24
- } else if (hashParams.get("code")) {
25
- // Get the stored state parameter and compare with incoming state
26
- let expectedState = localStorage.getItem("state");
27
- if (expectedState !== hashParams.get("state")) {
28
- // State does not match, report error
29
- handleAuthError("StateDoesNotMatch", hashParams);
30
- } else {
31
- microsoftTeams.authentication.notifySuccess();
32
- }
11
+ <body>
12
+ <script src="https://res.cdn.office.net/teams-js/2.31.1/js/MicrosoftTeams.min.js"
13
+ integrity="sha384-ihAqYgEJz9hzEU+HaYodG1aTzjebC/wKXQi1nWKZG7OLAUyOL9ZrzD/SfZu79Jeu"
14
+ crossorigin="anonymous"></script>
15
+ <div id="divError"></div>
16
+ <script type="text/javascript">
17
+ microsoftTeams.app.initialize().then(() => {
18
+ let hashParams = getHashParameters();
19
+
20
+ if (hashParams.get("error")) {
21
+ // Authentication failed
22
+ handleAuthError(hashParams.get("error"), hashParams);
23
+ } else if (hashParams.get("code")) {
24
+ // Get the stored state parameter and compare with incoming state
25
+ let expectedState = localStorage.getItem("state");
26
+ if (expectedState !== hashParams.get("state")) {
27
+ // State does not match, report error
28
+ handleAuthError("StateDoesNotMatch", hashParams);
33
29
  } else {
34
- // Unexpected condition: hash does not contain error or access_token parameter
35
- handleAuthError("UnexpectedFailure", hashParams);
30
+ microsoftTeams.authentication.notifySuccess();
36
31
  }
37
- });
32
+ } else {
33
+ // Unexpected condition: hash does not contain error or access_token parameter
34
+ handleAuthError("UnexpectedFailure", hashParams);
35
+ }
36
+ });
38
37
 
39
- // Parse hash parameters into key-value pairs
40
- function getHashParameters() {
41
- let hashParams = new Map();
38
+ // Parse hash parameters into key-value pairs
39
+ function getHashParameters() {
40
+ // Using ES6 Map instead of plain object to prevent prototype pollution
41
+ let hashParams = new Map();
42
+
43
+ if (location.hash && location.hash.length > 1) {
42
44
  location.hash
43
45
  .substr(1)
44
46
  .split("&")
45
47
  .forEach(function (item) {
48
+ if (!item) return;
46
49
  let s = item.split("="),
47
50
  k = s[0],
48
51
  v = s[1] && decodeURIComponent(s[1]);
49
- hashParams.set(k, v);
52
+
53
+ if (k) {
54
+ // Store parameters in the Map
55
+ hashParams.set(k, v);
56
+ }
50
57
  });
51
- return hashParams;
52
58
  }
59
+
60
+ // Convert Map back to a plain object for compatibility with the rest of the code
61
+ return Object.fromEntries(hashParams);
62
+ }
53
63
 
54
- // Show error information
55
- function handleAuthError(errorType, errorMessage) {
56
- const err = JSON.stringify({
57
- error: encodeURIComponent(errorType),
58
- message: encodeURIComponent(JSON.stringify(errorMessage)),
59
- });
60
- let para = document.createElement("p");
61
- let node = document.createTextNode(err);
62
- para.appendChild(node);
64
+ // Show error information
65
+ function handleAuthError(errorType, errorMessage) {
66
+ const err = JSON.stringify({
67
+ error: encodeURIComponent(errorType),
68
+ message: encodeURIComponent(JSON.stringify(errorMessage)),
69
+ });
70
+ let para = document.createElement("p");
71
+ let node = document.createTextNode(err);
72
+ para.appendChild(node);
63
73
 
64
- let element = document.getElementById("divError");
65
- element.appendChild(para);
66
- }
67
- </script>
68
- </body>
69
- </html>
74
+ let element = document.getElementById("divError");
75
+ element.appendChild(para);
76
+ }
77
+ </script>
78
+ </body>
79
+
80
+ </html>