msal
Version:
Microsoft Authentication Library for js
378 lines • 16.9 kB
JavaScript
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { __extends, __spreadArrays } from "tslib";
import { Constants, PersistentCacheKeys, TemporaryCacheKeys, ErrorCacheKeys, ServerHashParamKeys, SESSION_STORAGE } from "../utils/Constants";
import { AccessTokenCacheItem } from "./AccessTokenCacheItem";
import { BrowserStorage } from "./BrowserStorage";
import { RequestUtils } from "../utils/RequestUtils";
import { StringUtils } from "../utils/StringUtils";
import { IdToken } from "../IdToken";
import { ClientAuthError } from "../error/ClientAuthError";
/**
* @hidden
*/
var AuthCache = /** @class */ (function (_super) {
__extends(AuthCache, _super);
function AuthCache(clientId, cacheLocation, storeAuthStateInCookie) {
var _this = _super.call(this, cacheLocation) || this;
_this.temporaryCache = new BrowserStorage(SESSION_STORAGE);
_this.clientId = clientId;
// This is hardcoded to true for now. We may make this configurable in the future
_this.rollbackEnabled = true;
_this.migrateCacheEntries(storeAuthStateInCookie);
return _this;
}
/**
* Support roll back to old cache schema until the next major release: true by default now
* @param storeAuthStateInCookie
*/
AuthCache.prototype.migrateCacheEntries = function (storeAuthStateInCookie) {
var _this = this;
var idTokenKey = Constants.cachePrefix + "." + PersistentCacheKeys.IDTOKEN;
var clientInfoKey = Constants.cachePrefix + "." + PersistentCacheKeys.CLIENT_INFO;
var errorKey = Constants.cachePrefix + "." + ErrorCacheKeys.ERROR;
var errorDescKey = Constants.cachePrefix + "." + ErrorCacheKeys.ERROR_DESC;
var idTokenValue = _super.prototype.getItem.call(this, idTokenKey);
var idToken;
if (idTokenValue) {
try {
idToken = new IdToken(idTokenValue);
}
catch (e) {
return;
}
}
if (idToken && idToken.claims && idToken.claims.aud === this.clientId) {
var clientInfoValue = _super.prototype.getItem.call(this, clientInfoKey);
var errorValue = _super.prototype.getItem.call(this, errorKey);
var errorDescValue = _super.prototype.getItem.call(this, errorDescKey);
var values_1 = [idTokenValue, clientInfoValue, errorValue, errorDescValue];
var keysToMigrate = [PersistentCacheKeys.IDTOKEN, PersistentCacheKeys.CLIENT_INFO, ErrorCacheKeys.ERROR, ErrorCacheKeys.ERROR_DESC];
keysToMigrate.forEach(function (cacheKey, index) { return _this.duplicateCacheEntry(cacheKey, values_1[index], storeAuthStateInCookie); });
}
};
/**
* Utility function to help with roll back keys
* @param newKey
* @param value
* @param storeAuthStateInCookie
*/
AuthCache.prototype.duplicateCacheEntry = function (newKey, value, storeAuthStateInCookie) {
if (value) {
this.setItem(newKey, value, storeAuthStateInCookie);
}
};
/**
* Prepend msal.<client-id> to each key; Skip for any JSON object as Key (defined schemas do not need the key appended: AccessToken Keys or the upcoming schema)
* @param key
* @param addInstanceId
*/
AuthCache.prototype.generateCacheKey = function (key, addInstanceId) {
try {
// Defined schemas do not need the key appended
JSON.parse(key);
return key;
}
catch (e) {
if (key.indexOf("" + Constants.cachePrefix) === 0 || key.indexOf(Constants.adalIdToken) === 0) {
return key;
}
return addInstanceId ? Constants.cachePrefix + "." + this.clientId + "." + key : Constants.cachePrefix + "." + key;
}
};
/**
* Validates that the input cache key contains the account search terms (clientId and homeAccountIdentifier) and
* then whether or not it contains the "scopes", depending on the token type being searched for. With matching account
* search terms, Access Token search tries to match the "scopes" keyword, while Id Token search expects "scopes" to not be included.
* @param key
* @param clientId
* @param homeAccountIdentifier
* @param tokenType
*/
AuthCache.prototype.matchKeyForType = function (key, clientId, homeAccountIdentifier, tokenType) {
// All valid token cache item keys are valid JSON objects, ignore keys that aren't
var parsedKey = StringUtils.validateAndParseJsonCacheKey(key);
if (!parsedKey) {
return null;
}
// Does the cache item match the request account
var accountMatches = key.match(clientId) && key.match(homeAccountIdentifier);
// Does the cache item match the requested token type
var tokenTypeMatches = false;
switch (tokenType) {
case ServerHashParamKeys.ACCESS_TOKEN:
// Cache item is an access token if scopes are included in the cache item key
tokenTypeMatches = !!key.match(Constants.scopes);
break;
case ServerHashParamKeys.ID_TOKEN:
// Cache may be an ID token if scopes are NOT included in the cache item key
tokenTypeMatches = !key.match(Constants.scopes);
break;
}
return (accountMatches && tokenTypeMatches) ? parsedKey : null;
};
/**
* add value to storage
* @param key
* @param value
* @param enableCookieStorage
*/
AuthCache.prototype.setItem = function (key, value, enableCookieStorage) {
_super.prototype.setItem.call(this, this.generateCacheKey(key, true), value, enableCookieStorage);
// Values stored in cookies will have rollback disabled to minimize cookie length
if (this.rollbackEnabled && !enableCookieStorage) {
_super.prototype.setItem.call(this, this.generateCacheKey(key, false), value, enableCookieStorage);
}
};
/**
* get one item by key from storage
* @param key
* @param enableCookieStorage
*/
AuthCache.prototype.getItem = function (key, enableCookieStorage) {
return _super.prototype.getItem.call(this, this.generateCacheKey(key, true), enableCookieStorage);
};
/**
* remove value from storage
* @param key
*/
AuthCache.prototype.removeItem = function (key) {
this.temporaryCache.removeItem(this.generateCacheKey(key, true));
_super.prototype.removeItem.call(this, this.generateCacheKey(key, true));
if (this.rollbackEnabled) {
_super.prototype.removeItem.call(this, this.generateCacheKey(key, false));
}
};
/**
* Sets temporary cache value
* @param key
* @param value
* @param enableCookieStorage
*/
AuthCache.prototype.setTemporaryItem = function (key, value, enableCookieStorage) {
this.temporaryCache.setItem(this.generateCacheKey(key, true), value, enableCookieStorage);
};
/**
* Gets temporary cache value
* @param key
* @param enableCookieStorage
*/
AuthCache.prototype.getTemporaryItem = function (key, enableCookieStorage) {
return this.temporaryCache.getItem(this.generateCacheKey(key, true), enableCookieStorage);
};
/**
* Reset the cache items
*/
AuthCache.prototype.resetCacheItems = function () {
var storage = window[this.cacheLocation];
var key;
for (key in storage) {
// Check if key contains msal prefix; For now, we are clearing all cache items created by MSAL.js
if (storage.hasOwnProperty(key) && (key.indexOf(Constants.cachePrefix) !== -1)) {
_super.prototype.removeItem.call(this, key);
// TODO: Clear cache based on client id (clarify use cases where this is needed)
}
}
};
/**
* Reset all temporary cache items
*/
AuthCache.prototype.resetTempCacheItems = function (state) {
var _this = this;
var stateId = state && RequestUtils.parseLibraryState(state).id;
var isTokenRenewalInProgress = this.tokenRenewalInProgress(state);
var storage = window[this.cacheLocation];
// check state and remove associated cache
if (stateId && !isTokenRenewalInProgress) {
Object.keys(storage).forEach(function (key) {
if (key.indexOf(stateId) !== -1) {
_this.removeItem(key);
_super.prototype.clearItemCookie.call(_this, key);
}
});
}
// delete the interaction status cache
this.setInteractionInProgress(false);
this.removeItem(TemporaryCacheKeys.REDIRECT_REQUEST);
};
/**
* Set cookies for IE
* @param cName
* @param cValue
* @param expires
*/
AuthCache.prototype.setItemCookie = function (cName, cValue, expires) {
_super.prototype.setItemCookie.call(this, this.generateCacheKey(cName, true), cValue, expires);
if (this.rollbackEnabled) {
_super.prototype.setItemCookie.call(this, this.generateCacheKey(cName, false), cValue, expires);
}
};
AuthCache.prototype.clearItemCookie = function (cName) {
_super.prototype.clearItemCookie.call(this, this.generateCacheKey(cName, true));
if (this.rollbackEnabled) {
_super.prototype.clearItemCookie.call(this, this.generateCacheKey(cName, false));
}
};
/**
* get one item by key from cookies
* @param cName
*/
AuthCache.prototype.getItemCookie = function (cName) {
return _super.prototype.getItemCookie.call(this, this.generateCacheKey(cName, true));
};
/**
* Get all tokens of a certain type from the cache
* @param clientId
* @param homeAccountIdentifier
* @param tokenType
*/
AuthCache.prototype.getAllTokensByType = function (clientId, homeAccountIdentifier, tokenType) {
var _this = this;
var results = Object.keys(window[this.cacheLocation]).reduce(function (tokens, key) {
var matchedTokenKey = _this.matchKeyForType(key, clientId, homeAccountIdentifier, tokenType);
if (matchedTokenKey) {
var value = _this.getItem(key);
if (value) {
try {
var newAccessTokenCacheItem = new AccessTokenCacheItem(matchedTokenKey, JSON.parse(value));
return tokens.concat([newAccessTokenCacheItem]);
}
catch (err) {
// Skip cache items with non-valid JSON values
return tokens;
}
}
}
return tokens;
}, []);
return results;
};
/**
* Get all access tokens in the cache
* @param clientId
* @param homeAccountIdentifier
*/
AuthCache.prototype.getAllAccessTokens = function (clientId, homeAccountIdentifier) {
return this.getAllTokensByType(clientId, homeAccountIdentifier, ServerHashParamKeys.ACCESS_TOKEN);
};
/**
* Get all id tokens in the cache in the form of AccessTokenCacheItem objects so they are
* in a normalized format and can make use of the existing cached access token validation logic
*/
AuthCache.prototype.getAllIdTokens = function (clientId, homeAccountIdentifier) {
return this.getAllTokensByType(clientId, homeAccountIdentifier, ServerHashParamKeys.ID_TOKEN);
};
/**
* Get all access and ID tokens in the cache
* @param clientId
* @param homeAccountIdentifier
*/
AuthCache.prototype.getAllTokens = function (clientId, homeAccountIdentifier) {
var accessTokens = this.getAllAccessTokens(clientId, homeAccountIdentifier);
var idTokens = this.getAllIdTokens(clientId, homeAccountIdentifier);
return __spreadArrays(accessTokens, idTokens);
};
/**
* Returns whether or not interaction is currently in progress. Optionally scope it to just this clientId
* @param forThisClient
*/
AuthCache.prototype.isInteractionInProgress = function (matchClientId) {
var clientId = this.getInteractionInProgress();
if (matchClientId) {
return clientId === this.clientId;
}
else {
return !!clientId;
}
};
/**
* Returns the clientId of the interaction currently in progress
*/
AuthCache.prototype.getInteractionInProgress = function () {
return this.getTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.INTERACTION_STATUS, false));
};
/**
* Sets interaction in progress state
* @param isInProgress
*/
AuthCache.prototype.setInteractionInProgress = function (newInProgressValue) {
if (newInProgressValue) {
if (this.isInteractionInProgress(false)) {
throw ClientAuthError.createAcquireTokenInProgressError();
}
else {
// Ensure we don't overwrite interaction in progress for a different clientId
this.setTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.INTERACTION_STATUS, false), this.clientId);
}
}
else if (!newInProgressValue && this.isInteractionInProgress(true)) {
// Only remove if the current in progress interaction is for this clientId
this.removeItem(this.generateCacheKey(TemporaryCacheKeys.INTERACTION_STATUS, false));
}
};
/**
* Return if the token renewal is still in progress
*
* @param stateValue
*/
AuthCache.prototype.tokenRenewalInProgress = function (stateValue) {
var renewStatus = this.getItem(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.RENEW_STATUS, stateValue));
return !!(renewStatus && renewStatus === Constants.inProgress);
};
/**
* Clear all cookies
*/
AuthCache.prototype.clearMsalCookie = function (state) {
var _this = this;
/*
* If state is truthy, remove values associated with that request.
* Otherwise, remove all MSAL cookies.
*/
if (state) {
this.clearItemCookie(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.NONCE_IDTOKEN, state));
this.clearItemCookie(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.STATE_LOGIN, state));
this.clearItemCookie(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.LOGIN_REQUEST, state));
this.clearItemCookie(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.STATE_ACQ_TOKEN, state));
}
else {
var cookies = document.cookie.split(";");
cookies.forEach(function (cookieString) {
var cookieName = cookieString.trim().split("=")[0];
if (cookieName.indexOf(Constants.cachePrefix) > -1) {
_super.prototype.clearItemCookie.call(_this, cookieName);
}
});
}
};
/**
* Create acquireTokenAccountKey to cache account object
* @param accountId
* @param state
*/
AuthCache.generateAcquireTokenAccountKey = function (accountId, state) {
var stateId = RequestUtils.parseLibraryState(state).id;
return "" + TemporaryCacheKeys.ACQUIRE_TOKEN_ACCOUNT + Constants.resourceDelimiter + accountId + Constants.resourceDelimiter + stateId;
};
/**
* Create authorityKey to cache authority
* @param state
*/
AuthCache.generateAuthorityKey = function (state) {
return AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.AUTHORITY, state);
};
/**
* Generates the cache key for temporary cache items, using request state
* @param tempCacheKey Cache key prefix
* @param state Request state value
*/
AuthCache.generateTemporaryCacheKey = function (tempCacheKey, state) {
// Use the state id (a guid), in the interest of shorter key names, which is important for cookies.
var stateId = RequestUtils.parseLibraryState(state).id;
return "" + tempCacheKey + Constants.resourceDelimiter + stateId;
};
return AuthCache;
}(BrowserStorage));
export { AuthCache };
//# sourceMappingURL=AuthCache.js.map