diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9205393585..fe6e929f59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,12 +22,16 @@ 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 [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
- 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
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html
new file mode 100644
index 0000000000..f685f79214
--- /dev/null
+++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.disallowedScheme.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html
new file mode 100644
index 0000000000..2d64094d2a
--- /dev/null
+++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png
new file mode 100644
index 0000000000..7862e7e63a
Binary files /dev/null and b/__tests__/html2/adaptiveCard/msteams/signInSubAction.html.snap-1.png differ
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html
new file mode 100644
index 0000000000..3884b40105
--- /dev/null
+++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.relativeURL.html
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html
new file mode 100644
index 0000000000..6941a90178
--- /dev/null
+++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.size.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html
new file mode 100644
index 0000000000..acb2c2ee99
--- /dev/null
+++ b/__tests__/html2/adaptiveCard/msteams/signInSubAction.skip.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
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/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 866c954677..bf8b3bd0e8 100644
--- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx
+++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx
@@ -17,7 +17,21 @@ 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,
+ check,
+ literal,
+ object,
+ optional,
+ pipe,
+ readonly,
+ safeParse,
+ string,
+ transform,
+ url,
+ type InferInput
+} from 'valibot';
import useAdaptiveCardsHostConfig from '../hooks/useAdaptiveCardsHostConfig';
import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage';
@@ -32,6 +46,30 @@ 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 microsoftTeamsSubActionSchema = object({
+ msteams: object({})
+});
+
+const microsoftTeamsSignInSubActionSchema = object({
+ msteams: object({
+ 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)
+ )
+ })
+});
const { useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd, useUIState } = hooks;
@@ -55,6 +93,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();
@@ -155,12 +196,39 @@ function AdaptiveCardRenderer(props: AdaptiveCardRendererProps) {
} else if (data.__isBotFrameworkCardAction) {
performCardAction(data.cardAction);
} else {
- performCardAction({
- image,
- title,
- type: 'postBack',
- value: data
- });
+ 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,
+ title,
+ type: 'postBack',
+ value: data
+ });
+ }
}
}
@@ -170,7 +238,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',