From 8575752721e42bba5dac09130796d9237b8546dc Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 9 Sep 2026 23:15:35 +0000 Subject: [PATCH 01/13] Handle AC msteams/signin submit action --- .../html2/adaptiveCard/signInAction.html | 115 ++++++++++++++++++ .../adaptiveCard/signInAction.popup.html | 10 ++ .../Attachment/AdaptiveCardRenderer.tsx | 50 ++++++-- 3 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/signInAction.html create mode 100644 __tests__/html2/adaptiveCard/signInAction.popup.html diff --git a/__tests__/html2/adaptiveCard/signInAction.html b/__tests__/html2/adaptiveCard/signInAction.html new file mode 100644 index 0000000000..832804c0ef --- /dev/null +++ b/__tests__/html2/adaptiveCard/signInAction.html @@ -0,0 +1,115 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/signInAction.popup.html b/__tests__/html2/adaptiveCard/signInAction.popup.html new file mode 100644 index 0000000000..325f15984b --- /dev/null +++ b/__tests__/html2/adaptiveCard/signInAction.popup.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index 866c954677..5d438d7342 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -17,7 +17,19 @@ import React, { type MouseEventHandler } from 'react'; import { useRefFrom } from 'use-ref-from'; -import { any, boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; +import { + any, + boolean, + literal, + object, + optional, + pipe, + readonly, + safeParse, + string, + url, + type InferInput +} from 'valibot'; import useAdaptiveCardsHostConfig from '../hooks/useAdaptiveCardsHostConfig'; import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage'; @@ -33,6 +45,13 @@ import renderAdaptiveCard from './private/renderAdaptiveCard'; import styles from './AdaptiveCardRenderer.module.css'; +const microsoftTeamsSignInActionSchema = object({ + msteams: object({ + type: literal('signin'), + value: pipe(string(), url()) + }) +}); + const { useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd, useUIState } = hooks; const adaptiveCardRendererPropsSchema = pipe( @@ -155,12 +174,29 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { } else if (data.__isBotFrameworkCardAction) { performCardAction(data.cardAction); } else { - performCardAction({ - image, - title, - type: 'postBack', - value: data - }); + const parseMSTeamsSignInActionResult = safeParse(microsoftTeamsSignInActionSchema, data); + + if (parseMSTeamsSignInActionResult.success) { + window.open( + parseMSTeamsSignInActionResult.output.msteams.value, + '_blank', + [ + // TODO: Configurable width and height. + ['height', '640'], + ['popup', ''], + ['width', '480'] + ] + .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) + .join(',') + ); + } else { + performCardAction({ + image, + title, + type: 'postBack', + value: data + }); + } } } From e6c03a781485c4b8479892e215afd7b91d992e3d Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 02:34:03 +0000 Subject: [PATCH 02/13] Rename to *.skip.html --- __tests__/html2/adaptiveCard/signInAction.html | 2 +- .../{signInAction.popup.html => signInAction.skip.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename __tests__/html2/adaptiveCard/{signInAction.popup.html => signInAction.skip.html} (100%) diff --git a/__tests__/html2/adaptiveCard/signInAction.html b/__tests__/html2/adaptiveCard/signInAction.html index 832804c0ef..6073de403a 100644 --- a/__tests__/html2/adaptiveCard/signInAction.html +++ b/__tests__/html2/adaptiveCard/signInAction.html @@ -57,7 +57,7 @@ data: { msteams: { type: 'signin', - value: new URL('signInAction.popup.html', location) + value: new URL('signInAction.skip.html', location) } } } diff --git a/__tests__/html2/adaptiveCard/signInAction.popup.html b/__tests__/html2/adaptiveCard/signInAction.skip.html similarity index 100% rename from __tests__/html2/adaptiveCard/signInAction.popup.html rename to __tests__/html2/adaptiveCard/signInAction.skip.html From 8be698df0cd6fa5ad3812275b83d703c2aaa2c69 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 02:52:12 +0000 Subject: [PATCH 03/13] Add screenshot --- .../adaptiveCard/signInAction.html.snap-1.png | Bin 0 -> 7912 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/signInAction.html.snap-1.png diff --git a/__tests__/html2/adaptiveCard/signInAction.html.snap-1.png b/__tests__/html2/adaptiveCard/signInAction.html.snap-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7862e7e63a4512fb2cb8ac452e60318c870b0a2a GIT binary patch literal 7912 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 literal 0 HcmV?d00001 From b8118b0e827afdede67b7d5fb7216a66ce96b856 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 21:24:59 +0000 Subject: [PATCH 04/13] Add popup window size to AC style options --- .../html2/adaptiveCard/signInAction.html | 15 +- .../html2/adaptiveCard/signInAction.size.html | 129 ++++++++++++++++++ .../html2/adaptiveCard/signInAction.skip.html | 2 +- .../AdaptiveCardsStyleOptions.ts | 14 ++ .../Attachment/AdaptiveCardRenderer.tsx | 18 ++- .../src/adaptiveCards/defaultStyleOptions.ts | 2 + 6 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 __tests__/html2/adaptiveCard/signInAction.size.html diff --git a/__tests__/html2/adaptiveCard/signInAction.html b/__tests__/html2/adaptiveCard/signInAction.html index 6073de403a..ce87c27550 100644 --- a/__tests__/html2/adaptiveCard/signInAction.html +++ b/__tests__/html2/adaptiveCard/signInAction.html @@ -77,17 +77,20 @@ type: 'message' }); + // THEN: Should render the Adaptive Card with a "Sign in" button. await pageConditions.numActivitiesShown(1); - await host.snapshot('local'); // GIVEN: Intercept window.open to watch the popup window to be closed. const originalOpen = window.open.bind(window); const popupClosedDeferred = Promise.withResolvers(); + let windowOpenArgs; window.open = (...args) => { const popup = originalOpen(...args); + windowOpenArgs = args; + // Note: popup.addEventListener('close') does not dispatch. const interval = setInterval(() => { if (popup.closed) { @@ -107,7 +110,15 @@ await Promise.race([ popupClosedDeferred.promise, // THEN: Fail if popup window does not close after 2 seconds. - new Promise((_, reject) => setTimeout(() => reject(new Error('Timed out waiting for popup window to be closed')), 2_000)) + new Promise((_, reject) => + setTimeout(() => reject(new Error('Timed out waiting for popup window to be closed')), 2_000) + ) + ]); + + expect(windowOpenArgs).toEqual([ + expect.stringContaining('signInAction.skip.html'), + '_blank', + 'height=640,popup,width=480' ]); }); diff --git a/__tests__/html2/adaptiveCard/signInAction.size.html b/__tests__/html2/adaptiveCard/signInAction.size.html new file mode 100644 index 0000000000..589470b792 --- /dev/null +++ b/__tests__/html2/adaptiveCard/signInAction.size.html @@ -0,0 +1,129 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/signInAction.skip.html b/__tests__/html2/adaptiveCard/signInAction.skip.html index 325f15984b..acb2c2ee99 100644 --- a/__tests__/html2/adaptiveCard/signInAction.skip.html +++ b/__tests__/html2/adaptiveCard/signInAction.skip.html @@ -4,7 +4,7 @@ diff --git a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts index 6e83467a61..49f8e0ffe8 100644 --- a/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/AdaptiveCardsStyleOptions.ts @@ -33,6 +33,20 @@ type StrictAdaptiveCardsStyleOptions = { * style; see issue #4327). */ richCardTitleOmitHeadingRole: boolean | undefined; + + /** + * Adaptive Cards: sign-in action popup window height (in pixel) + * + * @default 640 + */ + adaptiveCardSignInActionPopupWindowHeight: number | undefined; + + /** + * Adaptive Cards: sign-in action popup window width (in pixel) + * + * @default 480 + */ + adaptiveCardSignInActionPopupWindowWidth: number | undefined; }; type AdaptiveCardsStyleOptions = Partial; diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index 5d438d7342..6d51ec76c1 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -44,6 +44,8 @@ import { directLineCardActionSchema } from './private/directLineSchema'; import renderAdaptiveCard from './private/renderAdaptiveCard'; import styles from './AdaptiveCardRenderer.module.css'; +import useStyleOptions from '../../hooks/useStyleOptions'; +import normalizeStyleOptions from '../normalizeStyleOptions'; const microsoftTeamsSignInActionSchema = object({ msteams: object({ @@ -74,6 +76,9 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { tapAction } = validateProps(adaptiveCardRendererPropsSchema, props); + const { adaptiveCardSignInActionPopupWindowHeight, adaptiveCardSignInActionPopupWindowWidth } = normalizeStyleOptions( + useStyleOptions()[0] + ); const [{ GlobalSettings, HostConfig }] = useAdaptiveCardsPackage(); const [adaptiveCardsHostConfig] = useAdaptiveCardsHostConfig(); const [uiState] = useUIState(); @@ -181,10 +186,9 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { parseMSTeamsSignInActionResult.output.msteams.value, '_blank', [ - // TODO: Configurable width and height. - ['height', '640'], + ['height', adaptiveCardSignInActionPopupWindowHeight], ['popup', ''], - ['width', '480'] + ['width', adaptiveCardSignInActionPopupWindowWidth] ] .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) .join(',') @@ -206,7 +210,13 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { console.error(action); } }, - [disabledRef, performCardAction, scrollToEnd] + [ + adaptiveCardSignInActionPopupWindowHeight, + adaptiveCardSignInActionPopupWindowWidth, + disabledRef, + performCardAction, + scrollToEnd + ] ); // For accessibility issue #1340, `tabindex="0"` must not be set for the root container if it is not interactive. diff --git a/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts b/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts index d3ff62377b..f5002a8a44 100644 --- a/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts +++ b/packages/bundle/src/adaptiveCards/defaultStyleOptions.ts @@ -1,6 +1,8 @@ import { type AdaptiveCardsStyleOptions } from './AdaptiveCardsStyleOptions'; const ADAPTIVE_CARDS_DEFAULT_STYLE_OPTIONS: Required = { + adaptiveCardSignInActionPopupWindowHeight: 640, + adaptiveCardSignInActionPopupWindowWidth: 480, adaptiveCardsParserMaxVersion: undefined, cardEmphasisBackgroundColor: '#F9F9F9', cardPushButtonBackgroundColor: '#0063B1', From 341b75027c943e9fae2220c233e9bd9497baee4f Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 21:44:40 +0000 Subject: [PATCH 05/13] Add entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9205393585..46b0c9a5e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ Legends: ### Added - Added `styleOptions.richCardTitleOmitHeadingRole` (default `false`) to opt out of `style: 'heading'` on rich card titles, in PR [#5839](https://github.com/microsoft/BotFramework-WebChat/pull/5839), by [@cjennison](https://github.com/cjennison) +- Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim) + - Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window + - Link to [spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData) + - Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload ### Fixed From e6f75e895e03afdfaa852c1c6ac4c2a70f6f1356 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 14:59:38 -0700 Subject: [PATCH 06/13] Convert URL to string in signInAction.html Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- __tests__/html2/adaptiveCard/signInAction.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/html2/adaptiveCard/signInAction.html b/__tests__/html2/adaptiveCard/signInAction.html index ce87c27550..d5f231908b 100644 --- a/__tests__/html2/adaptiveCard/signInAction.html +++ b/__tests__/html2/adaptiveCard/signInAction.html @@ -57,7 +57,7 @@ data: { msteams: { type: 'signin', - value: new URL('signInAction.skip.html', location) + value: new URL('signInAction.skip.html', location).toString() } } } From b01562dd3948d2218a8957380f5ceaf7ad273b2b Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 15:01:07 -0700 Subject: [PATCH 07/13] Enhance URL validation for MSTeams sign-in action Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Attachment/AdaptiveCardRenderer.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index 6d51ec76c1..3c3d925c92 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -182,17 +182,23 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { const parseMSTeamsSignInActionResult = safeParse(microsoftTeamsSignInActionSchema, data); if (parseMSTeamsSignInActionResult.success) { - window.open( - parseMSTeamsSignInActionResult.output.msteams.value, - '_blank', - [ - ['height', adaptiveCardSignInActionPopupWindowHeight], - ['popup', ''], - ['width', adaptiveCardSignInActionPopupWindowWidth] - ] - .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) - .join(',') - ); + const { value } = parseMSTeamsSignInActionResult.output.msteams; + + if (['http:', 'https:'].includes(new URL(value).protocol)) { + window.open( + value, + '_blank', + [ + ['height', adaptiveCardSignInActionPopupWindowHeight], + ['popup', ''], + ['width', adaptiveCardSignInActionPopupWindowWidth] + ] + .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) + .join(',') + ); + } else { + console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', value); + } } else { performCardAction({ image, From d1920f54886ccd2f751a5dab84295b9c6557af8f Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 10 Sep 2026 22:05:18 +0000 Subject: [PATCH 08/13] Add test for disallowed scheme --- CHANGELOG.md | 2 +- .../signInAction.disallowedScheme.html | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 __tests__/html2/adaptiveCard/signInAction.disallowedScheme.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b0c9a5e6..da8e2daed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ Legends: - Added `styleOptions.richCardTitleOmitHeadingRole` (default `false`) to opt out of `style: 'heading'` on rich card titles, in PR [#5839](https://github.com/microsoft/BotFramework-WebChat/pull/5839), by [@cjennison](https://github.com/cjennison) - Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim) - Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window - - Link to [spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData) + - Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData) - Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload ### Fixed diff --git a/__tests__/html2/adaptiveCard/signInAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/signInAction.disallowedScheme.html new file mode 100644 index 0000000000..6d7ff831e2 --- /dev/null +++ b/__tests__/html2/adaptiveCard/signInAction.disallowedScheme.html @@ -0,0 +1,118 @@ + + + + + + +
+ + + + From 2615ba5ac8aaa6ccd68822ee95fe95860de8d042 Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 11 Sep 2026 00:19:16 +0000 Subject: [PATCH 09/13] Add more validation --- .../signInSubAction.disallowedScheme.html} | 4 +- .../signInSubAction.html} | 0 .../signInSubAction.html.snap-1.png} | Bin .../msteams/signInSubAction.relativeURL.html | 119 ++++++++++++++++++ .../signInSubAction.size.html} | 0 .../signInSubAction.skip.html} | 0 .../msteams/unknownSubAction.html | 106 ++++++++++++++++ .../Attachment/AdaptiveCardRenderer.tsx | 68 ++++++---- 8 files changed, 272 insertions(+), 25 deletions(-) rename __tests__/html2/adaptiveCard/{signInAction.disallowedScheme.html => msteams/signInSubAction.disallowedScheme.html} (95%) rename __tests__/html2/adaptiveCard/{signInAction.html => msteams/signInSubAction.html} (100%) rename __tests__/html2/adaptiveCard/{signInAction.html.snap-1.png => msteams/signInSubAction.html.snap-1.png} (100%) create mode 100644 __tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html rename __tests__/html2/adaptiveCard/{signInAction.size.html => msteams/signInSubAction.size.html} (100%) rename __tests__/html2/adaptiveCard/{signInAction.skip.html => msteams/signInSubAction.skip.html} (100%) create mode 100644 __tests__/html2/adaptiveCard/msteams/unknownSubAction.html diff --git a/__tests__/html2/adaptiveCard/signInAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html similarity index 95% rename from __tests__/html2/adaptiveCard/signInAction.disallowedScheme.html rename to __tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html index 6d7ff831e2..7c4b93eaf3 100644 --- a/__tests__/html2/adaptiveCard/signInAction.disallowedScheme.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html @@ -109,8 +109,8 @@ // THEN: Should warn. expect(consoleWarnArgs).toEqual([ - 'botframework-webchat: Cannot open URL with disallowed schemes.', - 'mailto:johndoe@microsoft.com' + 'botframework-webchat: "Action.Submit/msteams" sub-action validation error.', + '"value" must have protocol of either "http:" or "https:"' ]); }); diff --git a/__tests__/html2/adaptiveCard/signInAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html similarity index 100% rename from __tests__/html2/adaptiveCard/signInAction.html rename to __tests__/html2/adaptiveCard/msteams/signInSubAction.html diff --git a/__tests__/html2/adaptiveCard/signInAction.html.snap-1.png b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png similarity index 100% rename from __tests__/html2/adaptiveCard/signInAction.html.snap-1.png rename to __tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html new file mode 100644 index 0000000000..92dc7aed1d --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html @@ -0,0 +1,119 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/adaptiveCard/signInAction.size.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html similarity index 100% rename from __tests__/html2/adaptiveCard/signInAction.size.html rename to __tests__/html2/adaptiveCard/msteams/signInSubAction.size.html diff --git a/__tests__/html2/adaptiveCard/signInAction.skip.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html similarity index 100% rename from __tests__/html2/adaptiveCard/signInAction.skip.html rename to __tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html diff --git a/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html b/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html new file mode 100644 index 0000000000..1ab4e2dcce --- /dev/null +++ b/__tests__/html2/adaptiveCard/msteams/unknownSubAction.html @@ -0,0 +1,106 @@ + + + + + + +
+ + + + diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index 3c3d925c92..bf8b3bd0e8 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -20,6 +20,7 @@ import { useRefFrom } from 'use-ref-from'; import { any, boolean, + check, literal, object, optional, @@ -27,6 +28,7 @@ import { readonly, safeParse, string, + transform, url, type InferInput } from 'valibot'; @@ -47,10 +49,25 @@ import styles from './AdaptiveCardRenderer.module.css'; import useStyleOptions from '../../hooks/useStyleOptions'; import normalizeStyleOptions from '../normalizeStyleOptions'; -const microsoftTeamsSignInActionSchema = object({ +const microsoftTeamsSubActionSchema = object({ + msteams: object({}) +}); + +const microsoftTeamsSignInSubActionSchema = object({ msteams: object({ - type: literal('signin'), - value: pipe(string(), url()) + type: literal('signin', 'Sub-action type must be "signin"'), + value: pipe( + string('"value" must be a string'), + url('"value" must be an absolute URL'), + check(value => { + try { + return ['http:', 'https:'].includes(new URL(value).protocol); + } catch { + return false; + } + }, '"value" must have protocol of either "http:" or "https:"'), + transform(value => value as any) + ) }) }); @@ -179,26 +196,31 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) { } else if (data.__isBotFrameworkCardAction) { performCardAction(data.cardAction); } else { - const parseMSTeamsSignInActionResult = safeParse(microsoftTeamsSignInActionSchema, data); - - if (parseMSTeamsSignInActionResult.success) { - const { value } = parseMSTeamsSignInActionResult.output.msteams; - - if (['http:', 'https:'].includes(new URL(value).protocol)) { - window.open( - value, - '_blank', - [ - ['height', adaptiveCardSignInActionPopupWindowHeight], - ['popup', ''], - ['width', adaptiveCardSignInActionPopupWindowWidth] - ] - .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) - .join(',') - ); - } else { - console.warn('botframework-webchat: Cannot open URL with disallowed schemes.', value); - } + const parseMSTeamsSubActionResult = safeParse(microsoftTeamsSubActionSchema, data); + + if (parseMSTeamsSubActionResult.success) { + const parseMSTeamsSignInSubActionResult = safeParse(microsoftTeamsSignInSubActionSchema, data); + + if (parseMSTeamsSignInSubActionResult.success) { + const { value } = parseMSTeamsSignInSubActionResult.output.msteams; + + window.open( + value, + '_blank', + [ + ['height', adaptiveCardSignInActionPopupWindowHeight], + ['popup', ''], + ['width', adaptiveCardSignInActionPopupWindowWidth] + ] + .map(([key, value]) => (value ? [key, encodeURIComponent(value)].join('=') : key)) + .join(',') + ); + } else { + console.warn( + 'botframework-webchat: "Action.Submit/msteams" sub-action validation error.', + ...parseMSTeamsSignInSubActionResult.issues.map(({ message }) => message) + ); + } } else { performCardAction({ image, From d612b974441601e9f8ad3cb301d6fc0386e8b7a6 Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 11 Sep 2026 00:21:05 +0000 Subject: [PATCH 10/13] Add entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da8e2daed6..0258061c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Legends: - Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window - Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData) - Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload + - Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation ### Fixed From 2be3d026827985f68512bd5f327830550af3e83d Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 11 Sep 2026 00:27:32 +0000 Subject: [PATCH 11/13] Remove empty line --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0258061c99..fe6e929f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,6 @@ Legends: - Fixed an error when a failed activity is present when Web Chat mounts, resolving [#5812](https://github.com/microsoft/BotFramework-WebChat/issues/5812), in PR [#5848](https://github.com/microsoft/BotFramework-WebChat/pull/5848), by [@OEvgeny](https://github.com/OEvgeny) - ## [4.19.1] - 2026-06-09 ### Changed From 403c524a37e78d8bb1de0d7f8e1fe1c6a696c84e Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 11 Sep 2026 01:56:42 +0000 Subject: [PATCH 12/13] Fix tests --- .../adaptiveCard/msteams/signInSubAction.disallowedScheme.html | 1 - __tests__/html2/adaptiveCard/msteams/signInSubAction.html | 2 +- __tests__/html2/adaptiveCard/msteams/signInSubAction.size.html | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html index 7c4b93eaf3..f685f79214 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html @@ -79,7 +79,6 @@ // THEN: Should render the Adaptive Card with a "Sign in" button. await pageConditions.numActivitiesShown(1); - await host.snapshot('local'); // GIVEN: Intercept `console.warn`. const originalConsoleWarn = console.warn.bind(console); diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html index d5f231908b..43aca6f455 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html @@ -57,7 +57,7 @@ data: { msteams: { type: 'signin', - value: new URL('signInAction.skip.html', location).toString() + value: new URL('signInSubAction.skip.html', location).toString() } } } diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html index 589470b792..c7fcc9eefb 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html @@ -61,7 +61,7 @@ data: { msteams: { type: 'signin', - value: new URL('signInAction.skip.html', location) + value: new URL('signInSubAction.skip.html', location) } } } From 197790c1d2c984bcb11bf44f19abf3c142c56641 Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 11 Sep 2026 04:14:35 +0000 Subject: [PATCH 13/13] Fix tests --- __tests__/html2/adaptiveCard/msteams/signInSubAction.html | 2 +- .../html2/adaptiveCard/msteams/signInSubAction.relativeURL.html | 1 - __tests__/html2/adaptiveCard/msteams/signInSubAction.size.html | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html index 43aca6f455..2d64094d2a 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html @@ -116,7 +116,7 @@ ]); expect(windowOpenArgs).toEqual([ - expect.stringContaining('signInAction.skip.html'), + expect.stringContaining('signInSubAction.skip.html'), '_blank', 'height=640,popup,width=480' ]); diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html index 92dc7aed1d..3884b40105 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html @@ -79,7 +79,6 @@ // THEN: Should render the Adaptive Card with a "Sign in" button. await pageConditions.numActivitiesShown(1); - await host.snapshot('local'); // GIVEN: Intercept `console.warn`. const originalConsoleWarn = console.warn.bind(console); diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html index c7fcc9eefb..6941a90178 100644 --- a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html +++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html @@ -119,7 +119,7 @@ ]); expect(windowOpenArgs).toEqual([ - expect.stringContaining('signInAction.skip.html'), + expect.stringContaining('signInSubAction.skip.html'), '_blank', 'height=480,popup,width=360' ]);