@azure/communication-common 2.4.2-alpha.20260210.1 → 2.4.2
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.
- package/README.md +1 -1
- package/dist/browser/credential/communicationAccessKeyCredentialPolicy.d.ts.map +1 -1
- package/dist/browser/credential/communicationAccessKeyCredentialPolicy.js +6 -10
- package/dist/browser/credential/communicationAccessKeyCredentialPolicy.js.map +1 -1
- package/dist/browser/entraTokenCredential.d.ts.map +1 -1
- package/dist/browser/entraTokenCredential.js +12 -7
- package/dist/browser/entraTokenCredential.js.map +1 -1
- package/dist/browser/index.d.ts +3 -3
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/commonjs/autoRefreshTokenCredential.js +110 -95
- package/dist/commonjs/autoRefreshTokenCredential.js.map +7 -1
- package/dist/commonjs/azureCommunicationTokenCredential.js +64 -47
- package/dist/commonjs/azureCommunicationTokenCredential.js.map +7 -1
- package/dist/commonjs/communicationTokenCredential.js +16 -5
- package/dist/commonjs/communicationTokenCredential.js.map +7 -1
- package/dist/commonjs/credential/clientArguments.js +46 -40
- package/dist/commonjs/credential/clientArguments.js.map +7 -1
- package/dist/commonjs/credential/communicationAccessKeyCredentialPolicy.d.ts.map +1 -1
- package/dist/commonjs/credential/communicationAccessKeyCredentialPolicy.js +53 -42
- package/dist/commonjs/credential/communicationAccessKeyCredentialPolicy.js.map +7 -1
- package/dist/commonjs/credential/communicationAuthPolicy.js +39 -26
- package/dist/commonjs/credential/communicationAuthPolicy.js.map +7 -1
- package/dist/commonjs/credential/connectionString.js +39 -28
- package/dist/commonjs/credential/connectionString.js.map +7 -1
- package/dist/commonjs/credential/index.js +28 -10
- package/dist/commonjs/credential/index.js.map +7 -1
- package/dist/commonjs/entraTokenCredential.d.ts.map +1 -1
- package/dist/commonjs/entraTokenCredential.js +137 -113
- package/dist/commonjs/entraTokenCredential.js.map +7 -1
- package/dist/commonjs/identifierModelSerializer.js +172 -159
- package/dist/commonjs/identifierModelSerializer.js.map +7 -1
- package/dist/commonjs/identifierModels.js +183 -205
- package/dist/commonjs/identifierModels.js.map +7 -1
- package/dist/commonjs/index.d.ts +3 -3
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +35 -12
- package/dist/commonjs/index.js.map +7 -1
- package/dist/commonjs/staticTokenCredential.js +36 -20
- package/dist/commonjs/staticTokenCredential.js.map +7 -1
- package/dist/commonjs/tokenParser.js +33 -13
- package/dist/commonjs/tokenParser.js.map +7 -1
- package/dist/commonjs/tsdoc-metadata.json +1 -1
- package/dist/esm/credential/communicationAccessKeyCredentialPolicy.d.ts.map +1 -1
- package/dist/esm/credential/communicationAccessKeyCredentialPolicy.js +6 -10
- package/dist/esm/credential/communicationAccessKeyCredentialPolicy.js.map +1 -1
- package/dist/esm/entraTokenCredential.d.ts.map +1 -1
- package/dist/esm/entraTokenCredential.js +12 -7
- package/dist/esm/entraTokenCredential.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/react-native/credential/communicationAccessKeyCredentialPolicy.d.ts.map +1 -1
- package/dist/react-native/credential/communicationAccessKeyCredentialPolicy.js +6 -10
- package/dist/react-native/credential/communicationAccessKeyCredentialPolicy.js.map +1 -1
- package/dist/react-native/entraTokenCredential.d.ts.map +1 -1
- package/dist/react-native/entraTokenCredential.js +12 -7
- package/dist/react-native/entraTokenCredential.js.map +1 -1
- package/dist/react-native/index.d.ts +3 -3
- package/dist/react-native/index.d.ts.map +1 -1
- package/dist/react-native/index.js.map +1 -1
- package/package.json +30 -37
- package/dist/browser/credential/cryptoUtils-browser.d.mts.map +0 -1
- package/dist/browser/credential/cryptoUtils-browser.mjs.map +0 -1
- package/dist/browser/credential/cryptoUtils.d.ts +0 -3
- package/dist/browser/credential/cryptoUtils.js +0 -19
- package/dist/browser/credential/encodeUtils-browser.d.mts +0 -4
- package/dist/browser/credential/encodeUtils-browser.d.mts.map +0 -1
- package/dist/browser/credential/encodeUtils-browser.mjs +0 -19
- package/dist/browser/credential/encodeUtils-browser.mjs.map +0 -1
- package/dist/commonjs/credential/cryptoUtils.d.ts +0 -3
- package/dist/commonjs/credential/cryptoUtils.d.ts.map +0 -1
- package/dist/commonjs/credential/cryptoUtils.js +0 -14
- package/dist/commonjs/credential/cryptoUtils.js.map +0 -1
- package/dist/esm/credential/cryptoUtils.d.ts +0 -3
- package/dist/esm/credential/cryptoUtils.d.ts.map +0 -1
- package/dist/esm/credential/cryptoUtils.js +0 -9
- package/dist/esm/credential/cryptoUtils.js.map +0 -1
- package/dist/esm/credential/encodeUtils-browser.d.mts +0 -4
- package/dist/esm/credential/encodeUtils-browser.d.mts.map +0 -1
- package/dist/esm/credential/encodeUtils-browser.mjs +0 -19
- package/dist/esm/credential/encodeUtils-browser.mjs.map +0 -1
- package/dist/react-native/credential/cryptoUtils.d.ts +0 -3
- package/dist/react-native/credential/cryptoUtils.d.ts.map +0 -1
- package/dist/react-native/credential/cryptoUtils.js +0 -9
- package/dist/react-native/credential/cryptoUtils.js.map +0 -1
- package/dist/react-native/credential/encodeUtils-browser.d.mts +0 -4
- package/dist/react-native/credential/encodeUtils-browser.d.mts.map +0 -1
- package/dist/react-native/credential/encodeUtils-browser.mjs +0 -19
- package/dist/react-native/credential/encodeUtils-browser.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -117,7 +117,7 @@ For scenarios where an Entra user can be used with Communication Services, you n
|
|
|
117
117
|
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.
|
|
118
118
|
|
|
119
119
|
This approach needs to be used for authorizing an Entra user with a Teams license to use Teams Phone Extensibility features through your Azure Communication Services resource.
|
|
120
|
-
This requires providing the `https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls` scope.
|
|
120
|
+
This requires providing the `https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls` scope. For the GCCH cloud environment, use `https://auth.msft.communication.azure.us/TeamsExtension.ManageCalls`.
|
|
121
121
|
|
|
122
122
|
```ts snippet:ReadmeSampleCredentialEntraUserTeamsPhoneExtensibility
|
|
123
123
|
import { InteractiveBrowserCredential } from "@azure/identity";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communicationAccessKeyCredentialPolicy.d.ts","sourceRoot":"","sources":["../../../src/credential/communicationAccessKeyCredentialPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"communicationAccessKeyCredentialPolicy.d.ts","sourceRoot":"","sources":["../../../src/credential/communicationAccessKeyCredentialPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAStD;;;;;GAKG;AACH,wBAAgB,4CAA4C,CAC1D,UAAU,EAAE,aAAa,GACxB,cAAc,CA4BhB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import {
|
|
4
|
-
import { isNodeLike } from "@azure/core-util";
|
|
3
|
+
import { computeSha256Hash, computeSha256Hmac } from "@azure/core-util";
|
|
5
4
|
/**
|
|
6
5
|
* CommunicationKeyCredentialPolicy provides a means of signing requests made through
|
|
7
6
|
* the SmsClient.
|
|
@@ -19,19 +18,16 @@ export function createCommunicationAccessKeyCredentialPolicy(credential) {
|
|
|
19
18
|
async sendRequest(request, next) {
|
|
20
19
|
const verb = request.method.toUpperCase();
|
|
21
20
|
const utcNow = new Date().toUTCString();
|
|
22
|
-
const contentHash = await
|
|
21
|
+
const contentHash = await computeSha256Hash(request.body?.toString() || "", "base64");
|
|
23
22
|
const dateHeader = "x-ms-date";
|
|
24
23
|
const signedHeaders = `${dateHeader};host;x-ms-content-sha256`;
|
|
25
24
|
const url = new URL(request.url);
|
|
26
25
|
const query = url.searchParams.toString();
|
|
27
26
|
const urlPathAndQuery = query ? `${url.pathname}?${query}` : url.pathname;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (isNodeLike) {
|
|
33
|
-
request.headers.set("Host", hostAndPort || "");
|
|
34
|
-
}
|
|
27
|
+
const stringToSign = `${verb}\n${urlPathAndQuery}\n${utcNow};${url.host};${contentHash}`;
|
|
28
|
+
const signature = await computeSha256Hmac(credential.key, stringToSign, "base64");
|
|
29
|
+
// Host is a forbidden header in browsers (silently ignored), but needed in Node.js
|
|
30
|
+
request.headers.set("Host", url.host);
|
|
35
31
|
request.headers.set(dateHeader, utcNow);
|
|
36
32
|
request.headers.set("x-ms-content-sha256", contentHash);
|
|
37
33
|
request.headers.set("Authorization", `HMAC-SHA256 SignedHeaders=${signedHeaders}&Signature=${signature}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communicationAccessKeyCredentialPolicy.js","sourceRoot":"","sources":["../../../src/credential/communicationAccessKeyCredentialPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;
|
|
1
|
+
{"version":3,"file":"communicationAccessKeyCredentialPolicy.js","sourceRoot":"","sources":["../../../src/credential/communicationAccessKeyCredentialPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AASlC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAExE;;;GAGG;AACH,MAAM,sCAAsC,GAAG,wCAAwC,CAAC;AAExF;;;;;GAKG;AACH,MAAM,UAAU,4CAA4C,CAC1D,UAAyB;IAEzB,OAAO;QACL,IAAI,EAAE,sCAAsC;QAC5C,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;YACtF,MAAM,UAAU,GAAG,WAAW,CAAC;YAC/B,MAAM,aAAa,GAAG,GAAG,UAAU,2BAA2B,CAAC;YAE/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;YAC1C,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;YAE1E,MAAM,YAAY,GAAG,GAAG,IAAI,KAAK,eAAe,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;YACzF,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;YAElF,mFAAmF;YACnF,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACtC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACxC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;YACxD,OAAO,CAAC,OAAO,CAAC,GAAG,CACjB,eAAe,EACf,6BAA6B,aAAa,cAAc,SAAS,EAAE,CACpE,CAAC;YACF,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport type { KeyCredential } from \"@azure/core-auth\";\nimport { computeSha256Hash, computeSha256Hmac } from \"@azure/core-util\";\n\n/**\n * CommunicationKeyCredentialPolicy provides a means of signing requests made through\n * the SmsClient.\n */\nconst communicationAccessKeyCredentialPolicy = \"CommunicationAccessKeyCredentialPolicy\";\n\n/**\n * Creates an HTTP pipeline policy to authenticate a request using a `KeyCredential`.\n * @hidden\n *\n * @param credential - The key credential.\n */\nexport function createCommunicationAccessKeyCredentialPolicy(\n credential: KeyCredential,\n): PipelinePolicy {\n return {\n name: communicationAccessKeyCredentialPolicy,\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const verb = request.method.toUpperCase();\n const utcNow = new Date().toUTCString();\n const contentHash = await computeSha256Hash(request.body?.toString() || \"\", \"base64\");\n const dateHeader = \"x-ms-date\";\n const signedHeaders = `${dateHeader};host;x-ms-content-sha256`;\n\n const url = new URL(request.url);\n const query = url.searchParams.toString();\n const urlPathAndQuery = query ? `${url.pathname}?${query}` : url.pathname;\n\n const stringToSign = `${verb}\\n${urlPathAndQuery}\\n${utcNow};${url.host};${contentHash}`;\n const signature = await computeSha256Hmac(credential.key, stringToSign, \"base64\");\n\n // Host is a forbidden header in browsers (silently ignored), but needed in Node.js\n request.headers.set(\"Host\", url.host);\n request.headers.set(dateHeader, utcNow);\n request.headers.set(\"x-ms-content-sha256\", contentHash);\n request.headers.set(\n \"Authorization\",\n `HMAC-SHA256 SignedHeaders=${signedHeaders}&Signature=${signature}`,\n );\n return next(request);\n },\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entraTokenCredential.d.ts","sourceRoot":"","sources":["../../src/entraTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,KAAK,eAAe,IAAI,kBAAkB,EAC1C,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"entraTokenCredential.d.ts","sourceRoot":"","sources":["../../src/entraTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,KAAK,eAAe,IAAI,kBAAkB,EAC1C,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAC;AAwB3C,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;CAmBvC"}
|
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { getClient } from "@azure-rest/core-client";
|
|
4
4
|
import { createDefaultHttpClient, createHttpHeaders, createPipelineRequest, } from "@azure/core-rest-pipeline";
|
|
5
|
-
const
|
|
5
|
+
const TeamsExtensionScopePrefixes = [
|
|
6
|
+
"https://auth.msft.communication.azure.com/",
|
|
7
|
+
"https://auth.msft.communication.azure.us/",
|
|
8
|
+
];
|
|
6
9
|
const CommunicationClientsScopePrefix = "https://communication.azure.com/clients/";
|
|
10
|
+
const ScopeValidationErrorMessage = `Scopes validation failed. Ensure all scopes start with one of ${[
|
|
11
|
+
...TeamsExtensionScopePrefixes,
|
|
12
|
+
CommunicationClientsScopePrefix,
|
|
13
|
+
].join(", ")}.`;
|
|
7
14
|
const TeamsExtensionEndpoint = "/access/teamsExtension/:exchangeAccessToken";
|
|
8
15
|
const TeamsExtensionApiVersion = "2025-06-30";
|
|
9
16
|
const CommunicationClientsEndpoint = "/access/entra/:exchangeAccessToken";
|
|
@@ -107,17 +114,15 @@ export class EntraTokenCredential {
|
|
|
107
114
|
}
|
|
108
115
|
determineEndpointAndApiVersion() {
|
|
109
116
|
if (!this.options.scopes || this.options.scopes.length === 0) {
|
|
110
|
-
throw new Error(
|
|
117
|
+
throw new Error(ScopeValidationErrorMessage);
|
|
111
118
|
}
|
|
112
|
-
|
|
119
|
+
if (this.options.scopes.every((scope) => TeamsExtensionScopePrefixes.some((prefix) => scope.startsWith(prefix)))) {
|
|
113
120
|
return [TeamsExtensionEndpoint, TeamsExtensionApiVersion];
|
|
114
121
|
}
|
|
115
|
-
|
|
122
|
+
if (this.options.scopes.every((scope) => scope.startsWith(CommunicationClientsScopePrefix))) {
|
|
116
123
|
return [CommunicationClientsEndpoint, CommunicationClientsApiVersion];
|
|
117
124
|
}
|
|
118
|
-
|
|
119
|
-
throw new Error(`Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {CommunicationClientsScopePrefix}.`);
|
|
120
|
-
}
|
|
125
|
+
throw new Error(ScopeValidationErrorMessage);
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
//# sourceMappingURL=entraTokenCredential.js.map
|
|
@@ -1 +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, type TokenCredential } from \"@azure/core-auth\";\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 CommunicationClientsScopePrefix = \"https://communication.azure.com/clients/\";\nconst TeamsExtensionEndpoint = \"/access/teamsExtension/:exchangeAccessToken\";\nconst TeamsExtensionApiVersion = \"2025-06-30\";\nconst CommunicationClientsEndpoint = \"/access/entra/:exchangeAccessToken\";\nconst CommunicationClientsApiVersion = \"2025-03-02-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 {CommunicationClientsScopePrefix}.`,\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(CommunicationClientsScopePrefix))\n ) {\n return [CommunicationClientsEndpoint, CommunicationClientsApiVersion];\n } else {\n throw new Error(\n `Scopes validation failed. Ensure all scopes start with either {TeamsExtensionScopePrefix} or {CommunicationClientsScopePrefix}.`,\n );\n }\n }\n}\n"]}
|
|
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, type TokenCredential } from \"@azure/core-auth\";\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 TeamsExtensionScopePrefixes = [\n \"https://auth.msft.communication.azure.com/\",\n \"https://auth.msft.communication.azure.us/\",\n];\nconst CommunicationClientsScopePrefix = \"https://communication.azure.com/clients/\";\nconst ScopeValidationErrorMessage = `Scopes validation failed. Ensure all scopes start with one of ${[\n ...TeamsExtensionScopePrefixes,\n CommunicationClientsScopePrefix,\n].join(\", \")}.`;\nconst TeamsExtensionEndpoint = \"/access/teamsExtension/:exchangeAccessToken\";\nconst TeamsExtensionApiVersion = \"2025-06-30\";\nconst CommunicationClientsEndpoint = \"/access/entra/:exchangeAccessToken\";\nconst CommunicationClientsApiVersion = \"2025-03-02-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(ScopeValidationErrorMessage);\n }\n\n if (\n this.options.scopes.every((scope) =>\n TeamsExtensionScopePrefixes.some((prefix) => scope.startsWith(prefix)),\n )\n ) {\n return [TeamsExtensionEndpoint, TeamsExtensionApiVersion];\n }\n\n if (this.options.scopes.every((scope) => scope.startsWith(CommunicationClientsScopePrefix))) {\n return [CommunicationClientsEndpoint, CommunicationClientsApiVersion];\n }\n\n throw new Error(ScopeValidationErrorMessage);\n }\n}\n"]}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { CommunicationTokenCredential, CommunicationGetTokenOptions, } from "./communicationTokenCredential.js";
|
|
1
|
+
export type { CommunicationTokenCredential, CommunicationGetTokenOptions, } from "./communicationTokenCredential.js";
|
|
2
2
|
export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCredential.js";
|
|
3
|
-
export { CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
|
|
4
|
-
export { EntraCommunicationTokenCredentialOptions } from "./entraTokenCredential.js";
|
|
3
|
+
export type { CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
|
|
4
|
+
export type { 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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,iCAAiC,EAAE,MAAM,wCAAwC,CAAC;AAC3F,YAAY,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AACxF,YAAY,EAAE,wCAAwC,EAAE,MAAM,2BAA2B,CAAC;AAC1F,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC"}
|
|
@@ -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;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
|
+
{"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 type {\n CommunicationTokenCredential,\n CommunicationGetTokenOptions,\n} from \"./communicationTokenCredential.js\";\nexport { AzureCommunicationTokenCredential } from \"./azureCommunicationTokenCredential.js\";\nexport type { CommunicationTokenRefreshOptions } from \"./autoRefreshTokenCredential.js\";\nexport type { EntraCommunicationTokenCredentialOptions } from \"./entraTokenCredential.js\";\nexport * from \"./credential/index.js\";\nexport * from \"./identifierModels.js\";\nexport * from \"./identifierModelSerializer.js\";\n"]}
|
|
@@ -1,109 +1,124 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var autoRefreshTokenCredential_exports = {};
|
|
19
|
+
__export(autoRefreshTokenCredential_exports, {
|
|
20
|
+
AutoRefreshTokenCredential: () => AutoRefreshTokenCredential
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(autoRefreshTokenCredential_exports);
|
|
23
|
+
var import_tokenParser = require("./tokenParser.js");
|
|
7
24
|
const expiredToken = { token: "", expiresOnTimestamp: -10 };
|
|
8
|
-
const minutesToMs = (minutes) => minutes *
|
|
25
|
+
const minutesToMs = (minutes) => minutes * 1e3 * 60;
|
|
9
26
|
const defaultExpiringSoonInterval = minutesToMs(10);
|
|
10
27
|
const defaultRefreshAfterLifetimePercentage = 0.5;
|
|
11
28
|
class AutoRefreshTokenCredential {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
+
refresh;
|
|
30
|
+
refreshProactively;
|
|
31
|
+
expiringSoonIntervalInMs = defaultExpiringSoonInterval;
|
|
32
|
+
refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;
|
|
33
|
+
currentToken;
|
|
34
|
+
activeTimeout;
|
|
35
|
+
activeTokenFetching = null;
|
|
36
|
+
activeTokenUpdating = null;
|
|
37
|
+
disposed = false;
|
|
38
|
+
constructor(refreshArgs) {
|
|
39
|
+
const { tokenRefresher, token, refreshProactively } = refreshArgs;
|
|
40
|
+
this.refresh = tokenRefresher;
|
|
41
|
+
this.currentToken = token ? (0, import_tokenParser.parseToken)(token) : expiredToken;
|
|
42
|
+
this.refreshProactively = refreshProactively ?? false;
|
|
43
|
+
if (this.refreshProactively) {
|
|
44
|
+
this.scheduleRefresh();
|
|
29
45
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!this.isTokenValid(this.currentToken)) {
|
|
35
|
-
const updatePromise = this.updateTokenAndReschedule(options?.abortSignal);
|
|
36
|
-
await updatePromise;
|
|
37
|
-
}
|
|
38
|
-
return this.currentToken;
|
|
46
|
+
}
|
|
47
|
+
async getToken(options) {
|
|
48
|
+
if (!this.isTokenExpiringSoon(this.currentToken)) {
|
|
49
|
+
return this.currentToken;
|
|
39
50
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.activeTokenUpdating = null;
|
|
44
|
-
this.currentToken = expiredToken;
|
|
45
|
-
if (this.activeTimeout) {
|
|
46
|
-
clearTimeout(this.activeTimeout);
|
|
47
|
-
}
|
|
51
|
+
if (!this.isTokenValid(this.currentToken)) {
|
|
52
|
+
const updatePromise = this.updateTokenAndReschedule(options?.abortSignal);
|
|
53
|
+
await updatePromise;
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.activeTokenUpdating = null;
|
|
59
|
-
}
|
|
55
|
+
return this.currentToken;
|
|
56
|
+
}
|
|
57
|
+
dispose() {
|
|
58
|
+
this.disposed = true;
|
|
59
|
+
this.activeTokenFetching = null;
|
|
60
|
+
this.activeTokenUpdating = null;
|
|
61
|
+
this.currentToken = expiredToken;
|
|
62
|
+
if (this.activeTimeout) {
|
|
63
|
+
clearTimeout(this.activeTimeout);
|
|
60
64
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
this.currentToken = newToken;
|
|
67
|
-
if (this.refreshProactively) {
|
|
68
|
-
this.scheduleRefresh();
|
|
69
|
-
}
|
|
65
|
+
}
|
|
66
|
+
async updateTokenAndReschedule(abortSignal) {
|
|
67
|
+
if (this.activeTokenUpdating) {
|
|
68
|
+
return this.activeTokenUpdating;
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return (0, tokenParser_js_1.parseToken)(await this.activeTokenFetching);
|
|
77
|
-
}
|
|
78
|
-
finally {
|
|
79
|
-
this.activeTokenFetching = null;
|
|
80
|
-
}
|
|
70
|
+
this.activeTokenUpdating = this.refreshTokenAndReschedule(abortSignal);
|
|
71
|
+
try {
|
|
72
|
+
await this.activeTokenUpdating;
|
|
73
|
+
} finally {
|
|
74
|
+
this.activeTokenUpdating = null;
|
|
81
75
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
clearTimeout(this.activeTimeout);
|
|
88
|
-
}
|
|
89
|
-
const tokenTtlInMs = this.currentToken.expiresOnTimestamp - Date.now();
|
|
90
|
-
let timespanInMs = null;
|
|
91
|
-
if (this.isTokenExpiringSoon(this.currentToken)) {
|
|
92
|
-
// Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime.
|
|
93
|
-
timespanInMs = tokenTtlInMs * this.refreshAfterLifetimePercentage;
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
// Schedule the next refresh for when it gets in to the soon-to-expire window.
|
|
97
|
-
timespanInMs = tokenTtlInMs - this.expiringSoonIntervalInMs;
|
|
98
|
-
}
|
|
99
|
-
this.activeTimeout = setTimeout(() => this.updateTokenAndReschedule(), timespanInMs);
|
|
76
|
+
}
|
|
77
|
+
async refreshTokenAndReschedule(abortSignal) {
|
|
78
|
+
const newToken = await this.refreshToken(abortSignal);
|
|
79
|
+
if (!this.isTokenValid(newToken)) {
|
|
80
|
+
throw new Error("The token returned from the tokenRefresher is expired.");
|
|
100
81
|
}
|
|
101
|
-
|
|
102
|
-
|
|
82
|
+
this.currentToken = newToken;
|
|
83
|
+
if (this.refreshProactively) {
|
|
84
|
+
this.scheduleRefresh();
|
|
103
85
|
}
|
|
104
|
-
|
|
105
|
-
|
|
86
|
+
}
|
|
87
|
+
async refreshToken(abortSignal) {
|
|
88
|
+
try {
|
|
89
|
+
if (!this.activeTokenFetching) {
|
|
90
|
+
this.activeTokenFetching = this.refresh(abortSignal);
|
|
91
|
+
}
|
|
92
|
+
return (0, import_tokenParser.parseToken)(await this.activeTokenFetching);
|
|
93
|
+
} finally {
|
|
94
|
+
this.activeTokenFetching = null;
|
|
106
95
|
}
|
|
96
|
+
}
|
|
97
|
+
scheduleRefresh() {
|
|
98
|
+
if (this.disposed) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (this.activeTimeout) {
|
|
102
|
+
clearTimeout(this.activeTimeout);
|
|
103
|
+
}
|
|
104
|
+
const tokenTtlInMs = this.currentToken.expiresOnTimestamp - Date.now();
|
|
105
|
+
let timespanInMs = null;
|
|
106
|
+
if (this.isTokenExpiringSoon(this.currentToken)) {
|
|
107
|
+
timespanInMs = tokenTtlInMs * this.refreshAfterLifetimePercentage;
|
|
108
|
+
} else {
|
|
109
|
+
timespanInMs = tokenTtlInMs - this.expiringSoonIntervalInMs;
|
|
110
|
+
}
|
|
111
|
+
this.activeTimeout = setTimeout(() => this.updateTokenAndReschedule(), timespanInMs);
|
|
112
|
+
}
|
|
113
|
+
isTokenValid(token) {
|
|
114
|
+
return token && Date.now() < token.expiresOnTimestamp;
|
|
115
|
+
}
|
|
116
|
+
isTokenExpiringSoon(token) {
|
|
117
|
+
return !token || Date.now() >= token.expiresOnTimestamp - this.expiringSoonIntervalInMs;
|
|
118
|
+
}
|
|
107
119
|
}
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
121
|
+
0 && (module.exports = {
|
|
122
|
+
AutoRefreshTokenCredential
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=autoRefreshTokenCredential.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/autoRefreshTokenCredential.ts"],
|
|
4
|
+
"sourcesContent": ["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CommunicationGetTokenOptions,\n TokenCredential,\n} from \"./communicationTokenCredential.js\";\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { parseToken } from \"./tokenParser.js\";\n\n/**\n * Options for auto-refreshing a Communication Token credential.\n */\nexport interface CommunicationTokenRefreshOptions {\n /**\n * Callback function that returns a string JWT token acquired from the Communication Identity API.\n * The returned token must be valid (expiration date must be in the future).\n */\n tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string>;\n\n /**\n * Optional token to initialize.\n */\n token?: string;\n\n /**\n * Indicates whether the token should be proactively renewed prior to expiry or only renew on demand.\n * By default false.\n */\n refreshProactively?: boolean;\n}\n\nconst expiredToken = { token: \"\", expiresOnTimestamp: -10 };\nconst minutesToMs = (minutes: number): number => minutes * 1000 * 60;\nconst defaultExpiringSoonInterval = minutesToMs(10);\nconst defaultRefreshAfterLifetimePercentage = 0.5;\n\nexport class AutoRefreshTokenCredential implements TokenCredential {\n private readonly refresh: (abortSignal?: AbortSignalLike) => Promise<string>;\n private readonly refreshProactively: boolean;\n private readonly expiringSoonIntervalInMs: number = defaultExpiringSoonInterval;\n private readonly refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;\n\n private currentToken: AccessToken;\n private activeTimeout: ReturnType<typeof setTimeout> | undefined;\n private activeTokenFetching: Promise<string> | null = null;\n private activeTokenUpdating: Promise<void> | null = null;\n private disposed = false;\n\n constructor(refreshArgs: CommunicationTokenRefreshOptions) {\n const { tokenRefresher, token, refreshProactively } = refreshArgs;\n\n this.refresh = tokenRefresher;\n this.currentToken = token ? parseToken(token) : expiredToken;\n this.refreshProactively = refreshProactively ?? false;\n\n if (this.refreshProactively) {\n this.scheduleRefresh();\n }\n }\n\n public async getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken> {\n if (!this.isTokenExpiringSoon(this.currentToken)) {\n return this.currentToken;\n }\n\n if (!this.isTokenValid(this.currentToken)) {\n const updatePromise = this.updateTokenAndReschedule(options?.abortSignal);\n await updatePromise;\n }\n\n return this.currentToken;\n }\n\n public dispose(): void {\n this.disposed = true;\n this.activeTokenFetching = null;\n this.activeTokenUpdating = null;\n this.currentToken = expiredToken;\n if (this.activeTimeout) {\n clearTimeout(this.activeTimeout);\n }\n }\n\n private async updateTokenAndReschedule(abortSignal?: AbortSignalLike): Promise<void> {\n if (this.activeTokenUpdating) {\n return this.activeTokenUpdating;\n }\n this.activeTokenUpdating = this.refreshTokenAndReschedule(abortSignal);\n try {\n await this.activeTokenUpdating;\n } finally {\n this.activeTokenUpdating = null;\n }\n }\n\n private async refreshTokenAndReschedule(abortSignal?: AbortSignalLike): Promise<void> {\n const newToken = await this.refreshToken(abortSignal);\n\n if (!this.isTokenValid(newToken)) {\n throw new Error(\"The token returned from the tokenRefresher is expired.\");\n }\n\n this.currentToken = newToken;\n if (this.refreshProactively) {\n this.scheduleRefresh();\n }\n }\n\n private async refreshToken(abortSignal?: AbortSignalLike): Promise<AccessToken> {\n try {\n if (!this.activeTokenFetching) {\n this.activeTokenFetching = this.refresh(abortSignal);\n }\n return parseToken(await this.activeTokenFetching);\n } finally {\n this.activeTokenFetching = null;\n }\n }\n\n private scheduleRefresh(): void {\n if (this.disposed) {\n return;\n }\n if (this.activeTimeout) {\n clearTimeout(this.activeTimeout);\n }\n const tokenTtlInMs = this.currentToken.expiresOnTimestamp - Date.now();\n let timespanInMs = null;\n\n if (this.isTokenExpiringSoon(this.currentToken)) {\n // Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime.\n timespanInMs = tokenTtlInMs * this.refreshAfterLifetimePercentage;\n } else {\n // Schedule the next refresh for when it gets in to the soon-to-expire window.\n timespanInMs = tokenTtlInMs - this.expiringSoonIntervalInMs;\n }\n\n this.activeTimeout = setTimeout(() => this.updateTokenAndReschedule(), timespanInMs);\n }\n\n private isTokenValid(token: AccessToken): boolean {\n return token && Date.now() < token.expiresOnTimestamp;\n }\n\n private isTokenExpiringSoon(token: AccessToken): boolean {\n return !token || Date.now() >= token.expiresOnTimestamp - this.expiringSoonIntervalInMs;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,yBAA2B;AAwB3B,MAAM,eAAe,EAAE,OAAO,IAAI,oBAAoB,IAAI;AAC1D,MAAM,cAAc,CAAC,YAA4B,UAAU,MAAO;AAClE,MAAM,8BAA8B,YAAY,EAAE;AAClD,MAAM,wCAAwC;AAEvC,MAAM,2BAAsD;AAAA,EAChD;AAAA,EACA;AAAA,EACA,2BAAmC;AAAA,EACnC,iCAAiC;AAAA,EAE1C;AAAA,EACA;AAAA,EACA,sBAA8C;AAAA,EAC9C,sBAA4C;AAAA,EAC5C,WAAW;AAAA,EAEnB,YAAY,aAA+C;AACzD,UAAM,EAAE,gBAAgB,OAAO,mBAAmB,IAAI;AAEtD,SAAK,UAAU;AACf,SAAK,eAAe,YAAQ,+BAAW,KAAK,IAAI;AAChD,SAAK,qBAAqB,sBAAsB;AAEhD,QAAI,KAAK,oBAAoB;AAC3B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAa,SAAS,SAA8D;AAClF,QAAI,CAAC,KAAK,oBAAoB,KAAK,YAAY,GAAG;AAChD,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,CAAC,KAAK,aAAa,KAAK,YAAY,GAAG;AACzC,YAAM,gBAAgB,KAAK,yBAAyB,SAAS,WAAW;AACxE,YAAM;AAAA,IACR;AAEA,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,UAAgB;AACrB,SAAK,WAAW;AAChB,SAAK,sBAAsB;AAC3B,SAAK,sBAAsB;AAC3B,SAAK,eAAe;AACpB,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAc,yBAAyB,aAA8C;AACnF,QAAI,KAAK,qBAAqB;AAC5B,aAAO,KAAK;AAAA,IACd;AACA,SAAK,sBAAsB,KAAK,0BAA0B,WAAW;AACrE,QAAI;AACF,YAAM,KAAK;AAAA,IACb,UAAE;AACA,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAc,0BAA0B,aAA8C;AACpF,UAAM,WAAW,MAAM,KAAK,aAAa,WAAW;AAEpD,QAAI,CAAC,KAAK,aAAa,QAAQ,GAAG;AAChC,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAEA,SAAK,eAAe;AACpB,QAAI,KAAK,oBAAoB;AAC3B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,aAAqD;AAC9E,QAAI;AACF,UAAI,CAAC,KAAK,qBAAqB;AAC7B,aAAK,sBAAsB,KAAK,QAAQ,WAAW;AAAA,MACrD;AACA,iBAAO,+BAAW,MAAM,KAAK,mBAAmB;AAAA,IAClD,UAAE;AACA,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,UAAU;AACjB;AAAA,IACF;AACA,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AACA,UAAM,eAAe,KAAK,aAAa,qBAAqB,KAAK,IAAI;AACrE,QAAI,eAAe;AAEnB,QAAI,KAAK,oBAAoB,KAAK,YAAY,GAAG;AAE/C,qBAAe,eAAe,KAAK;AAAA,IACrC,OAAO;AAEL,qBAAe,eAAe,KAAK;AAAA,IACrC;AAEA,SAAK,gBAAgB,WAAW,MAAM,KAAK,yBAAyB,GAAG,YAAY;AAAA,EACrF;AAAA,EAEQ,aAAa,OAA6B;AAChD,WAAO,SAAS,KAAK,IAAI,IAAI,MAAM;AAAA,EACrC;AAAA,EAEQ,oBAAoB,OAA6B;AACvD,WAAO,CAAC,SAAS,KAAK,IAAI,KAAK,MAAM,qBAAqB,KAAK;AAAA,EACjE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,51 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var azureCommunicationTokenCredential_exports = {};
|
|
19
|
+
__export(azureCommunicationTokenCredential_exports, {
|
|
20
|
+
AzureCommunicationTokenCredential: () => AzureCommunicationTokenCredential
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(azureCommunicationTokenCredential_exports);
|
|
23
|
+
var import_autoRefreshTokenCredential = require("./autoRefreshTokenCredential.js");
|
|
24
|
+
var import_staticTokenCredential = require("./staticTokenCredential.js");
|
|
25
|
+
var import_tokenParser = require("./tokenParser.js");
|
|
26
|
+
var import_entraTokenCredential = require("./entraTokenCredential.js");
|
|
13
27
|
class AzureCommunicationTokenCredential {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
28
|
+
tokenCredential;
|
|
29
|
+
disposed = false;
|
|
30
|
+
constructor(tokenOrRefreshOptionsOrEntraOptions) {
|
|
31
|
+
if (typeof tokenOrRefreshOptionsOrEntraOptions === "string") {
|
|
32
|
+
this.tokenCredential = new import_staticTokenCredential.StaticTokenCredential(
|
|
33
|
+
(0, import_tokenParser.parseToken)(tokenOrRefreshOptionsOrEntraOptions)
|
|
34
|
+
);
|
|
35
|
+
} else if ("tokenRefresher" in tokenOrRefreshOptionsOrEntraOptions) {
|
|
36
|
+
this.tokenCredential = new import_autoRefreshTokenCredential.AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
|
|
37
|
+
} else {
|
|
38
|
+
this.tokenCredential = new import_entraTokenCredential.EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
|
|
26
39
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Gets an `AccessToken` for the user. Throws if already disposed.
|
|
43
|
+
* @param abortSignal - An implementation of `AbortSignalLike` to cancel the operation.
|
|
44
|
+
*/
|
|
45
|
+
async getToken(options) {
|
|
46
|
+
this.throwIfDisposed();
|
|
47
|
+
const token = await this.tokenCredential.getToken(options);
|
|
48
|
+
this.throwIfDisposed();
|
|
49
|
+
return token;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Disposes the CommunicationTokenCredential and cancels any internal auto-refresh operation.
|
|
53
|
+
*/
|
|
54
|
+
dispose() {
|
|
55
|
+
this.disposed = true;
|
|
56
|
+
this.tokenCredential.dispose();
|
|
57
|
+
}
|
|
58
|
+
throwIfDisposed() {
|
|
59
|
+
if (this.disposed) {
|
|
60
|
+
throw new Error("User credential is disposed");
|
|
48
61
|
}
|
|
62
|
+
}
|
|
49
63
|
}
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
65
|
+
0 && (module.exports = {
|
|
66
|
+
AzureCommunicationTokenCredential
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=azureCommunicationTokenCredential.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/azureCommunicationTokenCredential.ts"],
|
|
4
|
+
"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"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,wCAGO;AAOP,mCAAsC;AACtC,yBAA2B;AAC3B,kCAGO;AAMA,MAAM,kCAA0E;AAAA,EACpE;AAAA,EACT,WAAW;AAAA,EAkBnB,YACE,qCAIA;AACA,QAAI,OAAO,wCAAwC,UAAU;AAC3D,WAAK,kBAAkB,IAAI;AAAA,YACzB,+BAAW,mCAAmC;AAAA,MAChD;AAAA,IACF,WAAW,oBAAoB,qCAAqC;AAClE,WAAK,kBAAkB,IAAI,6DAA2B,mCAAmC;AAAA,IAC3F,OAAO;AACL,WAAK,kBAAkB,IAAI,iDAAqB,mCAAmC;AAAA,IACrF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,SAAS,SAA8D;AAClF,SAAK,gBAAgB;AACrB,UAAM,QAAQ,MAAM,KAAK,gBAAgB,SAAS,OAAO;AACzD,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,UAAgB;AACrB,SAAK,WAAW;AAChB,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,UAAU;AACjB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|