UNPKG

@azure/communication-common

Version:
105 lines 3.88 kB
// 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 { refresh; refreshProactively; expiringSoonIntervalInMs = defaultExpiringSoonInterval; refreshAfterLifetimePercentage = defaultRefreshAfterLifetimePercentage; currentToken; activeTimeout; activeTokenFetching = null; activeTokenUpdating = null; disposed = false; constructor(refreshArgs) { const { tokenRefresher, token, refreshProactively } = refreshArgs; this.refresh = tokenRefresher; this.currentToken = token ? parseToken(token) : expiredToken; this.refreshProactively = 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?.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