Repository: OfficeDev/microsoft-teams-library-js
Pull request #2759: Added support for `isDeeplyNestedAuthSupported` to check if deeply nested auth is supported.
Author: login: baljesingh
Created: 2025-03-25T19:47:41Z | Merged: 2025-03-28T01:09:02Z

For more information about how to contribute to this repo, visit [this page](https://github.com/OfficeDev/microsoft-teams-library-js/blob/main/CONTRIBUTING.md).

## Description

> Added API to get the value of isDeeplyNestedAuthSupported from the runtime object. This is required by a nested child app to determine if deeply nested auth is supported. The parent app can read this value and pass it on to its child app.

### Main changes in the PR:

1. Added `isDeeplyNestedAuthSupported` in Runtime V4.
2. Added UI test in `nestedAppAuth.json`
3. Added unit tests in `nestedAppAuth.spec.ts`
4. Added `CheckIsDeeplyNestedAuthSupported` section in teams test app.
5. Added `isDeeplyNestedAuthSupported()` method in `nestedAppAuth.ts` to get value of isDeeplyNestedAuthSupported from runtime object

## Validation

### Validation performed:

1. Pull the changes. 
2. Run the teams test app. 
3. Click on 'Check Deeply Nested Auth Supported' button and check the value. It should be true because all SDKs supports it. 

### Unit Tests added:

> Unit tests are required for all changes. If no unit tests were added as part of this change, please explain why they aren't necessary.

Yes

### End-to-end tests added:

Yes

## Additional Requirements

### Change file added:

> Ensure the change file meets the [formatting requirements](https://github.com/OfficeDev/microsoft-teams-library-js/blob/main/CONTRIBUTING.md#change-log-using-beachball).

<Yes/No>

### Related PRs:

> Remove this section if n/a

### Next/remaining steps:

> List the next or remaining steps in implementing the overall feature in subsequent PRs (or is the feature 100% complete after this?).

> Remove this section if n/a

- [ ] Item 1
- [ ] Item 2

### Screenshots:

> Remove this section if n/a

| Before     | After      |
| ---------- | ---------- |
| < image1 > | < image2 > |



Diff:
diff --git a/apps/teams-test-app/e2e-test-data/nestedAppAuth.json b/apps/teams-test-app/e2e-test-data/nestedAppAuth.json
index 87d090080a..f306ee32e2 100644
--- a/apps/teams-test-app/e2e-test-data/nestedAppAuth.json
+++ b/apps/teams-test-app/e2e-test-data/nestedAppAuth.json
@@ -16,6 +16,18 @@
       "boxSelector": "#box_getParentOrigin",
       "platformsExcluded": ["iOS", "Android"],
       "expectedTestAppValue": "https://local.teams.office.com:8080"
+    },
+    {
+      "title": "nestedAppAuth isDeeplyNestedAuthSupported API Call - Success",
+      "type": "callResponse",
+      "version": ">2.35.0",
+      "boxSelector": "#box_checkIsDeeplyNestedAuthSupported",
+      "hostSdkVersion": {
+        "web": ">=7.1.0",
+        "android": ">=6.0.0",
+        "ios": ">=6.0.0"
+      },
+      "expectedTestAppValue": "NAA deeply nested auth is supported"
     }
   ]
 }
diff --git a/apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx b/apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx
index 45df761c1d..58e94f3ed1 100644
--- a/apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx
+++ b/apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx
@@ -79,6 +79,14 @@ const NestedAppAuthAPIs = (): ReactElement => {
       onClick: async () => `${nestedAppAuth.getParentOrigin()}`,
     });
 
+  const CheckIsDeeplyNestedAuthSupported = (): ReactElement =>
+    ApiWithoutInput({
+      name: 'checkIsDeeplyNestedAuthSupported',
+      title: 'Check Deeply Nested Auth Supported',
+      onClick: async () =>
+        `NAA deeply nested auth ${nestedAppAuth.isDeeplyNestedAuthSupported() ? 'is' : 'is not'} supported`,
+    });
+
   const SendMessageToNestedAppAuthBridge = (): React.ReactElement =>
     ApiWithTextInput({
       name: 'sendMessageToNestedAppAuthBridge',
@@ -232,6 +240,7 @@ const NestedAppAuthAPIs = (): ReactElement => {
     <ModuleWrapper title="NestedAppAuth">
       <CheckIsNAAChannelRecommended />
       <GetParentOrigin />
+      <CheckIsDeeplyNestedAuthSupported />
       <SendMessageToNestedAppAuthBridge />
       <SendMessageToTopWindow />
       <AddChildIframeSection />
diff --git a/change/@microsoft-teams-js-291c03f0-c69f-4b98-b14a-e8ae1f8da29d.json b/change/@microsoft-teams-js-291c03f0-c69f-4b98-b14a-e8ae1f8da29d.json
new file mode 100644
index 0000000000..2ee54982dd
--- /dev/null
+++ b/change/@microsoft-teams-js-291c03f0-c69f-4b98-b14a-e8ae1f8da29d.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "Added support for `isDeeplyNestedAuthSupported` to check if deeply nested auth is supported.",
+  "packageName": "@microsoft/teams-js",
+  "email": "baljesingh@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/teams-js/src/public/nestedAppAuth.ts b/packages/teams-js/src/public/nestedAppAuth.ts
index e89bb61831..6dc030d279 100644
--- a/packages/teams-js/src/public/nestedAppAuth.ts
+++ b/packages/teams-js/src/public/nestedAppAuth.ts
@@ -42,6 +42,18 @@ export function getParentOrigin(): string | null {
   return Communication.parentOrigin;
 }
 
+/**
+ * Checks if NAA deeply nested scenario supported by the host
+ * @returns true if host supports
+ *
+ * @throws Error if {@linkcode app.initialize} has not successfully completed
+ *
+ * @beta
+ */
+export function isDeeplyNestedAuthSupported(): boolean {
+  return (ensureInitialized(runtime) && isNAAChannelRecommended() && runtime.isDeeplyNestedAuthSupported) ?? false;
+}
+
 function isNAAChannelRecommendedForLegacyTeamsMobile(): boolean {
   return ensureInitialized(runtime) &&
     isHostAndroidOrIOSOrIPadOSOrVisionOS() &&
diff --git a/packages/teams-js/src/public/runtime.ts b/packages/teams-js/src/public/runtime.ts
index 0018b3a43d..3e746167a4 100644
--- a/packages/teams-js/src/public/runtime.ts
+++ b/packages/teams-js/src/public/runtime.ts
@@ -221,6 +221,7 @@ interface IRuntimeV4 extends IBaseRuntime {
   readonly apiVersion: 4;
   readonly hostVersionsInfo?: HostVersionsInfo;
   readonly isNAAChannelRecommended?: boolean;
+  readonly isDeeplyNestedAuthSupported?: boolean;
   readonly isLegacyTeams?: boolean;
   readonly supports: {
     readonly app?: {
@@ -371,6 +372,7 @@ export let runtime: Runtime | UninitializedRuntime = _uninitializedRuntime;
 export const versionAndPlatformAgnosticTeamsRuntimeConfig: Runtime = {
   apiVersion: 4,
   isNAAChannelRecommended: false,
+  isDeeplyNestedAuthSupported: false,
   hostVersionsInfo: teamsMinAdaptiveCardVersion,
   isLegacyTeams: true,
   supports: {
diff --git a/packages/teams-js/test/public/nestedAppAuth.spec.ts b/packages/teams-js/test/public/nestedAppAuth.spec.ts
index c572a87280..484254615b 100644
--- a/packages/teams-js/test/public/nestedAppAuth.spec.ts
+++ b/packages/teams-js/test/public/nestedAppAuth.spec.ts
@@ -123,6 +123,46 @@ describe('nestedAppAuth', () => {
       });
     });
   });
+  describe('nestedAppAuth.isDeeplyNestedAuthSupported', () => {
+    it('should throw if called before initialization', () => {
+      utils.uninitializeRuntimeConfig();
+      expect(() => nestedAppAuth.isDeeplyNestedAuthSupported()).toThrowError(new Error(errorLibraryNotInitialized));
+    });
+
+    it('should return true if isDeeplyNestedAuthSupported set to true in runtime object', async () => {
+      await utils.initializeWithContext(FrameContexts.content);
+      const runtimeConfig: Runtime = {
+        apiVersion: 4,
+        supports: {},
+        isNAAChannelRecommended: true,
+        isDeeplyNestedAuthSupported: true,
+      };
+      utils.setRuntimeConfig(runtimeConfig);
+      expect(nestedAppAuth.isDeeplyNestedAuthSupported()).toBeTruthy();
+    });
+
+    it('should return false if isDeeplyNestedAuthSupported set to false in runtime object ', async () => {
+      await utils.initializeWithContext(FrameContexts.content);
+      const runtimeConfig: Runtime = {
+        apiVersion: 4,
+        supports: {},
+        isNAAChannelRecommended: false,
+      };
+      utils.setRuntimeConfig(runtimeConfig);
+      expect(nestedAppAuth.isDeeplyNestedAuthSupported()).toBeFalsy();
+    });
+
+    it('should return false if isDeeplyNestedAuthSupported not present in runtime object ', async () => {
+      await utils.initializeWithContext(FrameContexts.content);
+      const runtimeConfig: Runtime = {
+        apiVersion: 4,
+        supports: {},
+        isNAAChannelRecommended: true,
+      };
+      utils.setRuntimeConfig(runtimeConfig);
+      expect(nestedAppAuth.isDeeplyNestedAuthSupported()).toBeFalsy();
+    });
+  });
   describe('getParentOrigin', () => {
     it('should throw if called before initialization', () => {
       utils.uninitializeRuntimeConfig();


Reviews:
user: login: maglims
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T00:04:36Z
user: login: baljesingh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T00:37:36Z
user: login: maglims
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T22:46:18Z
user: login: jadahiya-MSFT
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T22:54:46Z

Review comments:
diff_hunk: @@ -370,6 +371,7 @@ export let runtime: Runtime | UninitializedRuntime = _uninitializedRuntime;
 export const versionAndPlatformAgnosticTeamsRuntimeConfig: Runtime = {
   apiVersion: 4,
   isNAAChannelRecommended: false,
+  isDeeplyNestedAuthSupported: false,
path: packages/teams-js/src/public/runtime.ts
user: login: maglims
body: Does this value need to be updated, if so, how it will be updated? For now, it looks it is just set to false.
created_at: 2025-03-26T00:04:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -370,6 +371,7 @@ export let runtime: Runtime | UninitializedRuntime = _uninitializedRuntime;
 export const versionAndPlatformAgnosticTeamsRuntimeConfig: Runtime = {
   apiVersion: 4,
   isNAAChannelRecommended: false,
+  isDeeplyNestedAuthSupported: false,
path: packages/teams-js/src/public/runtime.ts
user: login: baljesingh
body: `versionAndPlatformAgnosticTeamsRuntimeConfig` is used as the default runtime for early versions of Teams that don't pass the runtime object. For others, all capabilities are dynamically discovered at runtime.
created_at: 2025-03-26T00:37:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-25T19:47:56Z
body: This pull request contains changes to the runtime.ts file. If you, as the author of this PR, have made changes to the Runtime interface please review RUNTIME.md to determine if a new runtime version is required. Please reply to this comment stating what changes, if any, were made to the runtime object and whether a new runtime version was required.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-25T19:52:01Z
body: ## size-limit report 📦 
| Path                                                      | Size                  | Loading time (3g) | Running time (snapdragon) | Total time |
| --------------------------------------------------------- | --------------------- | ----------------- | ------------------------- | ---------- |
| packages/teams-js/dist/esm/packages/teams-js/src/index.js | 182.76 KB (+0.09% 🔺) | 3.7 s (+0.09% 🔺) | 156 ms (-1.97% 🔽)        | 3.9 s      |
author_association: CONTRIBUTOR
user: login: baljesingh
created_at: 2025-03-27T17:11:30Z
body: > This pull request contains changes to the runtime.ts file. If you, as the author of this PR, have made changes to the Runtime interface please review RUNTIME.md to determine if a new runtime version is required. Please reply to this comment stating what changes, if any, were made to the runtime object and whether a new runtime version was required.

Added an optional property and hence no new version is required.
author_association: CONTRIBUTOR