Repository: microsoft/kiota (csharp)

Issue:
security mapping in getKiotaTree in npm package is not correct
### What are you generating using Kiota, clients or plugins?

Kiota plugin

### In what context or format are you using Kiota?

Npm package

### Client library/SDK language

None

### Describe the bug

When mapping from OpenAPI description a security requirement object does not return an array but a single object with a dictionary.

### Expected behavior

It should map into an array instead of single object

### How to reproduce

For example of below schema:
security:
  - api_key: []
  - petstore_auth:
	- write:pets
	- read:pets
 
It should produce security like below:

```code
"security": [
  {
	"api_key": []
  },
  {
	"petstore_auth": [
	  "write:pets",
	  "read:pets"
	]
  }
]
```
 
However, current getKiotaTree result is object:
```code
  {
	"path": "\\pet\\{petId}#GET",
	"segment": "GET",
	"children": [],
	"selected": true,
	"isOperation": true,
	"operationId": "getPetById",
	"summary": "Find pet by ID",
	"description": "Returns a single pet",
	"servers": [],
	"security": {
	  "api_key": [],
	  "petstore_auth": [
		"write:pets",
		"read:pets"
```

### Open API description file

_No response_

### Kiota Version

1.25.1

### Latest Kiota version known to work for scenario above?(Not required)

_No response_

### Known Workarounds

_No response_

### Configuration

_No response_

### Debug output

<details><summary>Click to expand log</summary>
```


<log output here>

```
</details>


### Other information

_No response_

Pull request description:
This PR closes #6417

Patch:
diff --git a/src/kiota/Rpc/PathItem.cs b/src/kiota/Rpc/PathItem.cs
index 8a52a943d8..5adc30cff7 100644
--- a/src/kiota/Rpc/PathItem.cs
+++ b/src/kiota/Rpc/PathItem.cs
@@ -11,6 +11,6 @@ public record PathItem(
     string? description = null,
     Uri? documentationUrl = null,
     IEnumerable<string>? servers = null,
-    IDictionary<string, IList<string>>? security = null, // key is the security scheme name, value is the list of scopes
+    IList<IDictionary<string, IList<string>?>>? security = null, // key is the security scheme name, value is the list of scopes
     AdaptiveCardInfo? adaptiveCard = null
 );
diff --git a/src/kiota/Rpc/SecurityRequirementMapper.cs b/src/kiota/Rpc/SecurityRequirementMapper.cs
index cd09e89bae..707bcba0e2 100644
--- a/src/kiota/Rpc/SecurityRequirementMapper.cs
+++ b/src/kiota/Rpc/SecurityRequirementMapper.cs
@@ -4,14 +4,15 @@ namespace kiota.Rpc
 {
     internal class SecurityRequirementMapper
     {
-        public static IDictionary<string, IList<string>>? FromSecurityRequirementList(IList<OpenApiSecurityRequirement>? securityRequirementList)
+        public static IList<IDictionary<string, IList<string>?>>? FromSecurityRequirementList(IList<OpenApiSecurityRequirement>? securityRequirementList)
         {
             if (securityRequirementList is null) return null;
-            var requirements = new Dictionary<string, IList<string>>();
+            var requirementsList = new List<IDictionary<string, IList<string>?>>();
 
             foreach (var securityRequirement in securityRequirementList)
             {
                 if (securityRequirement is null) continue;
+                var requirements = new Dictionary<string, IList<string>?>();
 
                 foreach (var securityRequirementItem in securityRequirement)
                 {
@@ -25,9 +26,10 @@ internal class SecurityRequirementMapper
 
                     requirements.Add(name, scopes);
                 }
-            }
+                requirementsList.Add(requirements);
 
-            return requirements;
+            }
+            return requirementsList;
         }
     }
 }
diff --git a/src/kiota/Rpc/SecuritySchemeMapper.cs b/src/kiota/Rpc/SecuritySchemeMapper.cs
index b4fadd4d6f..c362270a25 100644
--- a/src/kiota/Rpc/SecuritySchemeMapper.cs
+++ b/src/kiota/Rpc/SecuritySchemeMapper.cs
@@ -29,7 +29,7 @@ internal static Dictionary<string, SecuritySchemeInfo> FromComponents(OpenApiCom
         private static SecuritySchemeInfo BuildSchemeInfoFromSecurityScheme(IOpenApiSecurityScheme value)
         {
             string? description = value?.Description;
-            string? @in = value?.In?.GetDisplayName() ?? "testvalue";
+            string? @in = value?.In?.GetDisplayName();
             string? scheme = value?.Scheme;
             string? name = value?.Name;
             string? bearerFormat = value?.BearerFormat;
diff --git a/src/kiota/Rpc/ShowResult.cs b/src/kiota/Rpc/ShowResult.cs
index f66e4fdd61..b6bba1bc88 100644
--- a/src/kiota/Rpc/ShowResult.cs
+++ b/src/kiota/Rpc/ShowResult.cs
@@ -5,6 +5,6 @@ public record ShowResult(
     PathItem? rootNode,
     string? apiTitle,
     IEnumerable<string>? servers = null,
-    IDictionary<string, IList<string>>? security = null,
+    IList<IDictionary<string, IList<string>?>>? security = null,
     IDictionary<string, SecuritySchemeInfo>? securitySchemes = null);
 


Test patch:
diff --git a/tests/Kiota.Builder.IntegrationTests/ModelWithMultipleSecurity.yaml b/tests/Kiota.Builder.IntegrationTests/ModelWithMultipleSecurity.yaml
new file mode 100644
index 0000000000..3c32efeaa0
--- /dev/null
+++ b/tests/Kiota.Builder.IntegrationTests/ModelWithMultipleSecurity.yaml
@@ -0,0 +1,93 @@
+﻿openapi: 3.0.0
+info:
+  title: Repair Service
+  version: 1.0.0
+servers:
+  - url: https://sample.server/api
+components:
+  securitySchemes:
+    oAuth2AuthCode:
+      type: oauth2
+      description: OAuth configuration for the repair service
+      flows:
+        authorizationCode:
+          authorizationUrl: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
+          tokenUrl: https://login.microsoftonline.com/common/oauth2/v2.0/token
+          refreshUrl: https://login.microsoftonline.com/common/oauth2/v2.0/refresh
+          scopes:
+            api://sample/repairs_read: Read repair records
+            api://sample/repairs_write: Write repair records
+        password:
+          tokenUrl: https://login.microsoftonline.com/common/oauth2/v2.0/token
+          refreshUrl: https://login.microsoftonline.com/common/oauth2/v2.0/refresh
+          scopes:
+            api://sample/repairs_read: Read repair records
+            api://sample/repairs_write: Write repair records
+        implicit:
+          authorizationUrl: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
+          refreshUrl: https://login.microsoftonline.com/common/oauth2/v2.0/refresh
+          scopes:
+            api://sample/repairs_read: Read repair records
+            api://sample/repairs_write: Write repair records
+        clientCredentials:
+          tokenUrl: https://login.microsoftonline.com/common/oauth2/v2.0/token
+          refreshUrl: https://login.microsoftonline.com/common/oauth2/v2.0/refresh
+          scopes:
+            api://sample/repairs_read: Read repair records
+            api://sample/repairs_write: Write repair records
+    httpAuth:
+      type: http
+      scheme: basic
+      description: HTTP basic authentication
+    apiKeyAuth:
+      type: apiKey
+      in: header
+      name: X-API-Key
+      description: API key authentication
+    openIdConnectAuth:
+      type: openIdConnect
+      description: OpenID Connect authentication
+      openIdConnectUrl: https://login.microsoftonline.com/common/.well-known/openid-configuration
+paths:
+  /repairs:
+    get:
+      operationId: listRepairs
+      summary: List all repairs with oauth
+      description: Returns a list of repairs with their details and images
+      servers:
+        - url: https://sample.server.overridden/api
+      security:
+        - oAuth2AuthCode: ["api://sample/repairs_read"]
+      responses:
+        "200":
+          description: A list of repairs
+          content:
+            application/json:
+              schema:
+                type: object
+    post:
+      summary: Create new repair with oauth
+      description: Returns the create repair
+      security:
+        - oAuth2AuthCode: ["api://sample/repairs_write"]
+        - httpAuth: []
+      responses:
+        "200":
+          description: A new repair
+          content:
+            application/json:
+              schema:
+                type: object
+    put:
+      summary: Update new repair with oauth
+      description: Returns the updated repair
+      security:
+        - oAuth2AuthCode: ["api://sample/repairs_write"]
+          httpAuth: []
+      responses:
+        "200":
+          description: A new repair
+          content:
+            application/json:
+              schema:
+                type: object
\ No newline at end of file
diff --git a/vscode/npm-package/tests/integration/integrationGetKiotaTree.spec.ts b/vscode/npm-package/tests/integration/integrationGetKiotaTree.spec.ts
index 63cd31eedf..50b5f1f474 100644
--- a/vscode/npm-package/tests/integration/integrationGetKiotaTree.spec.ts
+++ b/vscode/npm-package/tests/integration/integrationGetKiotaTree.spec.ts
@@ -1,6 +1,5 @@
 import { KiotaLogEntry, KiotaTreeResult, KiotaOpenApiNode, LogLevel, SecurityRequirementObject, OAuth2SecurityScheme, HttpSecurityScheme, ApiKeySecurityScheme, OpenIdSecurityScheme } from "../../types";
 import { getKiotaTree } from "../../lib/getKiotaTree";
-import { LogLevel } from "../../types";
 
 function existsGreaterThanLevelLogs(logs: KiotaLogEntry[] | undefined, level: LogLevel): boolean {
   if (!logs) return false;
@@ -22,12 +21,6 @@ function flattenKiotaTree(tree: KiotaOpenApiNode): KiotaOpenApiNode[] {
   return result;
 }
 
-function findSecurityRequirementByName(node: KiotaOpenApiNode | undefined, security_requirement_name: string): SecurityRequirementObject | undefined {
-  if (!node) return undefined;
-  if (!node.security) return undefined;
-  return node.security[security_requirement_name];
-}
-
 describe("getKiotaTree", () => {
   test('testGetKiotaTree_from_valid_File', async () => {
     const descriptionUrl = '../../tests/Kiota.Builder.IntegrationTests/DiscriminatorSample.yaml';
@@ -51,22 +44,33 @@ describe("getKiotaTree", () => {
     expect(existsGreaterThanLevelLogs(actual?.logs, LogLevel.warning)).toBeFalsy();
     expect(existsGreaterThanLevelLogs(actual?.logs, LogLevel.information)).toBeTruthy();
 
+    // Check if the security requirements are defined correctly for get operation
+    // It should have one security requirement: oAuth2AuthCode
     const actualListOperationNode = findOperationByPath(actual, '\\repairs#GET');
     expect(actualListOperationNode).toBeDefined();
     expect(actualListOperationNode?.operationId).toEqual('listRepairs');
     expect(actualListOperationNode?.security).toBeDefined();
-    const actualListSecurityRequirement = findSecurityRequirementByName(actualListOperationNode, 'oAuth2AuthCode');
-    expect(actualListSecurityRequirement).toBeDefined();
-    expect(actualListSecurityRequirement?.[0]).toEqual('api://sample/repairs_read');
-
+    expect(actualListOperationNode?.security?.length).toEqual(1);
+    const firstSecurityRequirementInGet = actualListOperationNode?.security?.[0];
+    const oAuth2AuthCodeSecurityRequirementInGet = firstSecurityRequirementInGet?.['oAuth2AuthCode'];
+    expect(oAuth2AuthCodeSecurityRequirementInGet).toBeDefined();
+    expect(oAuth2AuthCodeSecurityRequirementInGet?.length).toEqual(1);
+    expect(oAuth2AuthCodeSecurityRequirementInGet?.[0]).toEqual('api://sample/repairs_read');
+
+    // Check if the security requirements are defined correctly for post operation
+    // It should have two security requirements: oAuth2AuthCode and httpAuth
     const actualPostOperationNode = findOperationByPath(actual, '\\repairs#POST');
     expect(actualPostOperationNode).toBeDefined();
     expect(actualPostOperationNode?.operationId).toEqual('repairs_post');
     expect(actualPostOperationNode?.security).toBeDefined();
-    const actualPostSecurityRequirement = findSecurityRequirementByName(actualPostOperationNode, 'oAuth2AuthCode');
-    expect(actualPostSecurityRequirement).toBeDefined();
-    expect(actualPostSecurityRequirement?.[0]).toEqual('api://sample/repairs_write');
-
+    expect(actualPostOperationNode?.security?.length).toEqual(1);
+    const firstSecurityRequirementInPost = actualPostOperationNode?.security?.[0];
+    const oAuth2AuthCodeSecurityRequirementInPost = firstSecurityRequirementInPost?.['oAuth2AuthCode'];
+    expect(oAuth2AuthCodeSecurityRequirementInPost).toBeDefined();
+    expect(oAuth2AuthCodeSecurityRequirementInPost?.length).toEqual(1);
+    expect(oAuth2AuthCodeSecurityRequirementInPost?.[0]).toEqual('api://sample/repairs_write');
+
+    // Check if the security schemes are defined correctly for post operation
     const actualOAuthSecuritySchema = actual?.securitySchemes?.["oAuth2AuthCode"] as OAuth2SecurityScheme;
     expect(actualOAuthSecuritySchema).toBeDefined();
     expect(actualOAuthSecuritySchema.flows).toBeDefined();
@@ -121,6 +125,116 @@ describe("getKiotaTree", () => {
 
   });
 
+  test('testGetKiotaTree_withMultipleSecurity', async () => {
+    const descriptionUrl = '../../tests/Kiota.Builder.IntegrationTests/ModelWithMultipleSecurity.yaml';
+
+    const actual = await getKiotaTree({ includeFilters: [], descriptionPath: descriptionUrl, excludeFilters: [], clearCache: false });
+
+    expect(actual).toBeDefined();
+    expect(existsGreaterThanLevelLogs(actual?.logs, LogLevel.warning)).toBeFalsy();
+    expect(existsGreaterThanLevelLogs(actual?.logs, LogLevel.information)).toBeTruthy();
+
+    // Check if the security requirements are defined correctly for get operation
+    // It should have one security requirement: oAuth2AuthCode
+    const actualListOperationNode = findOperationByPath(actual, '\\repairs#GET');
+    expect(actualListOperationNode).toBeDefined();
+    expect(actualListOperationNode?.operationId).toEqual('listRepairs');
+    expect(actualListOperationNode?.security).toBeDefined();
+    expect(actualListOperationNode?.security?.length).toEqual(1);
+    const firstSecurityRequirementInGet = actualListOperationNode?.security?.[0];
+    const oAuth2AuthCodeSecurityRequirementInGet = firstSecurityRequirementInGet?.['oAuth2AuthCode'];
+    expect(oAuth2AuthCodeSecurityRequirementInGet).toBeDefined();
+    expect(oAuth2AuthCodeSecurityRequirementInGet?.length).toEqual(1);
+    expect(oAuth2AuthCodeSecurityRequirementInGet?.[0]).toEqual('api://sample/repairs_read');
+
+    // Check if the security requirements are defined correctly for post operation
+    // It should have two security requirements: oAuth2AuthCode and httpAuth
+    const actualPostOperationNode = findOperationByPath(actual, '\\repairs#POST');
+    expect(actualPostOperationNode).toBeDefined();
+    expect(actualPostOperationNode?.operationId).toEqual('repairs_post');
+    expect(actualPostOperationNode?.security).toBeDefined();
+    expect(actualPostOperationNode?.security?.length).toEqual(2);
+    const firstSecurityRequirementInPost = actualPostOperationNode?.security?.[0];
+    const oAuth2AuthCodeSecurityRequirementInPost = firstSecurityRequirementInPost?.['oAuth2AuthCode'];
+    expect(oAuth2AuthCodeSecurityRequirementInPost).toBeDefined();
+    expect(oAuth2AuthCodeSecurityRequirementInPost?.length).toEqual(1);
+    expect(oAuth2AuthCodeSecurityRequirementInPost?.[0]).toEqual('api://sample/repairs_write');
+    const secondSecurityRequirementInPost = actualPostOperationNode?.security?.[1];
+    expect(secondSecurityRequirementInPost).toBeDefined();
+    const httpAuthSecurityRequirementInPost = secondSecurityRequirementInPost?.['httpAuth'];
+    expect(httpAuthSecurityRequirementInPost).toBeDefined();
+    expect(httpAuthSecurityRequirementInPost?.length).toEqual(0);
+
+    // Check if the security requirements are defined correctly for post operation
+    // It should have two security requirements: oAuth2AuthCode and httpAuth
+    const actualPutOperationNode = findOperationByPath(actual, '\\repairs#PUT');
+    expect(actualPutOperationNode).toBeDefined();
+    expect(actualPutOperationNode?.operationId).toEqual('repairs_put');
+    expect(actualPutOperationNode?.security).toBeDefined();
+    expect(actualPutOperationNode?.security?.length).toEqual(1);
+    const firstSecurityRequirementInPut = actualPutOperationNode?.security?.[0];
+    const oAuth2AuthCodeSecurityRequirementInPut = firstSecurityRequirementInPut?.['oAuth2AuthCode'];
+    expect(oAuth2AuthCodeSecurityRequirementInPut).toBeDefined();
+    expect(oAuth2AuthCodeSecurityRequirementInPut?.length).toEqual(1);
+    expect(oAuth2AuthCodeSecurityRequirementInPut?.[0]).toEqual('api://sample/repairs_write');
+    const httpAuthSecurityRequirementInPut = firstSecurityRequirementInPut?.['httpAuth'];
+    expect(httpAuthSecurityRequirementInPut).toBeDefined();
+    expect(httpAuthSecurityRequirementInPut?.length).toEqual(0);
+
+    // Check if the security schemes are defined correctly for post operation
+    const actualOAuthSecuritySchema = actual?.securitySchemes?.["oAuth2AuthCode"] as OAuth2SecurityScheme;
+    expect(actualOAuthSecuritySchema).toBeDefined();
+    expect(actualOAuthSecuritySchema.flows).toBeDefined();
+    const actualAuthorizationCodeFlow = actualOAuthSecuritySchema.flows.authorizationCode;
+    expect(actualAuthorizationCodeFlow).toBeDefined();
+    expect(actualAuthorizationCodeFlow?.authorizationUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/authorize');
+    expect(actualAuthorizationCodeFlow?.tokenUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/token');
+    expect(actualAuthorizationCodeFlow?.refreshUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/refresh');
+    expect(actualAuthorizationCodeFlow?.scopes).toBeDefined();
+    expect(actualAuthorizationCodeFlow?.scopes['api://sample/repairs_read']).toEqual('Read repair records');
+    expect(actualAuthorizationCodeFlow?.scopes['api://sample/repairs_write']).toEqual('Write repair records');
+    const actualImplicitFlow = actualOAuthSecuritySchema.flows.implicit;
+    expect(actualImplicitFlow).toBeDefined();
+    expect(actualImplicitFlow?.authorizationUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/authorize');
+    expect(actualImplicitFlow?.refreshUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/refresh');
+    expect(actualImplicitFlow?.scopes).toBeDefined();
+    expect(actualImplicitFlow?.scopes['api://sample/repairs_read']).toEqual('Read repair records');
+    expect(actualImplicitFlow?.scopes['api://sample/repairs_write']).toEqual('Write repair records');
+    const actualClientCredentialsFlow = actualOAuthSecuritySchema.flows.clientCredentials;
+    expect(actualClientCredentialsFlow).toBeDefined();
+    expect(actualClientCredentialsFlow?.tokenUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/token');
+    expect(actualClientCredentialsFlow?.refreshUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/refresh');
+    expect(actualClientCredentialsFlow?.scopes).toBeDefined();
+    expect(actualClientCredentialsFlow?.scopes['api://sample/repairs_read']).toEqual('Read repair records');
+    expect(actualClientCredentialsFlow?.scopes['api://sample/repairs_write']).toEqual('Write repair records');
+    const actualPasswordFlow = actualOAuthSecuritySchema.flows.password;
+    expect(actualPasswordFlow).toBeDefined();
+    expect(actualPasswordFlow?.tokenUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/token');
+    expect(actualPasswordFlow?.refreshUrl).toEqual('https://login.microsoftonline.com/common/oauth2/v2.0/refresh');
+    expect(actualPasswordFlow?.scopes).toBeDefined();
+    expect(actualPasswordFlow?.scopes['api://sample/repairs_read']).toEqual('Read repair records');
+    expect(actualPasswordFlow?.scopes['api://sample/repairs_write']).toEqual('Write repair records');
+
+    const actualHttpSecuritySchema = actual?.securitySchemes?.["httpAuth"] as HttpSecurityScheme;
+    expect(actualHttpSecuritySchema).toBeDefined();
+    expect(actualHttpSecuritySchema.type).toEqual('http');
+    expect(actualHttpSecuritySchema.scheme).toEqual('basic');
+    expect(actualHttpSecuritySchema.description).toEqual('HTTP basic authentication');
+
+    const actualApiKeySecuritySchema = actual?.securitySchemes?.["apiKeyAuth"] as ApiKeySecurityScheme;
+    expect(actualApiKeySecuritySchema).toBeDefined();
+    expect(actualApiKeySecuritySchema.type).toEqual('apiKey');
+    expect(actualApiKeySecuritySchema.name).toEqual('X-API-Key');
+    expect(actualApiKeySecuritySchema.in).toEqual('header');
+    expect(actualApiKeySecuritySchema.description).toEqual('API key authentication');
+
+    const actualOpenIdConnectSecuritySchema = actual?.securitySchemes?.["openIdConnectAuth"] as OpenIdSecurityScheme;
+    expect(actualOpenIdConnectSecuritySchema).toBeDefined();
+    expect(actualOpenIdConnectSecuritySchema.type).toEqual('openIdConnect');
+    expect(actualOpenIdConnectSecuritySchema.description).toEqual('OpenID Connect authentication');
+    expect(actualOpenIdConnectSecuritySchema.openIdConnectUrl).toEqual('https://login.microsoftonline.com/common/.well-known/openid-configuration');
+  });
+
   test('testGetKiotaTree_withReferenceIdExtension', async () => {
     const descriptionUrl = '../../tests/Kiota.Builder.IntegrationTests/ModelWithRefIdExtension.yaml';
     const actual = await getKiotaTree({ includeFilters: [], descriptionPath: descriptionUrl, excludeFilters: [], clearCache: false });