@azure/communication-common 2.3.2-alpha.20250122.2 → 2.3.2-alpha.20250122.7

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 (50) hide show
  1. package/README.md +48 -0
  2. package/dist/browser/azureCommunicationTokenCredential.d.ts +6 -0
  3. package/dist/browser/azureCommunicationTokenCredential.d.ts.map +1 -1
  4. package/dist/browser/azureCommunicationTokenCredential.js +8 -4
  5. package/dist/browser/azureCommunicationTokenCredential.js.map +1 -1
  6. package/dist/browser/entraTokenCredential.d.ts +45 -0
  7. package/dist/browser/entraTokenCredential.d.ts.map +1 -0
  8. package/dist/browser/entraTokenCredential.js +120 -0
  9. package/dist/browser/entraTokenCredential.js.map +1 -0
  10. package/dist/browser/index.d.ts +1 -1
  11. package/dist/browser/index.d.ts.map +1 -1
  12. package/dist/browser/index.js +0 -1
  13. package/dist/browser/index.js.map +1 -1
  14. package/dist/commonjs/azureCommunicationTokenCredential.d.ts +6 -0
  15. package/dist/commonjs/azureCommunicationTokenCredential.d.ts.map +1 -1
  16. package/dist/commonjs/azureCommunicationTokenCredential.js +8 -4
  17. package/dist/commonjs/azureCommunicationTokenCredential.js.map +1 -1
  18. package/dist/commonjs/entraTokenCredential.d.ts +45 -0
  19. package/dist/commonjs/entraTokenCredential.d.ts.map +1 -0
  20. package/dist/commonjs/entraTokenCredential.js +124 -0
  21. package/dist/commonjs/entraTokenCredential.js.map +1 -0
  22. package/dist/commonjs/index.d.ts +1 -1
  23. package/dist/commonjs/index.d.ts.map +1 -1
  24. package/dist/commonjs/index.js +0 -1
  25. package/dist/commonjs/index.js.map +1 -1
  26. package/dist/esm/azureCommunicationTokenCredential.d.ts +6 -0
  27. package/dist/esm/azureCommunicationTokenCredential.d.ts.map +1 -1
  28. package/dist/esm/azureCommunicationTokenCredential.js +8 -4
  29. package/dist/esm/azureCommunicationTokenCredential.js.map +1 -1
  30. package/dist/esm/entraTokenCredential.d.ts +45 -0
  31. package/dist/esm/entraTokenCredential.d.ts.map +1 -0
  32. package/dist/esm/entraTokenCredential.js +120 -0
  33. package/dist/esm/entraTokenCredential.js.map +1 -0
  34. package/dist/esm/index.d.ts +1 -1
  35. package/dist/esm/index.d.ts.map +1 -1
  36. package/dist/esm/index.js +0 -1
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/react-native/azureCommunicationTokenCredential.d.ts +6 -0
  39. package/dist/react-native/azureCommunicationTokenCredential.d.ts.map +1 -1
  40. package/dist/react-native/azureCommunicationTokenCredential.js +8 -4
  41. package/dist/react-native/azureCommunicationTokenCredential.js.map +1 -1
  42. package/dist/react-native/entraTokenCredential.d.ts +45 -0
  43. package/dist/react-native/entraTokenCredential.d.ts.map +1 -0
  44. package/dist/react-native/entraTokenCredential.js +120 -0
  45. package/dist/react-native/entraTokenCredential.js.map +1 -0
  46. package/dist/react-native/index.d.ts +1 -1
  47. package/dist/react-native/index.d.ts.map +1 -1
  48. package/dist/react-native/index.js +0 -1
  49. package/dist/react-native/index.js.map +1 -1
  50. package/package.json +14 -1
package/README.md CHANGED
@@ -8,6 +8,7 @@ This package contains common code for Azure Communication Service libraries.
8
8
 
9
9
  - An [Azure subscription][azure_sub].
10
10
  - An existing Communication Services resource. If you need to create the resource, you can use the [Azure Portal][azure_portal], the [Azure PowerShell][azure_powershell], or the [Azure CLI][azure_cli].
11
+ - Having the @azure/identity package installed.
11
12
 
12
13
  ### Installing
13
14
 
@@ -15,6 +16,10 @@ This package contains common code for Azure Communication Service libraries.
15
16
  npm install @azure/communication-common
16
17
  ```
17
18
 
19
+ ```bash
20
+ npm install @azure/identity
21
+ ```
22
+
18
23
  ### Browser support
19
24
 
20
25
  #### JavaScript Bundle
@@ -33,6 +38,7 @@ Depending on your scenario, you may want to initialize the `AzureCommunicationTo
33
38
 
34
39
  - a static token (suitable for short-lived clients used to e.g. send one-off Chat messages) or
35
40
  - a callback function that ensures a continuous authentication state during communications (ideal e.g. for long Calling sessions).
41
+ - a token credential capable of obtaining an Entra user token. You can provide any implementation of [TokenCredential interface](https://learn.microsoft.com/es-mx/javascript/api/@azure/core-auth/tokencredential?view=azure-node-latest). It is suitable for scenarios where Entra user access tokens are needed to authenticate with Communication Services.
36
42
 
37
43
  The tokens supplied to the `AzureCommunicationTokenCredential` either through the constructor or via the token refresher callback can be obtained using the Azure Communication Identity library.
38
44
 
@@ -82,6 +88,48 @@ const tokenCredential = new AzureCommunicationTokenCredential({
82
88
  });
83
89
  ```
84
90
 
91
+ ### Create a credential with a token credential capable of obtaining an Entra user token
92
+
93
+ For scenarios where an Entra user can be used with Communication Services, you need to initialize any implementation of [TokenCredential interface](https://learn.microsoft.com/es-mx/javascript/api/@azure/core-auth/tokencredential?view=azure-node-latest) and provide it to the ``EntraCommunicationTokenCredentialOptions``.
94
+ Along with this, you must provide the URI of the Azure Communication Services resource and the scopes required for the Entra user token. These scopes determine the permissions granted to the token.
95
+ If the scopes are not provided, by default, it sets the scopes to `https://communication.azure.com/clients/.default`.
96
+
97
+ ```typescript
98
+ const options: InteractiveBrowserCredentialInBrowserOptions = {
99
+ tenantId: "<your-tenant-id>",
100
+ clientId: "<your-client-id>",
101
+ redirectUri: "<your-redirect-uri>",
102
+ };
103
+ const entraTokenCredential = new InteractiveBrowserCredential(options);
104
+
105
+ const entraTokenCredentialOptions: EntraCommunicationTokenCredentialOptions = {
106
+ resourceEndpoint: "https://<your-resource>.communication.azure.com",
107
+ tokenCredential: entraTokenCredential,
108
+ scopes: ["https://communication.azure.com/clients/VoIP"]
109
+ };
110
+
111
+ const credential = new AzureCommunicationTokenCredential(entraTokenCredentialOptions);
112
+ ```
113
+
114
+ The same approach can be used for authorizing an Entra user with a Teams license to use Teams Phone Extensibility features through your Azure Communication Services resource.
115
+ This requires providing the `https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls` scope.
116
+
117
+ ```typescript
118
+ const options: InteractiveBrowserCredentialInBrowserOptions = {
119
+ tenantId: "<your-tenant-id>",
120
+ clientId: "<your-client-id>",
121
+ redirectUri: "<your-redirect-uri>",
122
+ };
123
+ const entraTokenCredential = new InteractiveBrowserCredential(options);
124
+
125
+ const entraTokenCredentialOptions: EntraCommunicationTokenCredentialOptions = {
126
+ resourceEndpoint: "https://<your-resource>.communication.azure.com",
127
+ tokenCredential: entraTokenCredential,
128
+ scopes: ["https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls"]
129
+ };
130
+
131
+ const credential = new AzureCommunicationTokenCredential(entraTokenCredentialOptions);
132
+ ```
85
133
  ## Troubleshooting
86
134
 
87
135
  - **Invalid token specified**: Make sure the token you are passing to the `AzureCommunicationTokenCredential` constructor or to the `tokenRefresher` callback is a bare JWT token string. E.g. if you're using the [Azure Communication Identity library][invalid_token_sdk] or [REST API][invalid_token_rest] to obtain the token, make sure you're passing just the `token` part of the response object.
@@ -1,6 +1,7 @@
1
1
  import { type CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
2
2
  import type { CommunicationGetTokenOptions, CommunicationTokenCredential } from "./communicationTokenCredential.js";
3
3
  import type { AccessToken } from "@azure/core-auth";
4
+ import { type EntraCommunicationTokenCredentialOptions } from "./entraTokenCredential.js";
4
5
  /**
5
6
  * The CommunicationTokenCredential implementation with support for proactive token refresh.
6
7
  */
@@ -18,6 +19,11 @@ export declare class AzureCommunicationTokenCredential implements CommunicationT
18
19
  * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.
19
20
  */
20
21
  constructor(refreshOptions: CommunicationTokenRefreshOptions);
22
+ /**
23
+ * Creates an instance of CommunicationTokenCredential with an Entra ID token credential. In most cases, you might want to use InteractiveBrowserCredential to sign in your user.
24
+ * @param entraOptions - Options to configure the Entra ID token credential.
25
+ */
26
+ constructor(entraOptions: EntraCommunicationTokenCredentialOptions);
21
27
  /**
22
28
  * Gets an `AccessToken` for the user. Throws if already disposed.
23
29
  * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.
@@ -1 +1 @@
1
- {"version":3,"file":"azureCommunicationTokenCredential.d.ts","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAE7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;GAEG;AAEH,qBAAa,iCAAkC,YAAW,4BAA4B;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;OAGG;gBACS,KAAK,EAAE,MAAM;IACzB;;;;OAIG;gBACS,cAAc,EAAE,gCAAgC;IAS5D;;;OAGG;IACU,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOnF;;OAEG;IACI,OAAO,IAAI,IAAI;IAKtB,OAAO,CAAC,eAAe;CAKxB"}
1
+ {"version":3,"file":"azureCommunicationTokenCredential.d.ts","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAE7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EACL,KAAK,wCAAwC,EAE9C,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AAEH,qBAAa,iCAAkC,YAAW,4BAA4B;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;OAGG;gBACS,KAAK,EAAE,MAAM;IACzB;;;;OAIG;gBACS,cAAc,EAAE,gCAAgC;IAC5D;;;OAGG;gBACS,YAAY,EAAE,wCAAwC;IAkBlE;;;OAGG;IACU,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOnF;;OAEG;IACI,OAAO,IAAI,IAAI;IAKtB,OAAO,CAAC,eAAe;CAKxB"}
@@ -3,17 +3,21 @@
3
3
  import { AutoRefreshTokenCredential, } from "./autoRefreshTokenCredential.js";
4
4
  import { StaticTokenCredential } from "./staticTokenCredential.js";
5
5
  import { parseToken } from "./tokenParser.js";
6
+ import { EntraTokenCredential, } from "./entraTokenCredential.js";
6
7
  /**
7
8
  * The CommunicationTokenCredential implementation with support for proactive token refresh.
8
9
  */
9
10
  export class AzureCommunicationTokenCredential {
10
- constructor(tokenOrRefreshOptions) {
11
+ constructor(tokenOrRefreshOptionsOrEntraOptions) {
11
12
  this.disposed = false;
12
- if (typeof tokenOrRefreshOptions === "string") {
13
- this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptions));
13
+ if (typeof tokenOrRefreshOptionsOrEntraOptions === "string") {
14
+ this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptionsOrEntraOptions));
15
+ }
16
+ else if ("tokenRefresher" in tokenOrRefreshOptionsOrEntraOptions) {
17
+ this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
14
18
  }
15
19
  else {
16
- this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptions);
20
+ this.tokenCredential = new EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
17
21
  }
18
22
  }
19
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"azureCommunicationTokenCredential.js","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EACL,0BAA0B,GAE3B,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;GAEG;AAEH,MAAM,OAAO,iCAAiC;IAe5C,YAAY,qBAAgE;QAbpE,aAAQ,GAAG,KAAK,CAAC;QAcvB,IAAI,OAAO,qBAAqB,KAAK,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC,eAAe,GAAG,IAAI,qBAAqB,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC;QACtF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,GAAG,IAAI,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,OAAsC;QAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n AutoRefreshTokenCredential,\n type CommunicationTokenRefreshOptions,\n} from \"./autoRefreshTokenCredential.js\";\nimport type {\n CommunicationGetTokenOptions,\n CommunicationTokenCredential,\n TokenCredential,\n} from \"./communicationTokenCredential.js\";\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { StaticTokenCredential } from \"./staticTokenCredential.js\";\nimport { parseToken } from \"./tokenParser.js\";\n\n/**\n * The CommunicationTokenCredential implementation with support for proactive token refresh.\n */\n\nexport class AzureCommunicationTokenCredential implements CommunicationTokenCredential {\n private readonly tokenCredential: TokenCredential;\n private disposed = false;\n\n /**\n * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.\n * @param token - A user access token issued by Communication Services.\n */\n constructor(token: string);\n /**\n * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options\n * to configure proactive refreshing.\n * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.\n */\n constructor(refreshOptions: CommunicationTokenRefreshOptions);\n constructor(tokenOrRefreshOptions: string | CommunicationTokenRefreshOptions) {\n if (typeof tokenOrRefreshOptions === \"string\") {\n this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptions));\n } else {\n this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptions);\n }\n }\n\n /**\n * Gets an `AccessToken` for the user. Throws if already disposed.\n * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.\n */\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n this.throwIfDisposed();\n const token = await this.tokenCredential.getToken(options);\n this.throwIfDisposed();\n return token;\n }\n\n /**\n * Disposes the CommunicationTokenCredential and cancels any internal auto-refresh operation.\n */\n public dispose(): void {\n this.disposed = true;\n this.tokenCredential.dispose();\n }\n\n private throwIfDisposed(): void {\n if (this.disposed) {\n throw new Error(\"User credential is disposed\");\n }\n }\n}\n"]}
1
+ {"version":3,"file":"azureCommunicationTokenCredential.js","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EACL,0BAA0B,GAE3B,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAEL,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AAEH,MAAM,OAAO,iCAAiC;IAoB5C,YACE,mCAG4C;QAtBtC,aAAQ,GAAG,KAAK,CAAC;QAwBvB,IAAI,OAAO,mCAAmC,KAAK,QAAQ,EAAE,CAAC;YAC5D,IAAI,CAAC,eAAe,GAAG,IAAI,qBAAqB,CAC9C,UAAU,CAAC,mCAAmC,CAAC,CAChD,CAAC;QACJ,CAAC;aAAM,IAAI,gBAAgB,IAAI,mCAAmC,EAAE,CAAC;YACnE,IAAI,CAAC,eAAe,GAAG,IAAI,0BAA0B,CAAC,mCAAmC,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,GAAG,IAAI,oBAAoB,CAAC,mCAAmC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,OAAsC;QAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n AutoRefreshTokenCredential,\n type CommunicationTokenRefreshOptions,\n} from \"./autoRefreshTokenCredential.js\";\nimport type {\n CommunicationGetTokenOptions,\n CommunicationTokenCredential,\n TokenCredential,\n} from \"./communicationTokenCredential.js\";\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { StaticTokenCredential } from \"./staticTokenCredential.js\";\nimport { parseToken } from \"./tokenParser.js\";\nimport {\n type EntraCommunicationTokenCredentialOptions,\n EntraTokenCredential,\n} from \"./entraTokenCredential.js\";\n\n/**\n * The CommunicationTokenCredential implementation with support for proactive token refresh.\n */\n\nexport class AzureCommunicationTokenCredential implements CommunicationTokenCredential {\n private readonly tokenCredential: TokenCredential;\n private disposed = false;\n\n /**\n * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.\n * @param token - A user access token issued by Communication Services.\n */\n constructor(token: string);\n /**\n * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options\n * to configure proactive refreshing.\n * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.\n */\n constructor(refreshOptions: CommunicationTokenRefreshOptions);\n /**\n * Creates an instance of CommunicationTokenCredential with an Entra ID token credential. In most cases, you might want to use InteractiveBrowserCredential to sign in your user.\n * @param entraOptions - Options to configure the Entra ID token credential.\n */\n constructor(entraOptions: EntraCommunicationTokenCredentialOptions);\n constructor(\n tokenOrRefreshOptionsOrEntraOptions:\n | string\n | CommunicationTokenRefreshOptions\n | EntraCommunicationTokenCredentialOptions,\n ) {\n if (typeof tokenOrRefreshOptionsOrEntraOptions === \"string\") {\n this.tokenCredential = new StaticTokenCredential(\n parseToken(tokenOrRefreshOptionsOrEntraOptions),\n );\n } else if (\"tokenRefresher\" in tokenOrRefreshOptionsOrEntraOptions) {\n this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);\n } else {\n this.tokenCredential = new EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);\n }\n }\n\n /**\n * Gets an `AccessToken` for the user. Throws if already disposed.\n * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.\n */\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n this.throwIfDisposed();\n const token = await this.tokenCredential.getToken(options);\n this.throwIfDisposed();\n return token;\n }\n\n /**\n * Disposes the CommunicationTokenCredential and cancels any internal auto-refresh operation.\n */\n public dispose(): void {\n this.disposed = true;\n this.tokenCredential.dispose();\n }\n\n private throwIfDisposed(): void {\n if (this.disposed) {\n throw new Error(\"User credential is disposed\");\n }\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { type AccessToken } from "@azure/core-auth";
2
+ import { type TokenCredential } from "@azure/identity";
3
+ import { type TokenCredential as AcsTokenCredential, type CommunicationGetTokenOptions } from "./communicationTokenCredential.js";
4
+ export interface ExchangeTokenResponse {
5
+ identity: string;
6
+ accessToken: {
7
+ token: string;
8
+ expiresOn: string;
9
+ };
10
+ }
11
+ /**
12
+ * The Entra Communication Token Options.
13
+ */
14
+ export interface EntraCommunicationTokenCredentialOptions {
15
+ /**
16
+ * The Azure Communication Service resource endpoint URL, e.g. https://myResource.communication.azure.com.
17
+ */
18
+ resourceEndpoint: string;
19
+ /**
20
+ * The Entra ID token credential.
21
+ */
22
+ tokenCredential: TokenCredential;
23
+ /**
24
+ * The scopes for retrieving the Entra ID access token.
25
+ */
26
+ scopes?: string[];
27
+ }
28
+ /**
29
+ * Represents a credential that exchanges an Entra token for an Azure Communication Services (ACS) token, enabling access to ACS resources.
30
+ */
31
+ export declare class EntraTokenCredential implements AcsTokenCredential {
32
+ private options;
33
+ private isPending;
34
+ private result;
35
+ private client;
36
+ private httpClient;
37
+ constructor(options: EntraCommunicationTokenCredentialOptions);
38
+ getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken>;
39
+ private getTokenInternal;
40
+ dispose(): void;
41
+ private exchangeEntraToken;
42
+ private createRequestUri;
43
+ private determineEndpointAndApiVersion;
44
+ }
45
+ //# sourceMappingURL=entraTokenCredential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entraTokenCredential.d.ts","sourceRoot":"","sources":["../../src/entraTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,KAAK,eAAe,IAAI,kBAAkB,EAC1C,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAC;AAiB3C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,wCAAwC;IACvD;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,eAAe,EAAE,eAAe,CAAC;IACjC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,kBAAkB;IASjD,OAAO,CAAC,OAAO;IAR3B,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,MAAM,CAGZ;IACF,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAa;gBAEX,OAAO,EAAE,wCAAwC;IAYxD,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;YAoBrE,gBAAgB;IAmCvB,OAAO,IAAI,IAAI;YAOR,kBAAkB;IA8BhC,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,8BAA8B;CAiBvC"}
@@ -0,0 +1,120 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { getClient } from "@azure-rest/core-client";
4
+ import { createDefaultHttpClient, createHttpHeaders, createPipelineRequest, } from "@azure/core-rest-pipeline";
5
+ const TeamsExtensionScopePrefix = "https://auth.msft.communication.azure.com/";
6
+ const ComunicationClientsScopePrefix = "https://communication.azure.com/clients/";
7
+ const TeamsExtensionEndpoint = "/access/teamsPhone/:exchangeAccessToken";
8
+ const TeamsExtensionApiVersion = "2025-03-02-preview";
9
+ const ComunicationClientsEndpoint = "/access/entra/:exchangeAccessToken";
10
+ const ComunicationClientsApiVersion = "2024-04-01-preview";
11
+ /**
12
+ * Represents a credential that exchanges an Entra token for an Azure Communication Services (ACS) token, enabling access to ACS resources.
13
+ */
14
+ export class EntraTokenCredential {
15
+ constructor(options) {
16
+ this.options = options;
17
+ this.result = {
18
+ entraToken: undefined,
19
+ acsToken: { token: "", expiresOnTimestamp: 0 },
20
+ };
21
+ this.client = getClient(options.resourceEndpoint);
22
+ this.httpClient = createDefaultHttpClient();
23
+ this.options = options;
24
+ this.options.scopes = this.options.scopes || [
25
+ "https://communication.azure.com/clients/.default",
26
+ ];
27
+ // immediately fetch the token to pre-warm
28
+ this.isPending = this.getToken();
29
+ }
30
+ async getToken(options) {
31
+ var _a;
32
+ if ((_a = options === null || options === void 0 ? void 0 : options.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) {
33
+ return { token: "", expiresOnTimestamp: 0 };
34
+ }
35
+ // we're awaiting the token fetch, so we don't want to start another one
36
+ // however, we're ignoring the new abortSignal, unfortunately
37
+ if (!this.isPending) {
38
+ this.isPending = this.getTokenInternal(options);
39
+ }
40
+ try {
41
+ await this.isPending;
42
+ }
43
+ finally {
44
+ this.isPending = null;
45
+ }
46
+ return this.result.acsToken;
47
+ }
48
+ async getTokenInternal(options) {
49
+ const getTokenOptions = (options === null || options === void 0 ? void 0 : options.abortSignal) ? { abortSignal: options.abortSignal } : undefined;
50
+ const token = await this.options.tokenCredential.getToken(this.options.scopes
51
+ ? this.options.scopes
52
+ : ["https://communication.azure.com/clients/.default"], getTokenOptions);
53
+ const currentDateTime = new Date();
54
+ const tokenExpiresOn = new Date(this.result.acsToken.expiresOnTimestamp);
55
+ if (token === null) {
56
+ this.result = {
57
+ entraToken: undefined,
58
+ acsToken: { token: "", expiresOnTimestamp: 0 },
59
+ };
60
+ }
61
+ else if (this.result.acsToken.token === "" ||
62
+ token.token !== this.result.entraToken ||
63
+ tokenExpiresOn < currentDateTime) {
64
+ const acsToken = await this.exchangeEntraToken(this.options.resourceEndpoint, token.token, getTokenOptions);
65
+ this.result = {
66
+ entraToken: token.token,
67
+ acsToken,
68
+ };
69
+ }
70
+ return this.result.acsToken;
71
+ }
72
+ dispose() {
73
+ this.result = {
74
+ entraToken: undefined,
75
+ acsToken: { token: "", expiresOnTimestamp: 0 },
76
+ };
77
+ }
78
+ async exchangeEntraToken(resourceEndpoint, entraToken, options) {
79
+ const request = createPipelineRequest({
80
+ url: this.createRequestUri(resourceEndpoint),
81
+ method: "POST",
82
+ headers: createHttpHeaders({
83
+ Authorization: `Bearer ${entraToken}`,
84
+ "Content-Type": "application/json",
85
+ Accept: "application/json",
86
+ }),
87
+ abortSignal: options === null || options === void 0 ? void 0 : options.abortSignal,
88
+ body: JSON.stringify({}),
89
+ });
90
+ const response = await this.client.pipeline.sendRequest(this.httpClient, request);
91
+ if (response.status !== 200 || !response.bodyAsText) {
92
+ throw new Error(`Service request failed. Status: ${response.status}, Body: ${response.bodyAsText}`);
93
+ }
94
+ const json = JSON.parse(response.bodyAsText);
95
+ return {
96
+ token: json.accessToken.token,
97
+ expiresOnTimestamp: Date.parse(json.accessToken.expiresOn),
98
+ };
99
+ }
100
+ createRequestUri(resourceEndpoint) {
101
+ const [endpoint, apiVersion] = this.determineEndpointAndApiVersion();
102
+ const requestUri = `${resourceEndpoint}${endpoint}?api-version=${apiVersion}`;
103
+ return requestUri;
104
+ }
105
+ determineEndpointAndApiVersion() {
106
+ if (!this.options.scopes || this.options.scopes.length === 0) {
107
+ throw new Error(`Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`);
108
+ }
109
+ else if (this.options.scopes.every((scope) => scope.startsWith(TeamsExtensionScopePrefix))) {
110
+ return [TeamsExtensionEndpoint, TeamsExtensionApiVersion];
111
+ }
112
+ else if (this.options.scopes.every((scope) => scope.startsWith(ComunicationClientsScopePrefix))) {
113
+ return [ComunicationClientsEndpoint, ComunicationClientsApiVersion];
114
+ }
115
+ else {
116
+ throw new Error(`Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`);
117
+ }
118
+ }
119
+ }
120
+ //# sourceMappingURL=entraTokenCredential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entraTokenCredential.js","sourceRoot":"","sources":["../../src/entraTokenCredential.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { type AccessToken } from \"@azure/core-auth\";\nimport { type TokenCredential } from \"@azure/identity\";\nimport {\n type TokenCredential as AcsTokenCredential,\n type CommunicationGetTokenOptions,\n} from \"./communicationTokenCredential.js\";\nimport { type AbortSignalLike } from \"@azure/abort-controller\";\nimport { type Client, getClient } from \"@azure-rest/core-client\";\nimport {\n type HttpClient,\n createDefaultHttpClient,\n createHttpHeaders,\n createPipelineRequest,\n} from \"@azure/core-rest-pipeline\";\n\nconst TeamsExtensionScopePrefix = \"https://auth.msft.communication.azure.com/\";\nconst ComunicationClientsScopePrefix = \"https://communication.azure.com/clients/\";\nconst TeamsExtensionEndpoint = \"/access/teamsPhone/:exchangeAccessToken\";\nconst TeamsExtensionApiVersion = \"2025-03-02-preview\";\nconst ComunicationClientsEndpoint = \"/access/entra/:exchangeAccessToken\";\nconst ComunicationClientsApiVersion = \"2024-04-01-preview\";\n\nexport interface ExchangeTokenResponse {\n identity: string;\n accessToken: {\n token: string;\n expiresOn: string;\n };\n}\n\n/**\n * The Entra Communication Token Options.\n */\nexport interface EntraCommunicationTokenCredentialOptions {\n /**\n * The Azure Communication Service resource endpoint URL, e.g. https://myResource.communication.azure.com.\n */\n resourceEndpoint: string;\n /**\n * The Entra ID token credential.\n */\n tokenCredential: TokenCredential;\n /**\n * The scopes for retrieving the Entra ID access token.\n */\n scopes?: string[];\n}\n\n/**\n * Represents a credential that exchanges an Entra token for an Azure Communication Services (ACS) token, enabling access to ACS resources.\n */\nexport class EntraTokenCredential implements AcsTokenCredential {\n private isPending: Promise<AccessToken> | null;\n private result = {\n entraToken: undefined as string | undefined,\n acsToken: { token: \"\", expiresOnTimestamp: 0 },\n };\n private client: Client;\n private httpClient: HttpClient;\n\n constructor(private options: EntraCommunicationTokenCredentialOptions) {\n this.client = getClient(options.resourceEndpoint);\n this.httpClient = createDefaultHttpClient();\n this.options = options;\n this.options.scopes = this.options.scopes || [\n \"https://communication.azure.com/clients/.default\",\n ];\n\n // immediately fetch the token to pre-warm\n this.isPending = this.getToken();\n }\n\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n if (options?.abortSignal?.aborted) {\n return { token: \"\", expiresOnTimestamp: 0 };\n }\n\n // we're awaiting the token fetch, so we don't want to start another one\n // however, we're ignoring the new abortSignal, unfortunately\n if (!this.isPending) {\n this.isPending = this.getTokenInternal(options);\n }\n\n try {\n await this.isPending;\n } finally {\n this.isPending = null;\n }\n\n return this.result.acsToken;\n }\n\n private async getTokenInternal(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n const getTokenOptions = options?.abortSignal ? { abortSignal: options.abortSignal } : undefined;\n const token = await this.options.tokenCredential.getToken(\n this.options.scopes\n ? this.options.scopes\n : [\"https://communication.azure.com/clients/.default\"],\n getTokenOptions,\n );\n const currentDateTime = new Date();\n const tokenExpiresOn = new Date(this.result.acsToken.expiresOnTimestamp);\n\n if (token === null) {\n this.result = {\n entraToken: undefined,\n acsToken: { token: \"\", expiresOnTimestamp: 0 },\n };\n } else if (\n this.result.acsToken.token === \"\" ||\n token.token !== this.result.entraToken ||\n tokenExpiresOn < currentDateTime\n ) {\n const acsToken = await this.exchangeEntraToken(\n this.options.resourceEndpoint,\n token.token,\n getTokenOptions,\n );\n this.result = {\n entraToken: token.token,\n acsToken,\n };\n }\n\n return this.result.acsToken;\n }\n\n public dispose(): void {\n this.result = {\n entraToken: undefined,\n acsToken: { token: \"\", expiresOnTimestamp: 0 },\n };\n }\n\n private async exchangeEntraToken(\n resourceEndpoint: string,\n entraToken: string,\n options?: { abortSignal: AbortSignalLike },\n ): Promise<AccessToken> {\n const request = createPipelineRequest({\n url: this.createRequestUri(resourceEndpoint),\n method: \"POST\",\n headers: createHttpHeaders({\n Authorization: `Bearer ${entraToken}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n }),\n abortSignal: options?.abortSignal,\n body: JSON.stringify({}),\n });\n const response = await this.client.pipeline.sendRequest(this.httpClient, request);\n\n if (response.status !== 200 || !response.bodyAsText) {\n throw new Error(\n `Service request failed. Status: ${response.status}, Body: ${response.bodyAsText}`,\n );\n }\n const json = JSON.parse(response.bodyAsText) as ExchangeTokenResponse;\n return {\n token: json.accessToken.token,\n expiresOnTimestamp: Date.parse(json.accessToken.expiresOn),\n };\n }\n\n private createRequestUri(resourceEndpoint: string): string {\n const [endpoint, apiVersion] = this.determineEndpointAndApiVersion();\n const requestUri = `${resourceEndpoint}${endpoint}?api-version=${apiVersion}`;\n return requestUri;\n }\n\n private determineEndpointAndApiVersion(): [string, string] {\n if (!this.options.scopes || this.options.scopes.length === 0) {\n throw new Error(\n `Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`,\n );\n } else if (this.options.scopes.every((scope) => scope.startsWith(TeamsExtensionScopePrefix))) {\n return [TeamsExtensionEndpoint, TeamsExtensionApiVersion];\n } else if (\n this.options.scopes.every((scope) => scope.startsWith(ComunicationClientsScopePrefix))\n ) {\n return [ComunicationClientsEndpoint, ComunicationClientsApiVersion];\n } else {\n throw new Error(\n `Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`,\n );\n }\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  export { CommunicationTokenCredential, CommunicationGetTokenOptions, } from "./communicationTokenCredential.js";
2
2
  export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCredential.js";
3
- export * from "./credential/index.js";
4
3
  export { CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
4
+ export { EntraCommunicationTokenCredentialOptions } from "./entraTokenCredential.js";
5
5
  export * from "./credential/index.js";
6
6
  export * from "./identifierModels.js";
7
7
  export * from "./identifierModelSerializer.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,iCAAiC,EAAE,MAAM,wCAAwC,CAAC;AAC3F,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AACnF,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,iCAAiC,EAAE,MAAM,wCAAwC,CAAC;AAC3F,OAAO,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,wCAAwC,EAAE,MAAM,2BAA2B,CAAC;AACrF,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC"}
@@ -2,7 +2,6 @@
2
2
  // Licensed under the MIT License.
3
3
  export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCredential.js";
4
4
  export * from "./credential/index.js";
5
- export * from "./credential/index.js";
6
5
  export * from "./identifierModels.js";
7
6
  export * from "./identifierModelSerializer.js";
8
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAMlC,OAAO,EAAE,iCAAiC,EAAE,MAAM,wCAAwC,CAAC;AAC3F,cAAc,uBAAuB,CAAC;AAEtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nexport {\n CommunicationTokenCredential,\n CommunicationGetTokenOptions,\n} from \"./communicationTokenCredential.js\";\nexport { AzureCommunicationTokenCredential } from \"./azureCommunicationTokenCredential.js\";\nexport * from \"./credential/index.js\";\nexport { CommunicationTokenRefreshOptions } from \"./autoRefreshTokenCredential.js\";\nexport * from \"./credential/index.js\";\nexport * from \"./identifierModels.js\";\nexport * from \"./identifierModelSerializer.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAMlC,OAAO,EAAE,iCAAiC,EAAE,MAAM,wCAAwC,CAAC;AAG3F,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nexport {\n CommunicationTokenCredential,\n CommunicationGetTokenOptions,\n} from \"./communicationTokenCredential.js\";\nexport { AzureCommunicationTokenCredential } from \"./azureCommunicationTokenCredential.js\";\nexport { CommunicationTokenRefreshOptions } from \"./autoRefreshTokenCredential.js\";\nexport { EntraCommunicationTokenCredentialOptions } from \"./entraTokenCredential.js\";\nexport * from \"./credential/index.js\";\nexport * from \"./identifierModels.js\";\nexport * from \"./identifierModelSerializer.js\";\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
2
2
  import type { CommunicationGetTokenOptions, CommunicationTokenCredential } from "./communicationTokenCredential.js";
3
3
  import type { AccessToken } from "@azure/core-auth";
4
+ import { type EntraCommunicationTokenCredentialOptions } from "./entraTokenCredential.js";
4
5
  /**
5
6
  * The CommunicationTokenCredential implementation with support for proactive token refresh.
6
7
  */
@@ -18,6 +19,11 @@ export declare class AzureCommunicationTokenCredential implements CommunicationT
18
19
  * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.
19
20
  */
20
21
  constructor(refreshOptions: CommunicationTokenRefreshOptions);
22
+ /**
23
+ * Creates an instance of CommunicationTokenCredential with an Entra ID token credential. In most cases, you might want to use InteractiveBrowserCredential to sign in your user.
24
+ * @param entraOptions - Options to configure the Entra ID token credential.
25
+ */
26
+ constructor(entraOptions: EntraCommunicationTokenCredentialOptions);
21
27
  /**
22
28
  * Gets an `AccessToken` for the user. Throws if already disposed.
23
29
  * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.
@@ -1 +1 @@
1
- {"version":3,"file":"azureCommunicationTokenCredential.d.ts","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAE7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;GAEG;AAEH,qBAAa,iCAAkC,YAAW,4BAA4B;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;OAGG;gBACS,KAAK,EAAE,MAAM;IACzB;;;;OAIG;gBACS,cAAc,EAAE,gCAAgC;IAS5D;;;OAGG;IACU,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOnF;;OAEG;IACI,OAAO,IAAI,IAAI;IAKtB,OAAO,CAAC,eAAe;CAKxB"}
1
+ {"version":3,"file":"azureCommunicationTokenCredential.d.ts","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAE7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EACL,KAAK,wCAAwC,EAE9C,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AAEH,qBAAa,iCAAkC,YAAW,4BAA4B;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;OAGG;gBACS,KAAK,EAAE,MAAM;IACzB;;;;OAIG;gBACS,cAAc,EAAE,gCAAgC;IAC5D;;;OAGG;gBACS,YAAY,EAAE,wCAAwC;IAkBlE;;;OAGG;IACU,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOnF;;OAEG;IACI,OAAO,IAAI,IAAI;IAKtB,OAAO,CAAC,eAAe;CAKxB"}
@@ -6,17 +6,21 @@ exports.AzureCommunicationTokenCredential = void 0;
6
6
  const autoRefreshTokenCredential_js_1 = require("./autoRefreshTokenCredential.js");
7
7
  const staticTokenCredential_js_1 = require("./staticTokenCredential.js");
8
8
  const tokenParser_js_1 = require("./tokenParser.js");
9
+ const entraTokenCredential_js_1 = require("./entraTokenCredential.js");
9
10
  /**
10
11
  * The CommunicationTokenCredential implementation with support for proactive token refresh.
11
12
  */
12
13
  class AzureCommunicationTokenCredential {
13
- constructor(tokenOrRefreshOptions) {
14
+ constructor(tokenOrRefreshOptionsOrEntraOptions) {
14
15
  this.disposed = false;
15
- if (typeof tokenOrRefreshOptions === "string") {
16
- this.tokenCredential = new staticTokenCredential_js_1.StaticTokenCredential((0, tokenParser_js_1.parseToken)(tokenOrRefreshOptions));
16
+ if (typeof tokenOrRefreshOptionsOrEntraOptions === "string") {
17
+ this.tokenCredential = new staticTokenCredential_js_1.StaticTokenCredential((0, tokenParser_js_1.parseToken)(tokenOrRefreshOptionsOrEntraOptions));
18
+ }
19
+ else if ("tokenRefresher" in tokenOrRefreshOptionsOrEntraOptions) {
20
+ this.tokenCredential = new autoRefreshTokenCredential_js_1.AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
17
21
  }
18
22
  else {
19
- this.tokenCredential = new autoRefreshTokenCredential_js_1.AutoRefreshTokenCredential(tokenOrRefreshOptions);
23
+ this.tokenCredential = new entraTokenCredential_js_1.EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
20
24
  }
21
25
  }
22
26
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"azureCommunicationTokenCredential.js","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;;AAElC,mFAGyC;AAOzC,yEAAmE;AACnE,qDAA8C;AAE9C;;GAEG;AAEH,MAAa,iCAAiC;IAe5C,YAAY,qBAAgE;QAbpE,aAAQ,GAAG,KAAK,CAAC;QAcvB,IAAI,OAAO,qBAAqB,KAAK,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC,eAAe,GAAG,IAAI,gDAAqB,CAAC,IAAA,2BAAU,EAAC,qBAAqB,CAAC,CAAC,CAAC;QACtF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,GAAG,IAAI,0DAA0B,CAAC,qBAAqB,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,OAAsC;QAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF;AA/CD,8EA+CC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n AutoRefreshTokenCredential,\n type CommunicationTokenRefreshOptions,\n} from \"./autoRefreshTokenCredential.js\";\nimport type {\n CommunicationGetTokenOptions,\n CommunicationTokenCredential,\n TokenCredential,\n} from \"./communicationTokenCredential.js\";\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { StaticTokenCredential } from \"./staticTokenCredential.js\";\nimport { parseToken } from \"./tokenParser.js\";\n\n/**\n * The CommunicationTokenCredential implementation with support for proactive token refresh.\n */\n\nexport class AzureCommunicationTokenCredential implements CommunicationTokenCredential {\n private readonly tokenCredential: TokenCredential;\n private disposed = false;\n\n /**\n * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.\n * @param token - A user access token issued by Communication Services.\n */\n constructor(token: string);\n /**\n * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options\n * to configure proactive refreshing.\n * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.\n */\n constructor(refreshOptions: CommunicationTokenRefreshOptions);\n constructor(tokenOrRefreshOptions: string | CommunicationTokenRefreshOptions) {\n if (typeof tokenOrRefreshOptions === \"string\") {\n this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptions));\n } else {\n this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptions);\n }\n }\n\n /**\n * Gets an `AccessToken` for the user. Throws if already disposed.\n * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.\n */\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n this.throwIfDisposed();\n const token = await this.tokenCredential.getToken(options);\n this.throwIfDisposed();\n return token;\n }\n\n /**\n * Disposes the CommunicationTokenCredential and cancels any internal auto-refresh operation.\n */\n public dispose(): void {\n this.disposed = true;\n this.tokenCredential.dispose();\n }\n\n private throwIfDisposed(): void {\n if (this.disposed) {\n throw new Error(\"User credential is disposed\");\n }\n }\n}\n"]}
1
+ {"version":3,"file":"azureCommunicationTokenCredential.js","sourceRoot":"","sources":["../../src/azureCommunicationTokenCredential.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;;AAElC,mFAGyC;AAOzC,yEAAmE;AACnE,qDAA8C;AAC9C,uEAGmC;AAEnC;;GAEG;AAEH,MAAa,iCAAiC;IAoB5C,YACE,mCAG4C;QAtBtC,aAAQ,GAAG,KAAK,CAAC;QAwBvB,IAAI,OAAO,mCAAmC,KAAK,QAAQ,EAAE,CAAC;YAC5D,IAAI,CAAC,eAAe,GAAG,IAAI,gDAAqB,CAC9C,IAAA,2BAAU,EAAC,mCAAmC,CAAC,CAChD,CAAC;QACJ,CAAC;aAAM,IAAI,gBAAgB,IAAI,mCAAmC,EAAE,CAAC;YACnE,IAAI,CAAC,eAAe,GAAG,IAAI,0DAA0B,CAAC,mCAAmC,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,GAAG,IAAI,8CAAoB,CAAC,mCAAmC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,OAAsC;QAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF;AA7DD,8EA6DC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n AutoRefreshTokenCredential,\n type CommunicationTokenRefreshOptions,\n} from \"./autoRefreshTokenCredential.js\";\nimport type {\n CommunicationGetTokenOptions,\n CommunicationTokenCredential,\n TokenCredential,\n} from \"./communicationTokenCredential.js\";\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { StaticTokenCredential } from \"./staticTokenCredential.js\";\nimport { parseToken } from \"./tokenParser.js\";\nimport {\n type EntraCommunicationTokenCredentialOptions,\n EntraTokenCredential,\n} from \"./entraTokenCredential.js\";\n\n/**\n * The CommunicationTokenCredential implementation with support for proactive token refresh.\n */\n\nexport class AzureCommunicationTokenCredential implements CommunicationTokenCredential {\n private readonly tokenCredential: TokenCredential;\n private disposed = false;\n\n /**\n * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.\n * @param token - A user access token issued by Communication Services.\n */\n constructor(token: string);\n /**\n * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options\n * to configure proactive refreshing.\n * @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.\n */\n constructor(refreshOptions: CommunicationTokenRefreshOptions);\n /**\n * Creates an instance of CommunicationTokenCredential with an Entra ID token credential. In most cases, you might want to use InteractiveBrowserCredential to sign in your user.\n * @param entraOptions - Options to configure the Entra ID token credential.\n */\n constructor(entraOptions: EntraCommunicationTokenCredentialOptions);\n constructor(\n tokenOrRefreshOptionsOrEntraOptions:\n | string\n | CommunicationTokenRefreshOptions\n | EntraCommunicationTokenCredentialOptions,\n ) {\n if (typeof tokenOrRefreshOptionsOrEntraOptions === \"string\") {\n this.tokenCredential = new StaticTokenCredential(\n parseToken(tokenOrRefreshOptionsOrEntraOptions),\n );\n } else if (\"tokenRefresher\" in tokenOrRefreshOptionsOrEntraOptions) {\n this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);\n } else {\n this.tokenCredential = new EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);\n }\n }\n\n /**\n * Gets an `AccessToken` for the user. Throws if already disposed.\n * @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.\n */\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n this.throwIfDisposed();\n const token = await this.tokenCredential.getToken(options);\n this.throwIfDisposed();\n return token;\n }\n\n /**\n * Disposes the CommunicationTokenCredential and cancels any internal auto-refresh operation.\n */\n public dispose(): void {\n this.disposed = true;\n this.tokenCredential.dispose();\n }\n\n private throwIfDisposed(): void {\n if (this.disposed) {\n throw new Error(\"User credential is disposed\");\n }\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { type AccessToken } from "@azure/core-auth";
2
+ import { type TokenCredential } from "@azure/identity";
3
+ import { type TokenCredential as AcsTokenCredential, type CommunicationGetTokenOptions } from "./communicationTokenCredential.js";
4
+ export interface ExchangeTokenResponse {
5
+ identity: string;
6
+ accessToken: {
7
+ token: string;
8
+ expiresOn: string;
9
+ };
10
+ }
11
+ /**
12
+ * The Entra Communication Token Options.
13
+ */
14
+ export interface EntraCommunicationTokenCredentialOptions {
15
+ /**
16
+ * The Azure Communication Service resource endpoint URL, e.g. https://myResource.communication.azure.com.
17
+ */
18
+ resourceEndpoint: string;
19
+ /**
20
+ * The Entra ID token credential.
21
+ */
22
+ tokenCredential: TokenCredential;
23
+ /**
24
+ * The scopes for retrieving the Entra ID access token.
25
+ */
26
+ scopes?: string[];
27
+ }
28
+ /**
29
+ * Represents a credential that exchanges an Entra token for an Azure Communication Services (ACS) token, enabling access to ACS resources.
30
+ */
31
+ export declare class EntraTokenCredential implements AcsTokenCredential {
32
+ private options;
33
+ private isPending;
34
+ private result;
35
+ private client;
36
+ private httpClient;
37
+ constructor(options: EntraCommunicationTokenCredentialOptions);
38
+ getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken>;
39
+ private getTokenInternal;
40
+ dispose(): void;
41
+ private exchangeEntraToken;
42
+ private createRequestUri;
43
+ private determineEndpointAndApiVersion;
44
+ }
45
+ //# sourceMappingURL=entraTokenCredential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entraTokenCredential.d.ts","sourceRoot":"","sources":["../../src/entraTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,KAAK,eAAe,IAAI,kBAAkB,EAC1C,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAC;AAiB3C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,wCAAwC;IACvD;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,eAAe,EAAE,eAAe,CAAC;IACjC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,kBAAkB;IASjD,OAAO,CAAC,OAAO;IAR3B,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,MAAM,CAGZ;IACF,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAa;gBAEX,OAAO,EAAE,wCAAwC;IAYxD,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;YAoBrE,gBAAgB;IAmCvB,OAAO,IAAI,IAAI;YAOR,kBAAkB;IA8BhC,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,8BAA8B;CAiBvC"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation.
3
+ // Licensed under the MIT License.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.EntraTokenCredential = void 0;
6
+ const core_client_1 = require("@azure-rest/core-client");
7
+ const core_rest_pipeline_1 = require("@azure/core-rest-pipeline");
8
+ const TeamsExtensionScopePrefix = "https://auth.msft.communication.azure.com/";
9
+ const ComunicationClientsScopePrefix = "https://communication.azure.com/clients/";
10
+ const TeamsExtensionEndpoint = "/access/teamsPhone/:exchangeAccessToken";
11
+ const TeamsExtensionApiVersion = "2025-03-02-preview";
12
+ const ComunicationClientsEndpoint = "/access/entra/:exchangeAccessToken";
13
+ const ComunicationClientsApiVersion = "2024-04-01-preview";
14
+ /**
15
+ * Represents a credential that exchanges an Entra token for an Azure Communication Services (ACS) token, enabling access to ACS resources.
16
+ */
17
+ class EntraTokenCredential {
18
+ constructor(options) {
19
+ this.options = options;
20
+ this.result = {
21
+ entraToken: undefined,
22
+ acsToken: { token: "", expiresOnTimestamp: 0 },
23
+ };
24
+ this.client = (0, core_client_1.getClient)(options.resourceEndpoint);
25
+ this.httpClient = (0, core_rest_pipeline_1.createDefaultHttpClient)();
26
+ this.options = options;
27
+ this.options.scopes = this.options.scopes || [
28
+ "https://communication.azure.com/clients/.default",
29
+ ];
30
+ // immediately fetch the token to pre-warm
31
+ this.isPending = this.getToken();
32
+ }
33
+ async getToken(options) {
34
+ var _a;
35
+ if ((_a = options === null || options === void 0 ? void 0 : options.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) {
36
+ return { token: "", expiresOnTimestamp: 0 };
37
+ }
38
+ // we're awaiting the token fetch, so we don't want to start another one
39
+ // however, we're ignoring the new abortSignal, unfortunately
40
+ if (!this.isPending) {
41
+ this.isPending = this.getTokenInternal(options);
42
+ }
43
+ try {
44
+ await this.isPending;
45
+ }
46
+ finally {
47
+ this.isPending = null;
48
+ }
49
+ return this.result.acsToken;
50
+ }
51
+ async getTokenInternal(options) {
52
+ const getTokenOptions = (options === null || options === void 0 ? void 0 : options.abortSignal) ? { abortSignal: options.abortSignal } : undefined;
53
+ const token = await this.options.tokenCredential.getToken(this.options.scopes
54
+ ? this.options.scopes
55
+ : ["https://communication.azure.com/clients/.default"], getTokenOptions);
56
+ const currentDateTime = new Date();
57
+ const tokenExpiresOn = new Date(this.result.acsToken.expiresOnTimestamp);
58
+ if (token === null) {
59
+ this.result = {
60
+ entraToken: undefined,
61
+ acsToken: { token: "", expiresOnTimestamp: 0 },
62
+ };
63
+ }
64
+ else if (this.result.acsToken.token === "" ||
65
+ token.token !== this.result.entraToken ||
66
+ tokenExpiresOn < currentDateTime) {
67
+ const acsToken = await this.exchangeEntraToken(this.options.resourceEndpoint, token.token, getTokenOptions);
68
+ this.result = {
69
+ entraToken: token.token,
70
+ acsToken,
71
+ };
72
+ }
73
+ return this.result.acsToken;
74
+ }
75
+ dispose() {
76
+ this.result = {
77
+ entraToken: undefined,
78
+ acsToken: { token: "", expiresOnTimestamp: 0 },
79
+ };
80
+ }
81
+ async exchangeEntraToken(resourceEndpoint, entraToken, options) {
82
+ const request = (0, core_rest_pipeline_1.createPipelineRequest)({
83
+ url: this.createRequestUri(resourceEndpoint),
84
+ method: "POST",
85
+ headers: (0, core_rest_pipeline_1.createHttpHeaders)({
86
+ Authorization: `Bearer ${entraToken}`,
87
+ "Content-Type": "application/json",
88
+ Accept: "application/json",
89
+ }),
90
+ abortSignal: options === null || options === void 0 ? void 0 : options.abortSignal,
91
+ body: JSON.stringify({}),
92
+ });
93
+ const response = await this.client.pipeline.sendRequest(this.httpClient, request);
94
+ if (response.status !== 200 || !response.bodyAsText) {
95
+ throw new Error(`Service request failed. Status: ${response.status}, Body: ${response.bodyAsText}`);
96
+ }
97
+ const json = JSON.parse(response.bodyAsText);
98
+ return {
99
+ token: json.accessToken.token,
100
+ expiresOnTimestamp: Date.parse(json.accessToken.expiresOn),
101
+ };
102
+ }
103
+ createRequestUri(resourceEndpoint) {
104
+ const [endpoint, apiVersion] = this.determineEndpointAndApiVersion();
105
+ const requestUri = `${resourceEndpoint}${endpoint}?api-version=${apiVersion}`;
106
+ return requestUri;
107
+ }
108
+ determineEndpointAndApiVersion() {
109
+ if (!this.options.scopes || this.options.scopes.length === 0) {
110
+ throw new Error(`Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`);
111
+ }
112
+ else if (this.options.scopes.every((scope) => scope.startsWith(TeamsExtensionScopePrefix))) {
113
+ return [TeamsExtensionEndpoint, TeamsExtensionApiVersion];
114
+ }
115
+ else if (this.options.scopes.every((scope) => scope.startsWith(ComunicationClientsScopePrefix))) {
116
+ return [ComunicationClientsEndpoint, ComunicationClientsApiVersion];
117
+ }
118
+ else {
119
+ throw new Error(`Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {ComunicationClientsScopePrefix}.`);
120
+ }
121
+ }
122
+ }
123
+ exports.EntraTokenCredential = EntraTokenCredential;
124
+ //# sourceMappingURL=entraTokenCredential.js.map