Commit iniziale
This commit is contained in:
162
node_modules/@azure/keyvault-common/dist/browser/tokenCycler.js
generated
vendored
Normal file
162
node_modules/@azure/keyvault-common/dist/browser/tokenCycler.js
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
import { delay } from "@azure/core-util";
|
||||
// Default options for the cycler if none are provided
|
||||
export const DEFAULT_CYCLER_OPTIONS = {
|
||||
forcedRefreshWindowInMs: 1000, // Force waiting for a refresh 1s before the token expires
|
||||
retryIntervalInMs: 3000, // Allow refresh attempts every 3s
|
||||
refreshWindowInMs: 1000 * 60 * 2, // Start refreshing 2m before expiry
|
||||
};
|
||||
/**
|
||||
* Converts an an unreliable access token getter (which may resolve with null)
|
||||
* into an AccessTokenGetter by retrying the unreliable getter in a regular
|
||||
* interval.
|
||||
*
|
||||
* @param getAccessToken - A function that produces a promise of an access token that may fail by returning null.
|
||||
* @param retryIntervalInMs - The time (in milliseconds) to wait between retry attempts.
|
||||
* @param refreshTimeout - The timestamp after which the refresh attempt will fail, throwing an exception.
|
||||
* @returns - A promise that, if it resolves, will resolve with an access token.
|
||||
*/
|
||||
async function beginRefresh(getAccessToken, retryIntervalInMs, refreshTimeout) {
|
||||
// This wrapper handles exceptions gracefully as long as we haven't exceeded
|
||||
// the timeout.
|
||||
async function tryGetAccessToken() {
|
||||
if (Date.now() < refreshTimeout) {
|
||||
try {
|
||||
return await getAccessToken();
|
||||
}
|
||||
catch (_a) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const finalToken = await getAccessToken();
|
||||
// Timeout is up, so throw if it's still null
|
||||
if (finalToken === null) {
|
||||
throw new Error("Failed to refresh access token.");
|
||||
}
|
||||
return finalToken;
|
||||
}
|
||||
}
|
||||
let token = await tryGetAccessToken();
|
||||
while (token === null) {
|
||||
await delay(retryIntervalInMs);
|
||||
token = await tryGetAccessToken();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
/**
|
||||
* Creates a token cycler from a credential, scopes, and optional settings.
|
||||
*
|
||||
* A token cycler represents a way to reliably retrieve a valid access token
|
||||
* from a TokenCredential. It will handle initializing the token, refreshing it
|
||||
* when it nears expiration, and synchronizes refresh attempts to avoid
|
||||
* concurrency hazards.
|
||||
*
|
||||
* @param credential - the underlying TokenCredential that provides the access
|
||||
* token
|
||||
* @param tokenCyclerOptions - optionally override default settings for the cycler
|
||||
*
|
||||
* @returns - a function that reliably produces a valid access token
|
||||
*/
|
||||
export function createTokenCycler(credential, tokenCyclerOptions) {
|
||||
let refreshWorker = null;
|
||||
let token = null;
|
||||
let tenantId;
|
||||
const options = Object.assign(Object.assign({}, DEFAULT_CYCLER_OPTIONS), tokenCyclerOptions);
|
||||
/**
|
||||
* This little holder defines several predicates that we use to construct
|
||||
* the rules of refreshing the token.
|
||||
*/
|
||||
const cycler = {
|
||||
/**
|
||||
* Produces true if a refresh job is currently in progress.
|
||||
*/
|
||||
get isRefreshing() {
|
||||
return refreshWorker !== null;
|
||||
},
|
||||
/**
|
||||
* Produces true if the cycler SHOULD refresh (we are within the refresh
|
||||
* window and not already refreshing)
|
||||
*/
|
||||
get shouldRefresh() {
|
||||
var _a;
|
||||
if (cycler.isRefreshing) {
|
||||
return false;
|
||||
}
|
||||
if ((token === null || token === void 0 ? void 0 : token.refreshAfterTimestamp) && token.refreshAfterTimestamp < Date.now()) {
|
||||
return true;
|
||||
}
|
||||
return ((_a = token === null || token === void 0 ? void 0 : token.expiresOnTimestamp) !== null && _a !== void 0 ? _a : 0) - options.refreshWindowInMs < Date.now();
|
||||
},
|
||||
/**
|
||||
* Produces true if the cycler MUST refresh (null or nearly-expired
|
||||
* token).
|
||||
*/
|
||||
get mustRefresh() {
|
||||
return (token === null || token.expiresOnTimestamp - options.forcedRefreshWindowInMs < Date.now());
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Starts a refresh job or returns the existing job if one is already
|
||||
* running.
|
||||
*/
|
||||
function refresh(scopes, getTokenOptions) {
|
||||
var _a;
|
||||
if (!cycler.isRefreshing) {
|
||||
// We bind `scopes` here to avoid passing it around a lot
|
||||
const tryGetAccessToken = () => credential.getToken(scopes, getTokenOptions);
|
||||
// Take advantage of promise chaining to insert an assignment to `token`
|
||||
// before the refresh can be considered done.
|
||||
refreshWorker = beginRefresh(tryGetAccessToken, options.retryIntervalInMs,
|
||||
// If we don't have a token, then we should timeout immediately
|
||||
(_a = token === null || token === void 0 ? void 0 : token.expiresOnTimestamp) !== null && _a !== void 0 ? _a : Date.now())
|
||||
.then((_token) => {
|
||||
refreshWorker = null;
|
||||
token = _token;
|
||||
tenantId = getTokenOptions.tenantId;
|
||||
return token;
|
||||
})
|
||||
.catch((reason) => {
|
||||
// We also should reset the refresher if we enter a failed state. All
|
||||
// existing awaiters will throw, but subsequent requests will start a
|
||||
// new retry chain.
|
||||
refreshWorker = null;
|
||||
token = null;
|
||||
tenantId = undefined;
|
||||
throw reason;
|
||||
});
|
||||
}
|
||||
return refreshWorker;
|
||||
}
|
||||
return async (scopes, tokenOptions) => {
|
||||
//
|
||||
// Simple rules:
|
||||
// - If we MUST refresh, then return the refresh task, blocking
|
||||
// the pipeline until a token is available.
|
||||
// - If we SHOULD refresh, then run refresh but don't return it
|
||||
// (we can still use the cached token).
|
||||
// - Return the token, since it's fine if we didn't return in
|
||||
// step 1.
|
||||
//
|
||||
const hasClaimChallenge = Boolean(tokenOptions.claims);
|
||||
const tenantIdChanged = tenantId !== tokenOptions.tenantId;
|
||||
if (hasClaimChallenge) {
|
||||
// If we've received a claim, we know the existing token isn't valid
|
||||
// We want to clear it so that that refresh worker won't use the old expiration time as a timeout
|
||||
token = null;
|
||||
}
|
||||
// If the tenantId passed in token options is different to the one we have
|
||||
// Or if we are in claim challenge and the token was rejected and a new access token need to be issued, we need to
|
||||
// refresh the token with the new tenantId or token.
|
||||
const mustRefresh = tenantIdChanged || hasClaimChallenge || cycler.mustRefresh;
|
||||
if (mustRefresh) {
|
||||
return refresh(scopes, tokenOptions);
|
||||
}
|
||||
if (cycler.shouldRefresh) {
|
||||
refresh(scopes, tokenOptions);
|
||||
}
|
||||
return token;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=tokenCycler.js.map
|
||||
Reference in New Issue
Block a user