@azure/communication-common 2.4.2 → 2.5.0

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 (83) hide show
  1. package/README.md +46 -0
  2. package/dist/browser/autoRefreshTokenCredential.d.ts +20 -4
  3. package/dist/browser/autoRefreshTokenCredential.d.ts.map +1 -1
  4. package/dist/browser/autoRefreshTokenCredential.js +13 -4
  5. package/dist/browser/autoRefreshTokenCredential.js.map +1 -1
  6. package/dist/browser/azureCommunicationTokenCredential.d.ts +3 -2
  7. package/dist/browser/azureCommunicationTokenCredential.d.ts.map +1 -1
  8. package/dist/browser/azureCommunicationTokenCredential.js +3 -2
  9. package/dist/browser/azureCommunicationTokenCredential.js.map +1 -1
  10. package/dist/browser/index.d.ts +1 -0
  11. package/dist/browser/index.d.ts.map +1 -1
  12. package/dist/browser/index.js +1 -0
  13. package/dist/browser/index.js.map +1 -1
  14. package/dist/browser/logger.d.ts +3 -0
  15. package/dist/browser/logger.d.ts.map +1 -0
  16. package/dist/browser/logger.js +6 -0
  17. package/dist/browser/logger.js.map +1 -0
  18. package/dist/browser/tokenParser.d.ts +9 -1
  19. package/dist/browser/tokenParser.d.ts.map +1 -1
  20. package/dist/browser/tokenParser.js +64 -3
  21. package/dist/browser/tokenParser.js.map +1 -1
  22. package/dist/commonjs/autoRefreshTokenCredential.d.ts +20 -4
  23. package/dist/commonjs/autoRefreshTokenCredential.d.ts.map +1 -1
  24. package/dist/commonjs/autoRefreshTokenCredential.js +12 -4
  25. package/dist/commonjs/autoRefreshTokenCredential.js.map +2 -2
  26. package/dist/commonjs/azureCommunicationTokenCredential.d.ts +3 -2
  27. package/dist/commonjs/azureCommunicationTokenCredential.d.ts.map +1 -1
  28. package/dist/commonjs/azureCommunicationTokenCredential.js +1 -1
  29. package/dist/commonjs/azureCommunicationTokenCredential.js.map +2 -2
  30. package/dist/commonjs/index.d.ts +1 -0
  31. package/dist/commonjs/index.d.ts.map +1 -1
  32. package/dist/commonjs/index.js +6 -1
  33. package/dist/commonjs/index.js.map +2 -2
  34. package/dist/commonjs/logger.d.ts +3 -0
  35. package/dist/commonjs/logger.d.ts.map +1 -0
  36. package/dist/commonjs/logger.js +29 -0
  37. package/dist/commonjs/logger.js.map +7 -0
  38. package/dist/commonjs/tokenParser.d.ts +9 -1
  39. package/dist/commonjs/tokenParser.d.ts.map +1 -1
  40. package/dist/commonjs/tokenParser.js +41 -3
  41. package/dist/commonjs/tokenParser.js.map +2 -2
  42. package/dist/commonjs/tsdoc-metadata.json +1 -1
  43. package/dist/esm/autoRefreshTokenCredential.d.ts +20 -4
  44. package/dist/esm/autoRefreshTokenCredential.d.ts.map +1 -1
  45. package/dist/esm/autoRefreshTokenCredential.js +13 -4
  46. package/dist/esm/autoRefreshTokenCredential.js.map +1 -1
  47. package/dist/esm/azureCommunicationTokenCredential.d.ts +3 -2
  48. package/dist/esm/azureCommunicationTokenCredential.d.ts.map +1 -1
  49. package/dist/esm/azureCommunicationTokenCredential.js +3 -2
  50. package/dist/esm/azureCommunicationTokenCredential.js.map +1 -1
  51. package/dist/esm/index.d.ts +1 -0
  52. package/dist/esm/index.d.ts.map +1 -1
  53. package/dist/esm/index.js +1 -0
  54. package/dist/esm/index.js.map +1 -1
  55. package/dist/esm/logger.d.ts +3 -0
  56. package/dist/esm/logger.d.ts.map +1 -0
  57. package/dist/esm/logger.js +6 -0
  58. package/dist/esm/logger.js.map +1 -0
  59. package/dist/esm/tokenParser.d.ts +9 -1
  60. package/dist/esm/tokenParser.d.ts.map +1 -1
  61. package/dist/esm/tokenParser.js +64 -3
  62. package/dist/esm/tokenParser.js.map +1 -1
  63. package/dist/react-native/autoRefreshTokenCredential.d.ts +20 -4
  64. package/dist/react-native/autoRefreshTokenCredential.d.ts.map +1 -1
  65. package/dist/react-native/autoRefreshTokenCredential.js +13 -4
  66. package/dist/react-native/autoRefreshTokenCredential.js.map +1 -1
  67. package/dist/react-native/azureCommunicationTokenCredential.d.ts +3 -2
  68. package/dist/react-native/azureCommunicationTokenCredential.d.ts.map +1 -1
  69. package/dist/react-native/azureCommunicationTokenCredential.js +3 -2
  70. package/dist/react-native/azureCommunicationTokenCredential.js.map +1 -1
  71. package/dist/react-native/index.d.ts +1 -0
  72. package/dist/react-native/index.d.ts.map +1 -1
  73. package/dist/react-native/index.js +1 -0
  74. package/dist/react-native/index.js.map +1 -1
  75. package/dist/react-native/logger.d.ts +3 -0
  76. package/dist/react-native/logger.d.ts.map +1 -0
  77. package/dist/react-native/logger.js +6 -0
  78. package/dist/react-native/logger.js.map +1 -0
  79. package/dist/react-native/tokenParser.d.ts +9 -1
  80. package/dist/react-native/tokenParser.d.ts.map +1 -1
  81. package/dist/react-native/tokenParser.js +64 -3
  82. package/dist/react-native/tokenParser.js.map +1 -1
  83. package/package.json +8 -8
package/README.md CHANGED
@@ -111,6 +111,52 @@ const tokenCredential = new AzureCommunicationTokenCredential({
111
111
  });
112
112
  ```
113
113
 
114
+ ### Create a credential for an encrypted or opaque access token
115
+
116
+ Some access tokens — such as those issued to enterprise Entra users — are encrypted and can't be decoded on the client, so the credential cannot read their expiry. The recommended approach is to return an `AccessToken` (`{ token, expiresOnTimestamp }`) from your `tokenRefresher` (or pass one as the initial `token`), setting `expiresOnTimestamp` from the token response's `expires_in`. This keeps proactive refresh scheduling accurate.
117
+
118
+ ```ts snippet:ReadmeSampleCredentialEncryptedToken
119
+ import { AzureCommunicationTokenCredential } from "@azure/communication-common";
120
+
121
+ // Some access tokens (e.g. for enterprise Entra users) are encrypted and cannot be
122
+ // decoded on the client. Return an AccessToken with an explicit expiry so proactive
123
+ // refresh scheduling stays accurate; derive expiresOnTimestamp from the response's expires_in.
124
+ async function fetchEncryptedTokenForUser(user: string): Promise<{
125
+ token: string;
126
+ expiresOnTimestamp: number;
127
+ }> {
128
+ const response = { token: "some-encrypted-token-for-" + user, expiresInSeconds: 8 * 60 * 60 };
129
+ return {
130
+ token: response.token,
131
+ expiresOnTimestamp: Date.now() + response.expiresInSeconds * 1000,
132
+ };
133
+ }
134
+
135
+ const tokenCredential = new AzureCommunicationTokenCredential({
136
+ tokenRefresher: async () => fetchEncryptedTokenForUser("bob@contoso.com"),
137
+ refreshProactively: true,
138
+ });
139
+ ```
140
+
141
+ If you can only provide the token string, the credential accepts it and assumes a fallback lifetime. Set `undecodableTokenExpiryIntervalInSeconds` to your tokens' actual lifetime so they aren't refreshed more often than needed; it defaults to 600 seconds.
142
+
143
+ ```ts snippet:ReadmeSampleCredentialUndecodableTokenInterval
144
+ import { AzureCommunicationTokenCredential } from "@azure/communication-common";
145
+
146
+ function fetchOpaqueTokenForUser(user: string): Promise<string> {
147
+ // Your custom implementation returns an opaque token string that can't be decoded.
148
+ return Promise.resolve("some-opaque-token-for-" + user);
149
+ }
150
+
151
+ const tokenCredential = new AzureCommunicationTokenCredential({
152
+ tokenRefresher: async () => fetchOpaqueTokenForUser("bob@contoso.com"),
153
+ refreshProactively: true,
154
+ // When the token can't be decoded and no explicit expiry is provided, assume this
155
+ // lifetime (in seconds) instead of the 600s default — set it to your tokens' real lifetime.
156
+ undecodableTokenExpiryIntervalInSeconds: 3600,
157
+ });
158
+ ```
159
+
114
160
  ### Create a credential with a token credential capable of obtaining an Entra user token
115
161
 
116
162
  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``.
@@ -6,25 +6,41 @@ import type { AccessToken } from "@azure/core-auth";
6
6
  */
7
7
  export interface CommunicationTokenRefreshOptions {
8
8
  /**
9
- * Callback function that returns a string JWT token acquired from the Communication Identity API.
9
+ * Callback that returns a token acquired from the Communication Identity API.
10
10
  * The returned token must be valid (expiration date must be in the future).
11
+ *
12
+ * Return a `string` to have the expiry read from the JWT, or an `AccessToken`
13
+ * to supply the expiry explicitly (required for encrypted access tokens that
14
+ * cannot be decoded).
11
15
  */
12
- tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string>;
16
+ tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;
13
17
  /**
14
- * Optional token to initialize.
18
+ * Optional token to initialize. May be a `string` or an `AccessToken` carrying
19
+ * an explicit expiry.
15
20
  */
16
- token?: string;
21
+ token?: string | AccessToken;
17
22
  /**
18
23
  * Indicates whether the token should be proactively renewed prior to expiry or only renew on demand.
19
24
  * By default false.
20
25
  */
21
26
  refreshProactively?: boolean;
27
+ /**
28
+ * Lifetime, in seconds, assumed for a returned token that cannot be decoded and
29
+ * carries no explicit expiry (e.g. an encrypted access token). Defaults to 600 (10 minutes).
30
+ *
31
+ * Set this to your tokens' real lifetime. It is not capped: a value larger than the token's
32
+ * actual lifetime means the credential refreshes later than it should and the service will
33
+ * reject the expired token. To avoid guessing, prefer returning an `AccessToken` with an
34
+ * explicit `expiresOnTimestamp` (e.g. derived from the token response's `expires_in`).
35
+ */
36
+ undecodableTokenExpiryIntervalInSeconds?: number;
22
37
  }
23
38
  export declare class AutoRefreshTokenCredential implements TokenCredential {
24
39
  private readonly refresh;
25
40
  private readonly refreshProactively;
26
41
  private readonly expiringSoonIntervalInMs;
27
42
  private readonly refreshAfterLifetimePercentage;
43
+ private readonly undecodableTokenExpiryIntervalInSeconds;
28
44
  private currentToken;
29
45
  private activeTimeout;
30
46
  private activeTokenFetching;
@@ -1 +1 @@
1
- {"version":3,"file":"autoRefreshTokenCredential.d.ts","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,EAChB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,cAAc,EAAE,CAAC,WAAW,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnE;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAOD,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqD;IAC7E,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAChF,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAyC;IAExF,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,mBAAmB,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,gCAAgC;IAY5C,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAa5E,OAAO,IAAI,IAAI;YAUR,wBAAwB;YAYxB,yBAAyB;YAazB,YAAY;IAW1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;CAG5B"}
1
+ {"version":3,"file":"autoRefreshTokenCredential.d.ts","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,EAChB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,WAAW,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;IAEjF;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;;;;OAQG;IACH,uCAAuC,CAAC,EAAE,MAAM,CAAC;CAClD;AASD,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmE;IAC3F,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAChF,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAyC;IACxF,OAAO,CAAC,QAAQ,CAAC,uCAAuC,CAAqB;IAE7E,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,mBAAmB,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,gCAAgC;IAgB5C,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAa5E,OAAO,IAAI,IAAI;YAUR,wBAAwB;YAexB,yBAAyB;YAazB,YAAY;IAc1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;CAG5B"}
@@ -3,6 +3,8 @@
3
3
  import { parseToken } from "./tokenParser.js";
4
4
  const expiredToken = { token: "", expiresOnTimestamp: -10 };
5
5
  const minutesToMs = (minutes) => minutes * 1000 * 60;
6
+ // Must stay in step with `defaultUndecodableTokenExpiryIntervalInSeconds` (600s) in tokenParser.ts —
7
+ // an opaque token's fallback expiry is expected to land in this expiring-soon window.
6
8
  const defaultExpiringSoonInterval = minutesToMs(10);
7
9
  const defaultRefreshAfterLifetimePercentage = 0.5;
8
10
  export class AutoRefreshTokenCredential {
@@ -10,15 +12,19 @@ export class AutoRefreshTokenCredential {
10
12
  refreshProactively;
11
13
  expiringSoonIntervalInMs = defaultExpiringSoonInterval;
12
14
  refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;
15
+ undecodableTokenExpiryIntervalInSeconds;
13
16
  currentToken;
14
17
  activeTimeout;
15
18
  activeTokenFetching = null;
16
19
  activeTokenUpdating = null;
17
20
  disposed = false;
18
21
  constructor(refreshArgs) {
19
- const { tokenRefresher, token, refreshProactively } = refreshArgs;
22
+ const { tokenRefresher, token, refreshProactively, undecodableTokenExpiryIntervalInSeconds } = refreshArgs;
20
23
  this.refresh = tokenRefresher;
21
- this.currentToken = token ? parseToken(token) : expiredToken;
24
+ this.undecodableTokenExpiryIntervalInSeconds = undecodableTokenExpiryIntervalInSeconds;
25
+ this.currentToken = token
26
+ ? parseToken(token, this.undecodableTokenExpiryIntervalInSeconds)
27
+ : expiredToken;
22
28
  this.refreshProactively = refreshProactively ?? false;
23
29
  if (this.refreshProactively) {
24
30
  this.scheduleRefresh();
@@ -39,11 +45,14 @@ export class AutoRefreshTokenCredential {
39
45
  this.activeTokenFetching = null;
40
46
  this.activeTokenUpdating = null;
41
47
  this.currentToken = expiredToken;
42
- if (this.activeTimeout) {
48
+ if (this.activeTimeout !== undefined) {
43
49
  clearTimeout(this.activeTimeout);
44
50
  }
45
51
  }
46
52
  async updateTokenAndReschedule(abortSignal) {
53
+ if (this.disposed) {
54
+ return;
55
+ }
47
56
  if (this.activeTokenUpdating) {
48
57
  return this.activeTokenUpdating;
49
58
  }
@@ -70,7 +79,7 @@ export class AutoRefreshTokenCredential {
70
79
  if (!this.activeTokenFetching) {
71
80
  this.activeTokenFetching = this.refresh(abortSignal);
72
81
  }
73
- return parseToken(await this.activeTokenFetching);
82
+ return parseToken(await this.activeTokenFetching, this.undecodableTokenExpiryIntervalInSeconds);
74
83
  }
75
84
  finally {
76
85
  this.activeTokenFetching = null;
@@ -1 +1 @@
1
- {"version":3,"file":"autoRefreshTokenCredential.js","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"autoRefreshTokenCredential.js","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAQlC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwC9C,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,EAAE,EAAE,CAAC;AAC5D,MAAM,WAAW,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;AACrE,qGAAqG;AACrG,sFAAsF;AACtF,MAAM,2BAA2B,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;AACpD,MAAM,qCAAqC,GAAG,GAAG,CAAC;AAElD,MAAM,OAAO,0BAA0B;IACpB,OAAO,CAAmE;IAC1E,kBAAkB,CAAU;IAC5B,wBAAwB,GAAW,2BAA2B,CAAC;IAC/D,8BAA8B,GAAG,qCAAqC,CAAC;IACvE,uCAAuC,CAAqB;IAErE,YAAY,CAAc;IAC1B,aAAa,CAA4C;IACzD,mBAAmB,GAAyC,IAAI,CAAC;IACjE,mBAAmB,GAAyB,IAAI,CAAC;IACjD,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,WAA6C;QACvD,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,EAAE,uCAAuC,EAAE,GAC1F,WAAW,CAAC;QAEd,IAAI,CAAC,OAAO,GAAG,cAAc,CAAC;QAC9B,IAAI,CAAC,uCAAuC,GAAG,uCAAuC,CAAC;QACvF,IAAI,CAAC,YAAY,GAAG,KAAK;YACvB,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,uCAAuC,CAAC;YACjE,CAAC,CAAC,YAAY,CAAC;QACjB,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,IAAI,KAAK,CAAC;QAEtD,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,OAAsC;QAC1D,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC,YAAY,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC1E,MAAM,aAAa,CAAC;QACtB,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACrC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,wBAAwB,CAAC,WAA6B;QAClE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,mBAAmB,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC;QACvE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,mBAAmB,CAAC;QACjC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAClC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,yBAAyB,CAAC,WAA6B;QACnE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAEtD,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC7B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,WAA6B;QACtD,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC9B,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,UAAU,CACf,MAAM,IAAI,CAAC,mBAAmB,EAC9B,IAAI,CAAC,uCAAuC,CAC7C,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAClC,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvE,IAAI,YAAY,GAAG,IAAI,CAAC;QAExB,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAChD,gGAAgG;YAChG,YAAY,GAAG,YAAY,GAAG,IAAI,CAAC,8BAA8B,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,8EAA8E;YAC9E,YAAY,GAAG,YAAY,GAAG,IAAI,CAAC,wBAAwB,CAAC;QAC9D,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,YAAY,CAAC,CAAC;IACvF,CAAC;IAEO,YAAY,CAAC,KAAkB;QACrC,OAAO,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,kBAAkB,CAAC;IACxD,CAAC;IAEO,mBAAmB,CAAC,KAAkB;QAC5C,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC,wBAAwB,CAAC;IAC1F,CAAC;CACF","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 that returns a token acquired from the Communication Identity API.\n * The returned token must be valid (expiration date must be in the future).\n *\n * Return a `string` to have the expiry read from the JWT, or an `AccessToken`\n * to supply the expiry explicitly (required for encrypted access tokens that\n * cannot be decoded).\n */\n tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;\n\n /**\n * Optional token to initialize. May be a `string` or an `AccessToken` carrying\n * an explicit expiry.\n */\n token?: string | AccessToken;\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 /**\n * Lifetime, in seconds, assumed for a returned token that cannot be decoded and\n * carries no explicit expiry (e.g. an encrypted access token). Defaults to 600 (10 minutes).\n *\n * Set this to your tokens' real lifetime. It is not capped: a value larger than the token's\n * actual lifetime means the credential refreshes later than it should and the service will\n * reject the expired token. To avoid guessing, prefer returning an `AccessToken` with an\n * explicit `expiresOnTimestamp` (e.g. derived from the token response's `expires_in`).\n */\n undecodableTokenExpiryIntervalInSeconds?: number;\n}\n\nconst expiredToken = { token: \"\", expiresOnTimestamp: -10 };\nconst minutesToMs = (minutes: number): number => minutes * 1000 * 60;\n// Must stay in step with `defaultUndecodableTokenExpiryIntervalInSeconds` (600s) in tokenParser.ts —\n// an opaque token's fallback expiry is expected to land in this expiring-soon window.\nconst defaultExpiringSoonInterval = minutesToMs(10);\nconst defaultRefreshAfterLifetimePercentage = 0.5;\n\nexport class AutoRefreshTokenCredential implements TokenCredential {\n private readonly refresh: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;\n private readonly refreshProactively: boolean;\n private readonly expiringSoonIntervalInMs: number = defaultExpiringSoonInterval;\n private readonly refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;\n private readonly undecodableTokenExpiryIntervalInSeconds: number | undefined;\n\n private currentToken: AccessToken;\n private activeTimeout: ReturnType<typeof setTimeout> | undefined;\n private activeTokenFetching: Promise<string | AccessToken> | null = null;\n private activeTokenUpdating: Promise<void> | null = null;\n private disposed = false;\n\n constructor(refreshArgs: CommunicationTokenRefreshOptions) {\n const { tokenRefresher, token, refreshProactively, undecodableTokenExpiryIntervalInSeconds } =\n refreshArgs;\n\n this.refresh = tokenRefresher;\n this.undecodableTokenExpiryIntervalInSeconds = undecodableTokenExpiryIntervalInSeconds;\n this.currentToken = token\n ? parseToken(token, this.undecodableTokenExpiryIntervalInSeconds)\n : 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 !== undefined) {\n clearTimeout(this.activeTimeout);\n }\n }\n\n private async updateTokenAndReschedule(abortSignal?: AbortSignalLike): Promise<void> {\n if (this.disposed) {\n return;\n }\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(\n await this.activeTokenFetching,\n this.undecodableTokenExpiryIntervalInSeconds,\n );\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"]}
@@ -10,9 +10,10 @@ export declare class AzureCommunicationTokenCredential implements CommunicationT
10
10
  private disposed;
11
11
  /**
12
12
  * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.
13
- * @param token - A user access token issued by Communication Services.
13
+ * @param token - A user access token issued by Communication Services. Pass an `AccessToken`
14
+ * to supply the expiry explicitly when the token cannot be decoded (e.g. an encrypted token).
14
15
  */
15
- constructor(token: string);
16
+ constructor(token: string | AccessToken);
16
17
  /**
17
18
  * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options
18
19
  * to configure proactive refreshing.
@@ -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;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"}
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;;;;OAIG;gBACS,KAAK,EAAE,MAAM,GAAG,WAAW;IACvC;;;;OAIG;gBACS,cAAc,EAAE,gCAAgC;IAC5D;;;OAGG;gBACS,YAAY,EAAE,wCAAwC;IAsBlE;;;OAGG;IACU,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOnF;;OAEG;IACI,OAAO,IAAI,IAAI;IAKtB,OAAO,CAAC,eAAe;CAKxB"}
@@ -2,7 +2,7 @@
2
2
  // Licensed under the MIT License.
3
3
  import { AutoRefreshTokenCredential, } from "./autoRefreshTokenCredential.js";
4
4
  import { StaticTokenCredential } from "./staticTokenCredential.js";
5
- import { parseToken } from "./tokenParser.js";
5
+ import { isAccessToken, parseToken } from "./tokenParser.js";
6
6
  import { EntraTokenCredential, } from "./entraTokenCredential.js";
7
7
  /**
8
8
  * The CommunicationTokenCredential implementation with support for proactive token refresh.
@@ -11,7 +11,8 @@ export class AzureCommunicationTokenCredential {
11
11
  tokenCredential;
12
12
  disposed = false;
13
13
  constructor(tokenOrRefreshOptionsOrEntraOptions) {
14
- if (typeof tokenOrRefreshOptionsOrEntraOptions === "string") {
14
+ if (typeof tokenOrRefreshOptionsOrEntraOptions === "string" ||
15
+ isAccessToken(tokenOrRefreshOptionsOrEntraOptions)) {
15
16
  this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptionsOrEntraOptions));
16
17
  }
17
18
  else if ("tokenRefresher" in tokenOrRefreshOptionsOrEntraOptions) {
@@ -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;AAC9C,OAAO,EAEL,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AAEH,MAAM,OAAO,iCAAiC;IAC3B,eAAe,CAAkB;IAC1C,QAAQ,GAAG,KAAK,CAAC;IAkBzB,YACE,mCAG4C;QAE5C,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"]}
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,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAEL,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AAEH,MAAM,OAAO,iCAAiC;IAC3B,eAAe,CAAkB;IAC1C,QAAQ,GAAG,KAAK,CAAC;IAmBzB,YACE,mCAI4C;QAE5C,IACE,OAAO,mCAAmC,KAAK,QAAQ;YACvD,aAAa,CAAC,mCAAmC,CAAC,EAClD,CAAC;YACD,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 { isAccessToken, 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. Pass an `AccessToken`\n * to supply the expiry explicitly when the token cannot be decoded (e.g. an encrypted token).\n */\n constructor(token: string | AccessToken);\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 | AccessToken\n | CommunicationTokenRefreshOptions\n | EntraCommunicationTokenCredentialOptions,\n ) {\n if (\n typeof tokenOrRefreshOptionsOrEntraOptions === \"string\" ||\n isAccessToken(tokenOrRefreshOptionsOrEntraOptions)\n ) {\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,4 +5,5 @@ export type { EntraCommunicationTokenCredentialOptions } from "./entraTokenCrede
5
5
  export * from "./credential/index.js";
6
6
  export * from "./identifierModels.js";
7
7
  export * from "./identifierModelSerializer.js";
8
+ export { RestError, isRestError } from "@azure/core-rest-pipeline";
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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
+ {"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;AAC/C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC"}
@@ -4,4 +4,5 @@ export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCred
4
4
  export * from "./credential/index.js";
5
5
  export * from "./identifierModels.js";
6
6
  export * from "./identifierModelSerializer.js";
7
+ export { RestError, isRestError } from "@azure/core-rest-pipeline";
7
8
  //# 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;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
+ {"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;AAC/C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,2BAA2B,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\";\nexport { RestError, isRestError } from \"@azure/core-rest-pipeline\";\n"]}
@@ -0,0 +1,3 @@
1
+ /** The `@azure/logger` instance used by this package. */
2
+ export declare const logger: import("@azure/logger").AzureLogger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAKA,yDAAyD;AACzD,eAAO,MAAM,MAAM,qCAA6C,CAAC"}
@@ -0,0 +1,6 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { createClientLogger } from "@azure/logger";
4
+ /** The `@azure/logger` instance used by this package. */
5
+ export const logger = createClientLogger("communication-common");
6
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,yDAAyD;AACzD,MAAM,CAAC,MAAM,MAAM,GAAG,kBAAkB,CAAC,sBAAsB,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { createClientLogger } from \"@azure/logger\";\n\n/** The `@azure/logger` instance used by this package. */\nexport const logger = createClientLogger(\"communication-common\");\n"]}
@@ -1,3 +1,11 @@
1
1
  import type { AccessToken } from "@azure/core-auth";
2
- export declare const parseToken: (token: string) => AccessToken;
2
+ /** Type guard for a caller-supplied {@link AccessToken} (carries a valid explicit expiry). */
3
+ export declare const isAccessToken: (value: unknown) => value is AccessToken;
4
+ /**
5
+ * Resolves a token into an {@link AccessToken}. Expiry is taken from a caller-supplied
6
+ * `AccessToken` if present, otherwise from the JWT `exp` claim. A token that cannot be decoded
7
+ * (e.g. an encrypted/opaque token) or that lacks a usable `exp` is accepted as-is with a fallback
8
+ * expiry of `now + undecodableTokenExpiryIntervalInSeconds`; an empty/blank token throws.
9
+ */
10
+ export declare const parseToken: (token: string | AccessToken, undecodableTokenExpiryIntervalInSeconds?: number) => AccessToken;
3
11
  //# sourceMappingURL=tokenParser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokenParser.d.ts","sourceRoot":"","sources":["../../src/tokenParser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOpD,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,WAM1C,CAAC"}
1
+ {"version":3,"file":"tokenParser.d.ts","sourceRoot":"","sources":["../../src/tokenParser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAkBpD,8FAA8F;AAC9F,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAMI,CAAC;AA8B7D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,MAAM,GAAG,WAAW,EAC3B,0CAAyC,MAAuD,KAC/F,WA4BF,CAAC"}
@@ -1,11 +1,72 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  import { jwtDecode } from "jwt-decode";
4
- export const parseToken = (token) => {
5
- const { exp } = jwtDecode(token);
4
+ import { logger } from "./logger.js";
5
+ /**
6
+ * Default lifetime (in seconds) assumed for an encrypted/undecodable token when no expiry is
7
+ * supplied. Must stay in step with `defaultExpiringSoonInterval` (10 min) in
8
+ * autoRefreshTokenCredential.ts: the "refresh an opaque token at half-life" behavior relies on a
9
+ * fresh fallback token immediately landing in the expiring-soon window.
10
+ */
11
+ const defaultUndecodableTokenExpiryIntervalInSeconds = 10 * 60;
12
+ let undecodableTokenWarningEmitted = false;
13
+ /** Type guard for a caller-supplied {@link AccessToken} (carries a valid explicit expiry). */
14
+ export const isAccessToken = (value) => typeof value === "object" &&
15
+ value !== null &&
16
+ typeof value.token === "string" &&
17
+ value.token.length > 0 &&
18
+ typeof value.expiresOnTimestamp === "number" &&
19
+ Number.isFinite(value.expiresOnTimestamp);
20
+ /**
21
+ * Accepts a token that can't be decoded (encrypted/opaque, or a JWT without a usable `exp`) and
22
+ * assigns a fallback expiry of `now + undecodableTokenExpiryIntervalInSeconds`. Emits a one-time
23
+ * warning; throws if the interval is not a positive, finite number.
24
+ */
25
+ const resolveOpaqueToken = (token, undecodableTokenExpiryIntervalInSeconds) => {
26
+ if (!Number.isFinite(undecodableTokenExpiryIntervalInSeconds) ||
27
+ undecodableTokenExpiryIntervalInSeconds <= 0) {
28
+ throw new Error("undecodableTokenExpiryIntervalInSeconds must be a positive number.");
29
+ }
30
+ if (!undecodableTokenWarningEmitted) {
31
+ undecodableTokenWarningEmitted = true;
32
+ logger.warning(`Received a token that could not be decoded; treating it as opaque and assuming a ${undecodableTokenExpiryIntervalInSeconds}s lifetime. ` +
33
+ `Supply an AccessToken with an explicit expiry (e.g. from the token response 'expires_in') to control refresh scheduling.`);
34
+ }
6
35
  return {
7
36
  token,
8
- expiresOnTimestamp: exp * 1000,
37
+ expiresOnTimestamp: Date.now() + undecodableTokenExpiryIntervalInSeconds * 1000,
9
38
  };
10
39
  };
40
+ /**
41
+ * Resolves a token into an {@link AccessToken}. Expiry is taken from a caller-supplied
42
+ * `AccessToken` if present, otherwise from the JWT `exp` claim. A token that cannot be decoded
43
+ * (e.g. an encrypted/opaque token) or that lacks a usable `exp` is accepted as-is with a fallback
44
+ * expiry of `now + undecodableTokenExpiryIntervalInSeconds`; an empty/blank token throws.
45
+ */
46
+ export const parseToken = (token, undecodableTokenExpiryIntervalInSeconds = defaultUndecodableTokenExpiryIntervalInSeconds) => {
47
+ // Caller supplied an explicit expiry — use it as-is.
48
+ if (isAccessToken(token)) {
49
+ return token;
50
+ }
51
+ if (typeof token !== "string") {
52
+ throw new Error("Invalid token: expected a string or an AccessToken with a non-empty token and a finite expiresOnTimestamp.");
53
+ }
54
+ if (token.trim().length === 0) {
55
+ throw new Error("Invalid token: token must not be empty.");
56
+ }
57
+ // Prefer the expiry from a decodable JWT.
58
+ try {
59
+ const { exp } = jwtDecode(token);
60
+ if (typeof exp === "number" && Number.isFinite(exp)) {
61
+ return {
62
+ token,
63
+ expiresOnTimestamp: exp * 1000,
64
+ };
65
+ }
66
+ }
67
+ catch {
68
+ // Not decodable — treated as an opaque token below.
69
+ }
70
+ return resolveOpaqueToken(token, undecodableTokenExpiryIntervalInSeconds);
71
+ };
11
72
  //# sourceMappingURL=tokenParser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokenParser.js","sourceRoot":"","sources":["../../src/tokenParser.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAGlC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAe,EAAE;IACvD,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAW,KAAK,CAAC,CAAC;IAC3C,OAAO;QACL,KAAK;QACL,kBAAkB,EAAE,GAAG,GAAG,IAAI;KAC/B,CAAC;AACJ,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { jwtDecode } from \"jwt-decode\";\n\ninterface JwtToken {\n exp: number;\n}\n\nexport const parseToken = (token: string): AccessToken => {\n const { exp } = jwtDecode<JwtToken>(token);\n return {\n token,\n expiresOnTimestamp: exp * 1000,\n };\n};\n"]}
1
+ {"version":3,"file":"tokenParser.js","sourceRoot":"","sources":["../../src/tokenParser.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAGlC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAMrC;;;;;GAKG;AACH,MAAM,8CAA8C,GAAG,EAAE,GAAG,EAAE,CAAC;AAE/D,IAAI,8BAA8B,GAAG,KAAK,CAAC;AAE3C,8FAA8F;AAC9F,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAc,EAAwB,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAAqB,CAAC,KAAK,KAAK,QAAQ;IAC/C,KAAqB,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;IACvC,OAAQ,KAAqB,CAAC,kBAAkB,KAAK,QAAQ;IAC7D,MAAM,CAAC,QAAQ,CAAE,KAAqB,CAAC,kBAAkB,CAAC,CAAC;AAE7D;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,CACzB,KAAa,EACb,uCAA+C,EAClC,EAAE;IACf,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QACzD,uCAAuC,IAAI,CAAC,EAC5C,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,CAAC,8BAA8B,EAAE,CAAC;QACpC,8BAA8B,GAAG,IAAI,CAAC;QACtC,MAAM,CAAC,OAAO,CACZ,oFAAoF,uCAAuC,cAAc;YACvI,0HAA0H,CAC7H,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK;QACL,kBAAkB,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,uCAAuC,GAAG,IAAI;KAChF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,KAA2B,EAC3B,0CAAkD,8CAA8C,EACnF,EAAE;IACf,qDAAqD;IACrD,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,4GAA4G,CAC7G,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAW,KAAK,CAAC,CAAC;QAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACpD,OAAO;gBACL,KAAK;gBACL,kBAAkB,EAAE,GAAG,GAAG,IAAI;aAC/B,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IAED,OAAO,kBAAkB,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;AAC5E,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken } from \"@azure/core-auth\";\nimport { jwtDecode } from \"jwt-decode\";\nimport { logger } from \"./logger.js\";\n\ninterface JwtToken {\n exp: number;\n}\n\n/**\n * Default lifetime (in seconds) assumed for an encrypted/undecodable token when no expiry is\n * supplied. Must stay in step with `defaultExpiringSoonInterval` (10 min) in\n * autoRefreshTokenCredential.ts: the \"refresh an opaque token at half-life\" behavior relies on a\n * fresh fallback token immediately landing in the expiring-soon window.\n */\nconst defaultUndecodableTokenExpiryIntervalInSeconds = 10 * 60;\n\nlet undecodableTokenWarningEmitted = false;\n\n/** Type guard for a caller-supplied {@link AccessToken} (carries a valid explicit expiry). */\nexport const isAccessToken = (value: unknown): value is AccessToken =>\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AccessToken).token === \"string\" &&\n (value as AccessToken).token.length > 0 &&\n typeof (value as AccessToken).expiresOnTimestamp === \"number\" &&\n Number.isFinite((value as AccessToken).expiresOnTimestamp);\n\n/**\n * Accepts a token that can't be decoded (encrypted/opaque, or a JWT without a usable `exp`) and\n * assigns a fallback expiry of `now + undecodableTokenExpiryIntervalInSeconds`. Emits a one-time\n * warning; throws if the interval is not a positive, finite number.\n */\nconst resolveOpaqueToken = (\n token: string,\n undecodableTokenExpiryIntervalInSeconds: number,\n): AccessToken => {\n if (\n !Number.isFinite(undecodableTokenExpiryIntervalInSeconds) ||\n undecodableTokenExpiryIntervalInSeconds <= 0\n ) {\n throw new Error(\"undecodableTokenExpiryIntervalInSeconds must be a positive number.\");\n }\n if (!undecodableTokenWarningEmitted) {\n undecodableTokenWarningEmitted = true;\n logger.warning(\n `Received a token that could not be decoded; treating it as opaque and assuming a ${undecodableTokenExpiryIntervalInSeconds}s lifetime. ` +\n `Supply an AccessToken with an explicit expiry (e.g. from the token response 'expires_in') to control refresh scheduling.`,\n );\n }\n return {\n token,\n expiresOnTimestamp: Date.now() + undecodableTokenExpiryIntervalInSeconds * 1000,\n };\n};\n\n/**\n * Resolves a token into an {@link AccessToken}. Expiry is taken from a caller-supplied\n * `AccessToken` if present, otherwise from the JWT `exp` claim. A token that cannot be decoded\n * (e.g. an encrypted/opaque token) or that lacks a usable `exp` is accepted as-is with a fallback\n * expiry of `now + undecodableTokenExpiryIntervalInSeconds`; an empty/blank token throws.\n */\nexport const parseToken = (\n token: string | AccessToken,\n undecodableTokenExpiryIntervalInSeconds: number = defaultUndecodableTokenExpiryIntervalInSeconds,\n): AccessToken => {\n // Caller supplied an explicit expiry — use it as-is.\n if (isAccessToken(token)) {\n return token;\n }\n if (typeof token !== \"string\") {\n throw new Error(\n \"Invalid token: expected a string or an AccessToken with a non-empty token and a finite expiresOnTimestamp.\",\n );\n }\n if (token.trim().length === 0) {\n throw new Error(\"Invalid token: token must not be empty.\");\n }\n\n // Prefer the expiry from a decodable JWT.\n try {\n const { exp } = jwtDecode<JwtToken>(token);\n if (typeof exp === \"number\" && Number.isFinite(exp)) {\n return {\n token,\n expiresOnTimestamp: exp * 1000,\n };\n }\n } catch {\n // Not decodable — treated as an opaque token below.\n }\n\n return resolveOpaqueToken(token, undecodableTokenExpiryIntervalInSeconds);\n};\n"]}
@@ -6,25 +6,41 @@ import type { AccessToken } from "@azure/core-auth";
6
6
  */
7
7
  export interface CommunicationTokenRefreshOptions {
8
8
  /**
9
- * Callback function that returns a string JWT token acquired from the Communication Identity API.
9
+ * Callback that returns a token acquired from the Communication Identity API.
10
10
  * The returned token must be valid (expiration date must be in the future).
11
+ *
12
+ * Return a `string` to have the expiry read from the JWT, or an `AccessToken`
13
+ * to supply the expiry explicitly (required for encrypted access tokens that
14
+ * cannot be decoded).
11
15
  */
12
- tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string>;
16
+ tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;
13
17
  /**
14
- * Optional token to initialize.
18
+ * Optional token to initialize. May be a `string` or an `AccessToken` carrying
19
+ * an explicit expiry.
15
20
  */
16
- token?: string;
21
+ token?: string | AccessToken;
17
22
  /**
18
23
  * Indicates whether the token should be proactively renewed prior to expiry or only renew on demand.
19
24
  * By default false.
20
25
  */
21
26
  refreshProactively?: boolean;
27
+ /**
28
+ * Lifetime, in seconds, assumed for a returned token that cannot be decoded and
29
+ * carries no explicit expiry (e.g. an encrypted access token). Defaults to 600 (10 minutes).
30
+ *
31
+ * Set this to your tokens' real lifetime. It is not capped: a value larger than the token's
32
+ * actual lifetime means the credential refreshes later than it should and the service will
33
+ * reject the expired token. To avoid guessing, prefer returning an `AccessToken` with an
34
+ * explicit `expiresOnTimestamp` (e.g. derived from the token response's `expires_in`).
35
+ */
36
+ undecodableTokenExpiryIntervalInSeconds?: number;
22
37
  }
23
38
  export declare class AutoRefreshTokenCredential implements TokenCredential {
24
39
  private readonly refresh;
25
40
  private readonly refreshProactively;
26
41
  private readonly expiringSoonIntervalInMs;
27
42
  private readonly refreshAfterLifetimePercentage;
43
+ private readonly undecodableTokenExpiryIntervalInSeconds;
28
44
  private currentToken;
29
45
  private activeTimeout;
30
46
  private activeTokenFetching;
@@ -1 +1 @@
1
- {"version":3,"file":"autoRefreshTokenCredential.d.ts","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,EAChB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,cAAc,EAAE,CAAC,WAAW,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnE;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAOD,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqD;IAC7E,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAChF,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAyC;IAExF,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,mBAAmB,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,gCAAgC;IAY5C,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAa5E,OAAO,IAAI,IAAI;YAUR,wBAAwB;YAYxB,yBAAyB;YAazB,YAAY;IAW1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;CAG5B"}
1
+ {"version":3,"file":"autoRefreshTokenCredential.d.ts","sourceRoot":"","sources":["../../src/autoRefreshTokenCredential.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,4BAA4B,EAC5B,eAAe,EAChB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,WAAW,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;IAEjF;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;;;;OAQG;IACH,uCAAuC,CAAC,EAAE,MAAM,CAAC;CAClD;AASD,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmE;IAC3F,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAChF,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAyC;IACxF,OAAO,CAAC,QAAQ,CAAC,uCAAuC,CAAqB;IAE7E,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,mBAAmB,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,gCAAgC;IAgB5C,QAAQ,CAAC,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC;IAa5E,OAAO,IAAI,IAAI;YAUR,wBAAwB;YAexB,yBAAyB;YAazB,YAAY;IAc1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;CAG5B"}
@@ -30,15 +30,17 @@ class AutoRefreshTokenCredential {
30
30
  refreshProactively;
31
31
  expiringSoonIntervalInMs = defaultExpiringSoonInterval;
32
32
  refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;
33
+ undecodableTokenExpiryIntervalInSeconds;
33
34
  currentToken;
34
35
  activeTimeout;
35
36
  activeTokenFetching = null;
36
37
  activeTokenUpdating = null;
37
38
  disposed = false;
38
39
  constructor(refreshArgs) {
39
- const { tokenRefresher, token, refreshProactively } = refreshArgs;
40
+ const { tokenRefresher, token, refreshProactively, undecodableTokenExpiryIntervalInSeconds } = refreshArgs;
40
41
  this.refresh = tokenRefresher;
41
- this.currentToken = token ? (0, import_tokenParser.parseToken)(token) : expiredToken;
42
+ this.undecodableTokenExpiryIntervalInSeconds = undecodableTokenExpiryIntervalInSeconds;
43
+ this.currentToken = token ? (0, import_tokenParser.parseToken)(token, this.undecodableTokenExpiryIntervalInSeconds) : expiredToken;
42
44
  this.refreshProactively = refreshProactively ?? false;
43
45
  if (this.refreshProactively) {
44
46
  this.scheduleRefresh();
@@ -59,11 +61,14 @@ class AutoRefreshTokenCredential {
59
61
  this.activeTokenFetching = null;
60
62
  this.activeTokenUpdating = null;
61
63
  this.currentToken = expiredToken;
62
- if (this.activeTimeout) {
64
+ if (this.activeTimeout !== void 0) {
63
65
  clearTimeout(this.activeTimeout);
64
66
  }
65
67
  }
66
68
  async updateTokenAndReschedule(abortSignal) {
69
+ if (this.disposed) {
70
+ return;
71
+ }
67
72
  if (this.activeTokenUpdating) {
68
73
  return this.activeTokenUpdating;
69
74
  }
@@ -89,7 +94,10 @@ class AutoRefreshTokenCredential {
89
94
  if (!this.activeTokenFetching) {
90
95
  this.activeTokenFetching = this.refresh(abortSignal);
91
96
  }
92
- return (0, import_tokenParser.parseToken)(await this.activeTokenFetching);
97
+ return (0, import_tokenParser.parseToken)(
98
+ await this.activeTokenFetching,
99
+ this.undecodableTokenExpiryIntervalInSeconds
100
+ );
93
101
  } finally {
94
102
  this.activeTokenFetching = null;
95
103
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
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 that returns a token acquired from the Communication Identity API.\n * The returned token must be valid (expiration date must be in the future).\n *\n * Return a `string` to have the expiry read from the JWT, or an `AccessToken`\n * to supply the expiry explicitly (required for encrypted access tokens that\n * cannot be decoded).\n */\n tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;\n\n /**\n * Optional token to initialize. May be a `string` or an `AccessToken` carrying\n * an explicit expiry.\n */\n token?: string | AccessToken;\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 /**\n * Lifetime, in seconds, assumed for a returned token that cannot be decoded and\n * carries no explicit expiry (e.g. an encrypted access token). Defaults to 600 (10 minutes).\n *\n * Set this to your tokens' real lifetime. It is not capped: a value larger than the token's\n * actual lifetime means the credential refreshes later than it should and the service will\n * reject the expired token. To avoid guessing, prefer returning an `AccessToken` with an\n * explicit `expiresOnTimestamp` (e.g. derived from the token response's `expires_in`).\n */\n undecodableTokenExpiryIntervalInSeconds?: number;\n}\n\nconst expiredToken = { token: \"\", expiresOnTimestamp: -10 };\nconst minutesToMs = (minutes: number): number => minutes * 1000 * 60;\n// Must stay in step with `defaultUndecodableTokenExpiryIntervalInSeconds` (600s) in tokenParser.ts \u2014\n// an opaque token's fallback expiry is expected to land in this expiring-soon window.\nconst defaultExpiringSoonInterval = minutesToMs(10);\nconst defaultRefreshAfterLifetimePercentage = 0.5;\n\nexport class AutoRefreshTokenCredential implements TokenCredential {\n private readonly refresh: (abortSignal?: AbortSignalLike) => Promise<string | AccessToken>;\n private readonly refreshProactively: boolean;\n private readonly expiringSoonIntervalInMs: number = defaultExpiringSoonInterval;\n private readonly refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;\n private readonly undecodableTokenExpiryIntervalInSeconds: number | undefined;\n\n private currentToken: AccessToken;\n private activeTimeout: ReturnType<typeof setTimeout> | undefined;\n private activeTokenFetching: Promise<string | AccessToken> | null = null;\n private activeTokenUpdating: Promise<void> | null = null;\n private disposed = false;\n\n constructor(refreshArgs: CommunicationTokenRefreshOptions) {\n const { tokenRefresher, token, refreshProactively, undecodableTokenExpiryIntervalInSeconds } =\n refreshArgs;\n\n this.refresh = tokenRefresher;\n this.undecodableTokenExpiryIntervalInSeconds = undecodableTokenExpiryIntervalInSeconds;\n this.currentToken = token\n ? parseToken(token, this.undecodableTokenExpiryIntervalInSeconds)\n : 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 !== undefined) {\n clearTimeout(this.activeTimeout);\n }\n }\n\n private async updateTokenAndReschedule(abortSignal?: AbortSignalLike): Promise<void> {\n if (this.disposed) {\n return;\n }\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(\n await this.activeTokenFetching,\n this.undecodableTokenExpiryIntervalInSeconds,\n );\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": "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",
6
6
  "names": []
7
7
  }
@@ -10,9 +10,10 @@ export declare class AzureCommunicationTokenCredential implements CommunicationT
10
10
  private disposed;
11
11
  /**
12
12
  * Creates an instance of CommunicationTokenCredential with a static token and no proactive refreshing.
13
- * @param token - A user access token issued by Communication Services.
13
+ * @param token - A user access token issued by Communication Services. Pass an `AccessToken`
14
+ * to supply the expiry explicitly when the token cannot be decoded (e.g. an encrypted token).
14
15
  */
15
- constructor(token: string);
16
+ constructor(token: string | AccessToken);
16
17
  /**
17
18
  * Creates an instance of CommunicationTokenCredential with a lambda to get a token and options
18
19
  * to configure proactive refreshing.