@azure/communication-common
Version:
Common package for Azure Communication services.
101 lines • 3.96 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { parseToken } from "./tokenParser.js";
const expiredToken = { token: "", expiresOnTimestamp: -10 };
const minutesToMs = (minutes) => minutes * 1000 * 60;
const defaultExpiringSoonInterval = minutesToMs(10);
const defaultRefreshAfterLifetimePercentage = 0.5;
export class AutoRefreshTokenCredential {
constructor(refreshArgs) {
this.expiringSoonIntervalInMs = defaultExpiringSoonInterval;
this.refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage;
this.activeTokenFetching = null;
this.activeTokenUpdating = null;
this.disposed = false;
const { tokenRefresher, token, refreshProactively } = refreshArgs;
this.refresh = tokenRefresher;
this.currentToken = token ? parseToken(token) : expiredToken;
this.refreshProactively = refreshProactively !== null && refreshProactively !== void 0 ? refreshProactively : false;
if (this.refreshProactively) {
this.scheduleRefresh();
}
}
async getToken(options) {
if (!this.isTokenExpiringSoon(this.currentToken)) {
return this.currentToken;
}
if (!this.isTokenValid(this.currentToken)) {
const updatePromise = this.updateTokenAndReschedule(options === null || options === void 0 ? void 0 : options.abortSignal);
await updatePromise;
}
return this.currentToken;
}
dispose() {
this.disposed = true;
this.activeTokenFetching = null;
this.activeTokenUpdating = null;
this.currentToken = expiredToken;
if (this.activeTimeout) {
clearTimeout(this.activeTimeout);
}
}
async updateTokenAndReschedule(abortSignal) {
if (this.activeTokenUpdating) {
return this.activeTokenUpdating;
}
this.activeTokenUpdating = this.refreshTokenAndReschedule(abortSignal);
try {
await this.activeTokenUpdating;
}
finally {
this.activeTokenUpdating = null;
}
}
async refreshTokenAndReschedule(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) {
this.scheduleRefresh();
}
}
async refreshToken(abortSignal) {
try {
if (!this.activeTokenFetching) {
this.activeTokenFetching = this.refresh(abortSignal);
}
return parseToken(await this.activeTokenFetching);
}
finally {
this.activeTokenFetching = null;
}
}
scheduleRefresh() {
if (this.disposed) {
return;
}
if (this.activeTimeout) {
clearTimeout(this.activeTimeout);
}
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);
}
isTokenValid(token) {
return token && Date.now() < token.expiresOnTimestamp;
}
isTokenExpiringSoon(token) {
return !token || Date.now() >= token.expiresOnTimestamp - this.expiringSoonIntervalInMs;
}
}
//# sourceMappingURL=autoRefreshTokenCredential.js.map