@azure/communication-common
Advanced tools
Comparing version 1.1.1-alpha.20220113.1 to 1.2.0-alpha.20220128.1
# Release History | ||
## 1.1.1 (Unreleased) | ||
## 1.2.0 (Unreleased) | ||
### Features Added | ||
- Optimization added: When the proactive refreshing is enabled and the token refresher fails to provide a token that's not about to expire soon, the subsequent refresh attempts will be scheduled for when the token reaches half of its remaining lifetime until a token with long enough validity (>10 minutes) is obtained. | ||
### Breaking Changes | ||
@@ -8,0 +10,0 @@ |
@@ -6,6 +6,8 @@ // Copyright (c) Microsoft Corporation. | ||
const minutesToMs = (minutes) => minutes * 1000 * 60; | ||
const defaultRefreshingInterval = minutesToMs(10); | ||
const defaultExpiringSoonInterval = minutesToMs(10); | ||
const defaultRefreshAfterLifetimePercentage = 0.5; | ||
export class AutoRefreshTokenCredential { | ||
constructor(refreshArgs) { | ||
this.refreshingIntervalInMs = defaultRefreshingInterval; | ||
this.expiringSoonIntervalInMs = defaultExpiringSoonInterval; | ||
this.refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage; | ||
this.activeTokenFetching = null; | ||
@@ -23,7 +25,7 @@ this.activeTokenUpdating = null; | ||
async getToken(options) { | ||
if (!this.isCurrentTokenExpiringSoon) { | ||
if (!this.isTokenExpiringSoon(this.currentToken)) { | ||
return this.currentToken; | ||
} | ||
const updatePromise = this.updateTokenAndReschedule(options === null || options === void 0 ? void 0 : options.abortSignal); | ||
if (!this.isCurrentTokenValid) { | ||
if (!this.isTokenValid(this.currentToken)) { | ||
const updatePromise = this.updateTokenAndReschedule(options === null || options === void 0 ? void 0 : options.abortSignal); | ||
await updatePromise; | ||
@@ -55,3 +57,7 @@ } | ||
async refreshTokenAndReschedule(abortSignal) { | ||
this.currentToken = await this.refreshToken(abortSignal); | ||
const newToken = await this.refreshToken(abortSignal); | ||
if (!this.isTokenValid(newToken)) { | ||
throw new Error("The token returned from the tokenRefresher is expired."); | ||
} | ||
this.currentToken = newToken; | ||
if (this.refreshProactively) { | ||
@@ -79,13 +85,21 @@ this.scheduleRefresh(); | ||
} | ||
const timespanInMs = this.currentToken.expiresOnTimestamp - Date.now() - this.refreshingIntervalInMs; | ||
const tokenTtlInMs = this.currentToken.expiresOnTimestamp - Date.now(); | ||
let timespanInMs = null; | ||
if (this.isTokenExpiringSoon(this.currentToken)) { | ||
// Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime. | ||
timespanInMs = tokenTtlInMs * this.refreshAfterLifetimePercentage; | ||
} | ||
else { | ||
// Schedule the next refresh for when it gets in to the soon-to-expire window. | ||
timespanInMs = tokenTtlInMs - this.expiringSoonIntervalInMs; | ||
} | ||
this.activeTimeout = setTimeout(() => this.updateTokenAndReschedule(), timespanInMs); | ||
} | ||
get isCurrentTokenValid() { | ||
return this.currentToken && Date.now() < this.currentToken.expiresOnTimestamp; | ||
isTokenValid(token) { | ||
return token && Date.now() < token.expiresOnTimestamp; | ||
} | ||
get isCurrentTokenExpiringSoon() { | ||
return (!this.currentToken || | ||
Date.now() >= this.currentToken.expiresOnTimestamp - this.refreshingIntervalInMs); | ||
isTokenExpiringSoon(token) { | ||
return !token || Date.now() >= token.expiresOnTimestamp - this.expiringSoonIntervalInMs; | ||
} | ||
} | ||
//# sourceMappingURL=autoRefreshTokenCredential.js.map |
@@ -42,6 +42,8 @@ 'use strict'; | ||
const minutesToMs = (minutes) => minutes * 1000 * 60; | ||
const defaultRefreshingInterval = minutesToMs(10); | ||
const defaultExpiringSoonInterval = minutesToMs(10); | ||
const defaultRefreshAfterLifetimePercentage = 0.5; | ||
class AutoRefreshTokenCredential { | ||
constructor(refreshArgs) { | ||
this.refreshingIntervalInMs = defaultRefreshingInterval; | ||
this.expiringSoonIntervalInMs = defaultExpiringSoonInterval; | ||
this.refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage; | ||
this.activeTokenFetching = null; | ||
@@ -59,7 +61,7 @@ this.activeTokenUpdating = null; | ||
async getToken(options) { | ||
if (!this.isCurrentTokenExpiringSoon) { | ||
if (!this.isTokenExpiringSoon(this.currentToken)) { | ||
return this.currentToken; | ||
} | ||
const updatePromise = this.updateTokenAndReschedule(options === null || options === void 0 ? void 0 : options.abortSignal); | ||
if (!this.isCurrentTokenValid) { | ||
if (!this.isTokenValid(this.currentToken)) { | ||
const updatePromise = this.updateTokenAndReschedule(options === null || options === void 0 ? void 0 : options.abortSignal); | ||
await updatePromise; | ||
@@ -91,3 +93,7 @@ } | ||
async refreshTokenAndReschedule(abortSignal) { | ||
this.currentToken = await this.refreshToken(abortSignal); | ||
const newToken = await this.refreshToken(abortSignal); | ||
if (!this.isTokenValid(newToken)) { | ||
throw new Error("The token returned from the tokenRefresher is expired."); | ||
} | ||
this.currentToken = newToken; | ||
if (this.refreshProactively) { | ||
@@ -115,11 +121,19 @@ this.scheduleRefresh(); | ||
} | ||
const timespanInMs = this.currentToken.expiresOnTimestamp - Date.now() - this.refreshingIntervalInMs; | ||
const tokenTtlInMs = this.currentToken.expiresOnTimestamp - Date.now(); | ||
let timespanInMs = null; | ||
if (this.isTokenExpiringSoon(this.currentToken)) { | ||
// Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime. | ||
timespanInMs = tokenTtlInMs * this.refreshAfterLifetimePercentage; | ||
} | ||
else { | ||
// Schedule the next refresh for when it gets in to the soon-to-expire window. | ||
timespanInMs = tokenTtlInMs - this.expiringSoonIntervalInMs; | ||
} | ||
this.activeTimeout = setTimeout(() => this.updateTokenAndReschedule(), timespanInMs); | ||
} | ||
get isCurrentTokenValid() { | ||
return this.currentToken && Date.now() < this.currentToken.expiresOnTimestamp; | ||
isTokenValid(token) { | ||
return token && Date.now() < token.expiresOnTimestamp; | ||
} | ||
get isCurrentTokenExpiringSoon() { | ||
return (!this.currentToken || | ||
Date.now() >= this.currentToken.expiresOnTimestamp - this.refreshingIntervalInMs); | ||
isTokenExpiringSoon(token) { | ||
return !token || Date.now() >= token.expiresOnTimestamp - this.expiringSoonIntervalInMs; | ||
} | ||
@@ -126,0 +140,0 @@ } |
{ | ||
"name": "@azure/communication-common", | ||
"version": "1.1.1-alpha.20220113.1", | ||
"version": "1.2.0-alpha.20220128.1", | ||
"description": "Common package for Azure Communication services.", | ||
@@ -5,0 +5,0 @@ "sdk-type": "client", |
@@ -28,8 +28,13 @@ # Azure Communication Common client library for JavaScript | ||
A `CommunicationTokenCredential` authenticates a user with Communication Services, such as Chat or Calling. It optionally provides an auto-refresh mechanism to ensure a continuously stable authentication state during communications. | ||
The `CommunicationTokenCredential` is an interface used to authenticate a user with Communication Services, such as Chat or Calling. | ||
It is up to you the developer to first create valid user tokens with the Azure Communication Administration library. Then you use these tokens to create a `AzureCommunicationTokenCredential`. | ||
The `AzureCommunicationTokenCredential` offers a convenient way to create a credential implementing the said interface and allows you to take advantage of the built-in auto-refresh logic. | ||
`CommunicationTokenCredential` is only the interface, please always use the `AzureCommunicationTokenCredential` constructor to create a credential and take advantage of the built-in refresh logic. | ||
Depending on your scenario, you may want to initialize the `AzureCommunicationTokenCredential` with: | ||
- a static token (suitable for short-lived clients used to e.g. send one-off Chat messages) or | ||
- a callback function that ensures a continuous authentication state during communications (ideal e.g. for long Calling sessions). | ||
The tokens supplied to the `AzureCommunicationTokenCredential` either through the constructor or via the token refresher callback can be obtained using the Azure Communication Identity library. | ||
## Examples | ||
@@ -39,2 +44,4 @@ | ||
For a short-lived clients, refreshing the token upon expiry is not necessary and the `AzureCommunicationTokenCredential` may be instantiated with a static token. | ||
```typescript | ||
@@ -48,7 +55,7 @@ const tokenCredential = new AzureCommunicationTokenCredential( | ||
Here we assume that we have a function `fetchTokenFromMyServerForUser` that makes a network request to retrieve a token string for a user. We pass it into the credential to fetch a token for Bob from our own server. Our server would use the Azure Communication Administration library to issue tokens. | ||
Here we assume that we have a function `fetchTokenFromMyServerForUser` that makes a network request to retrieve a JWT token string for a user. We pass it into the credential to fetch a token for Bob from our own server. Our server would use the Azure Communication Identity library to issue tokens. It's necessary that the `fetchTokenFromMyServerForUser` function returns a valid token (with an expiration date set in the future) at all times. | ||
```typescript | ||
const tokenCredential = new AzureCommunicationTokenCredential({ | ||
tokenRefresher: async () => fetchTokenFromMyServerForUser("bob@contoso.com") | ||
tokenRefresher: async () => fetchTokenFromMyServerForUser("bob@contoso.com"), | ||
}); | ||
@@ -64,3 +71,3 @@ ``` | ||
tokenRefresher: async () => fetchTokenFromMyServerForUser("bob@contoso.com"), | ||
refreshProactively: true | ||
refreshProactively: true, | ||
}); | ||
@@ -78,3 +85,3 @@ ``` | ||
token: | ||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM2MDB9.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs" | ||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM2MDB9.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs", | ||
}); | ||
@@ -85,2 +92,4 @@ ``` | ||
- **Invalid token specified**: Make sure the token you are passing to the `AzureCommunicationTokenCredential` constructor or to the `tokenRefresher` callback is a bare JWT token string. E.g. if you're using the [Azure Communication Identity library][invalid_token_sdk] or [REST API][invalid_token_rest] to obtain the token, make sure you're passing just the `token` part of the response object. | ||
## Next steps | ||
@@ -102,3 +111,5 @@ | ||
[azure_powershell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice | ||
[invalid_token_sdk]: https://docs.microsoft.com/javascript/api/@azure/communication-identity/communicationaccesstoken#@azure-communication-identity-communicationaccesstoken-token | ||
[invalid_token_rest]: https://docs.microsoft.com/rest/api/communication/communicationidentity/communication-identity/issue-access-token#communicationidentityaccesstoken | ||
![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-js%2Fsdk%2Fcommunication%2Fcommunication-sms%2FREADME.png) |
@@ -76,3 +76,4 @@ import { AbortSignalLike } from '@azure/core-http'; | ||
/** | ||
* Function that returns a token acquired from the Communication configuration SDK. | ||
* Callback function that returns a string JWT token acquired from the Communication Identity API. | ||
* The returned token must be valid (expiration date must be in the future). | ||
*/ | ||
@@ -79,0 +80,0 @@ tokenRefresher: (abortSignal?: AbortSignalLike) => Promise<string>; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
153141
1335
109