Estructura inicial del proyecto
This commit is contained in:
79
backend/node_modules/@azure/msal-browser/src/cache/AccountManager.ts
generated
vendored
Normal file
79
backend/node_modules/@azure/msal-browser/src/cache/AccountManager.ts
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { AccountInfo, AccountFilter, Logger } from "@azure/msal-common/browser";
|
||||
import { BrowserCacheManager } from "./BrowserCacheManager.js";
|
||||
|
||||
/**
|
||||
* Returns all the accounts in the cache that match the optional filter. If no filter is provided, all accounts are returned.
|
||||
* @param accountFilter - (Optional) filter to narrow down the accounts returned
|
||||
* @returns Array of AccountInfo objects in cache
|
||||
*/
|
||||
export function getAllAccounts(
|
||||
logger: Logger,
|
||||
browserStorage: BrowserCacheManager,
|
||||
isInBrowser: boolean,
|
||||
correlationId: string,
|
||||
accountFilter?: AccountFilter
|
||||
): AccountInfo[] {
|
||||
logger.verbose("getAllAccounts called", correlationId);
|
||||
return isInBrowser
|
||||
? browserStorage.getAllAccounts(accountFilter, correlationId)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first account found in the cache that matches the account filter passed in.
|
||||
* @param accountFilter
|
||||
* @returns The first account found in the cache matching the provided filter or null if no account could be found.
|
||||
*/
|
||||
export function getAccount(
|
||||
accountFilter: AccountFilter,
|
||||
logger: Logger,
|
||||
browserStorage: BrowserCacheManager,
|
||||
correlationId: string
|
||||
): AccountInfo | null {
|
||||
logger.trace("getAccount called", correlationId);
|
||||
const account: AccountInfo | null = browserStorage.getAccountInfoFilteredBy(
|
||||
accountFilter,
|
||||
correlationId
|
||||
);
|
||||
|
||||
if (account) {
|
||||
logger.verbose(
|
||||
"getAccount: Account matching provided filter found, returning",
|
||||
correlationId
|
||||
);
|
||||
return account;
|
||||
} else {
|
||||
logger.verbose(
|
||||
"getAccount: No matching account found, returning null",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account to use as the active account. If no account is passed to the acquireToken APIs, then MSAL will use this active account.
|
||||
* @param account
|
||||
*/
|
||||
export function setActiveAccount(
|
||||
account: AccountInfo | null,
|
||||
browserStorage: BrowserCacheManager,
|
||||
correlationId: string
|
||||
): void {
|
||||
browserStorage.setActiveAccount(account, correlationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently active account
|
||||
*/
|
||||
export function getActiveAccount(
|
||||
browserStorage: BrowserCacheManager,
|
||||
correlationId: string
|
||||
): AccountInfo | null {
|
||||
return browserStorage.getActiveAccount(correlationId);
|
||||
}
|
||||
173
backend/node_modules/@azure/msal-browser/src/cache/AsyncMemoryStorage.ts
generated
vendored
Normal file
173
backend/node_modules/@azure/msal-browser/src/cache/AsyncMemoryStorage.ts
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { Logger } from "@azure/msal-common/browser";
|
||||
import {
|
||||
BrowserAuthError,
|
||||
BrowserAuthErrorCodes,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import { DatabaseStorage } from "./DatabaseStorage.js";
|
||||
import { IAsyncStorage } from "./IAsyncStorage.js";
|
||||
import { MemoryStorage } from "./MemoryStorage.js";
|
||||
|
||||
/**
|
||||
* This class allows MSAL to store artifacts asynchronously using the DatabaseStorage IndexedDB wrapper,
|
||||
* backed up with the more volatile MemoryStorage object for cases in which IndexedDB may be unavailable.
|
||||
*/
|
||||
export class AsyncMemoryStorage<T> implements IAsyncStorage<T> {
|
||||
private inMemoryCache: MemoryStorage<T>;
|
||||
private indexedDBCache: DatabaseStorage<T>;
|
||||
private logger: Logger;
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.inMemoryCache = new MemoryStorage<T>();
|
||||
this.indexedDBCache = new DatabaseStorage<T>();
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private handleDatabaseAccessError(
|
||||
error: unknown,
|
||||
correlationId: string
|
||||
): void {
|
||||
if (
|
||||
error instanceof BrowserAuthError &&
|
||||
error.errorCode === BrowserAuthErrorCodes.databaseUnavailable
|
||||
) {
|
||||
this.logger.error(
|
||||
"Could not access persistent storage. This may be caused by browser privacy features which block persistent storage in third-party contexts.",
|
||||
correlationId
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the item matching the given key. Tries in-memory cache first, then in the asynchronous
|
||||
* storage object if item isn't found in-memory.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
async getItem(key: string, correlationId: string): Promise<T | null> {
|
||||
const item = this.inMemoryCache.getItem(key);
|
||||
if (!item) {
|
||||
try {
|
||||
this.logger.verbose(
|
||||
"Queried item not found in in-memory cache, now querying persistent storage.",
|
||||
correlationId
|
||||
);
|
||||
return await this.indexedDBCache.getItem(key);
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item in the in-memory cache and then tries to set it in the asynchronous
|
||||
* storage object with the given key.
|
||||
* @param key
|
||||
* @param value
|
||||
* @param correlationId
|
||||
*/
|
||||
async setItem(key: string, value: T, correlationId: string): Promise<void> {
|
||||
this.inMemoryCache.setItem(key, value);
|
||||
try {
|
||||
await this.indexedDBCache.setItem(key, value);
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the item matching the key from the in-memory cache, then tries to remove it from the asynchronous storage object.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
async removeItem(key: string, correlationId: string): Promise<void> {
|
||||
this.inMemoryCache.removeItem(key);
|
||||
try {
|
||||
await this.indexedDBCache.removeItem(key);
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the keys from the in-memory cache as an iterable array of strings. If no keys are found, query the keys in the
|
||||
* asynchronous storage object.
|
||||
* @param correlationId
|
||||
*/
|
||||
async getKeys(correlationId: string): Promise<string[]> {
|
||||
const cacheKeys = this.inMemoryCache.getKeys();
|
||||
if (cacheKeys.length === 0) {
|
||||
try {
|
||||
this.logger.verbose(
|
||||
"In-memory cache is empty, now querying persistent storage.",
|
||||
correlationId
|
||||
);
|
||||
return await this.indexedDBCache.getKeys();
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
}
|
||||
}
|
||||
return cacheKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false if the given key is present in the cache.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
async containsKey(key: string, correlationId: string): Promise<boolean> {
|
||||
const containsKey = this.inMemoryCache.containsKey(key);
|
||||
if (!containsKey) {
|
||||
try {
|
||||
this.logger.verbose(
|
||||
"Key not found in in-memory cache, now querying persistent storage.",
|
||||
correlationId
|
||||
);
|
||||
return await this.indexedDBCache.containsKey(key);
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
}
|
||||
}
|
||||
return containsKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears in-memory Map
|
||||
* @param correlationId
|
||||
*/
|
||||
clearInMemory(correlationId: string): void {
|
||||
// InMemory cache is a Map instance, clear is straightforward
|
||||
this.logger.verbose(`Deleting in-memory keystore`, correlationId);
|
||||
this.inMemoryCache.clear();
|
||||
this.logger.verbose(`In-memory keystore deleted`, correlationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to delete the IndexedDB database
|
||||
* @param correlationId
|
||||
* @returns
|
||||
*/
|
||||
async clearPersistent(correlationId: string): Promise<boolean> {
|
||||
try {
|
||||
this.logger.verbose("Deleting persistent keystore", correlationId);
|
||||
const dbDeleted = await this.indexedDBCache.deleteDatabase();
|
||||
if (dbDeleted) {
|
||||
this.logger.verbose(
|
||||
"Persistent keystore deleted",
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
|
||||
return dbDeleted;
|
||||
} catch (e) {
|
||||
this.handleDatabaseAccessError(e, correlationId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
2419
backend/node_modules/@azure/msal-browser/src/cache/BrowserCacheManager.ts
generated
vendored
Normal file
2419
backend/node_modules/@azure/msal-browser/src/cache/BrowserCacheManager.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
60
backend/node_modules/@azure/msal-browser/src/cache/CacheHelpers.ts
generated
vendored
Normal file
60
backend/node_modules/@azure/msal-browser/src/cache/CacheHelpers.ts
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { TokenKeys } from "@azure/msal-common/browser";
|
||||
import { IWindowStorage } from "./IWindowStorage.js";
|
||||
import * as CacheKeys from "./CacheKeys.js";
|
||||
|
||||
/**
|
||||
* Returns a list of cache keys for all known accounts
|
||||
* @param storage
|
||||
* @returns
|
||||
*/
|
||||
export function getAccountKeys(
|
||||
storage: IWindowStorage<string>,
|
||||
schemaVersion?: number
|
||||
): Array<string> {
|
||||
const accountKeys = storage.getItem(
|
||||
CacheKeys.getAccountKeysCacheKey(schemaVersion)
|
||||
);
|
||||
if (accountKeys) {
|
||||
return JSON.parse(accountKeys);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of cache keys for all known tokens
|
||||
* @param clientId
|
||||
* @param storage
|
||||
* @returns
|
||||
*/
|
||||
export function getTokenKeys(
|
||||
clientId: string,
|
||||
storage: IWindowStorage<string>,
|
||||
schemaVersion?: number
|
||||
): TokenKeys {
|
||||
const item = storage.getItem(
|
||||
CacheKeys.getTokenKeysCacheKey(clientId, schemaVersion)
|
||||
);
|
||||
if (item) {
|
||||
const tokenKeys = JSON.parse(item);
|
||||
if (
|
||||
tokenKeys &&
|
||||
tokenKeys.hasOwnProperty("idToken") &&
|
||||
tokenKeys.hasOwnProperty("accessToken") &&
|
||||
tokenKeys.hasOwnProperty("refreshToken")
|
||||
) {
|
||||
return tokenKeys as TokenKeys;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
idToken: [],
|
||||
accessToken: [],
|
||||
refreshToken: [],
|
||||
};
|
||||
}
|
||||
40
backend/node_modules/@azure/msal-browser/src/cache/CacheKeys.ts
generated
vendored
Normal file
40
backend/node_modules/@azure/msal-browser/src/cache/CacheKeys.ts
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const PREFIX = "msal";
|
||||
const BROWSER_PREFIX = "browser";
|
||||
export const CACHE_KEY_SEPARATOR = "|";
|
||||
export const CREDENTIAL_SCHEMA_VERSION = 3;
|
||||
export const ACCOUNT_SCHEMA_VERSION = 3;
|
||||
|
||||
export const LOG_LEVEL_CACHE_KEY = `${PREFIX}.${BROWSER_PREFIX}.log.level`;
|
||||
export const LOG_PII_CACHE_KEY = `${PREFIX}.${BROWSER_PREFIX}.log.pii`;
|
||||
export const BROWSER_PERF_ENABLED_KEY = `${PREFIX}.${BROWSER_PREFIX}.performance.enabled`;
|
||||
export const PLATFORM_AUTH_DOM_SUPPORT = `${PREFIX}.${BROWSER_PREFIX}.platform.auth.dom`;
|
||||
export const VERSION_CACHE_KEY = `${PREFIX}.version`;
|
||||
export const ACCOUNT_KEYS = "account.keys";
|
||||
export const TOKEN_KEYS = "token.keys";
|
||||
export const SSO_CAPABLE = `${PREFIX}.${BROWSER_PREFIX}.sso.capable`;
|
||||
|
||||
export function getAccountKeysCacheKey(
|
||||
schema: number = ACCOUNT_SCHEMA_VERSION
|
||||
): string {
|
||||
if (schema < 1) {
|
||||
return `${PREFIX}.${ACCOUNT_KEYS}`;
|
||||
}
|
||||
|
||||
return `${PREFIX}.${schema}.${ACCOUNT_KEYS}`;
|
||||
}
|
||||
|
||||
export function getTokenKeysCacheKey(
|
||||
clientId: string,
|
||||
schema: number = CREDENTIAL_SCHEMA_VERSION
|
||||
): string {
|
||||
if (schema < 1) {
|
||||
return `${PREFIX}.${TOKEN_KEYS}.${clientId}`;
|
||||
}
|
||||
|
||||
return `${PREFIX}.${schema}.${TOKEN_KEYS}.${clientId}`;
|
||||
}
|
||||
125
backend/node_modules/@azure/msal-browser/src/cache/CookieStorage.ts
generated
vendored
Normal file
125
backend/node_modules/@azure/msal-browser/src/cache/CookieStorage.ts
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ClientAuthErrorCodes,
|
||||
createClientAuthError,
|
||||
} from "@azure/msal-common/browser";
|
||||
import { IWindowStorage } from "./IWindowStorage.js";
|
||||
|
||||
// Cookie life calculation (hours * minutes * seconds * ms)
|
||||
const COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
|
||||
|
||||
export const SameSiteOptions = {
|
||||
Lax: "Lax",
|
||||
None: "None",
|
||||
} as const;
|
||||
export type SameSiteOptions =
|
||||
(typeof SameSiteOptions)[keyof typeof SameSiteOptions];
|
||||
|
||||
export class CookieStorage implements IWindowStorage<string> {
|
||||
initialize(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getItem(key: string): string | null {
|
||||
const name = encodeURIComponent(key);
|
||||
const cookieList = document.cookie.split(";");
|
||||
for (let i = 0; i < cookieList.length; i++) {
|
||||
const cookie = cookieList[i].trim();
|
||||
const eqIndex = cookie.indexOf("=");
|
||||
const rawKey =
|
||||
eqIndex === -1 ? cookie : cookie.substring(0, eqIndex);
|
||||
if (rawKey === name) {
|
||||
const rawValue =
|
||||
eqIndex === -1 ? "" : cookie.substring(eqIndex + 1);
|
||||
try {
|
||||
return decodeURIComponent(rawValue);
|
||||
} catch {
|
||||
return rawValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
getUserData(): string | null {
|
||||
throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented);
|
||||
}
|
||||
|
||||
setItem(
|
||||
key: string,
|
||||
value: string,
|
||||
cookieLifeDays?: number,
|
||||
secure: boolean = true,
|
||||
sameSite: SameSiteOptions = SameSiteOptions.Lax
|
||||
): void {
|
||||
let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(
|
||||
value
|
||||
)};path=/;SameSite=${sameSite};`;
|
||||
|
||||
if (cookieLifeDays) {
|
||||
const expireTime = getCookieExpirationTime(cookieLifeDays);
|
||||
cookieStr += `expires=${expireTime};`;
|
||||
}
|
||||
|
||||
if (secure || sameSite === SameSiteOptions.None) {
|
||||
// SameSite None requires Secure flag
|
||||
cookieStr += "Secure;";
|
||||
}
|
||||
|
||||
document.cookie = cookieStr;
|
||||
}
|
||||
|
||||
async setUserData(): Promise<void> {
|
||||
return Promise.reject(
|
||||
createClientAuthError(ClientAuthErrorCodes.methodNotImplemented)
|
||||
);
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
// Setting expiration to -1 removes it
|
||||
this.setItem(key, "", -1);
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
const cookieList = document.cookie.split(";");
|
||||
const keys: Array<string> = [];
|
||||
cookieList.forEach((cookie) => {
|
||||
const trimmed = cookie.trim();
|
||||
const eqIndex = trimmed.indexOf("=");
|
||||
const rawKey =
|
||||
eqIndex === -1 ? trimmed : trimmed.substring(0, eqIndex);
|
||||
try {
|
||||
keys.push(decodeURIComponent(rawKey));
|
||||
} catch {
|
||||
// Skip cookies with malformed percent-encoded sequences in the key
|
||||
}
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
containsKey(key: string): boolean {
|
||||
return this.getKeys().includes(key);
|
||||
}
|
||||
|
||||
decryptData(): Promise<object | null> {
|
||||
// Cookie storage does not support encryption, so this method is a no-op
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cookie expiration time
|
||||
* @param cookieLifeDays
|
||||
*/
|
||||
export function getCookieExpirationTime(cookieLifeDays: number): string {
|
||||
const today = new Date();
|
||||
const expr = new Date(
|
||||
today.getTime() + cookieLifeDays * COOKIE_LIFE_MULTIPLIER
|
||||
);
|
||||
return expr.toUTCString();
|
||||
}
|
||||
301
backend/node_modules/@azure/msal-browser/src/cache/DatabaseStorage.ts
generated
vendored
Normal file
301
backend/node_modules/@azure/msal-browser/src/cache/DatabaseStorage.ts
generated
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createBrowserAuthError,
|
||||
BrowserAuthErrorCodes,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import {
|
||||
DB_NAME,
|
||||
DB_TABLE_NAME,
|
||||
DB_VERSION,
|
||||
} from "../utils/BrowserConstants.js";
|
||||
import { IAsyncStorage } from "./IAsyncStorage.js";
|
||||
|
||||
interface IDBOpenDBRequestEvent extends Event {
|
||||
target: IDBOpenDBRequest & EventTarget;
|
||||
}
|
||||
|
||||
interface IDBOpenOnUpgradeNeededEvent extends IDBVersionChangeEvent {
|
||||
target: IDBOpenDBRequest & EventTarget;
|
||||
}
|
||||
|
||||
interface IDBRequestEvent extends Event {
|
||||
target: IDBRequest & EventTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage wrapper for IndexedDB storage in browsers: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
||||
*/
|
||||
export class DatabaseStorage<T> implements IAsyncStorage<T> {
|
||||
private db: IDBDatabase | undefined;
|
||||
private dbName: string;
|
||||
private tableName: string;
|
||||
private version: number;
|
||||
private dbOpen: boolean;
|
||||
|
||||
constructor() {
|
||||
this.dbName = DB_NAME;
|
||||
this.version = DB_VERSION;
|
||||
this.tableName = DB_TABLE_NAME;
|
||||
this.dbOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens IndexedDB instance.
|
||||
*/
|
||||
async open(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const openDB = window.indexedDB.open(this.dbName, this.version);
|
||||
openDB.addEventListener(
|
||||
"upgradeneeded",
|
||||
(e: IDBVersionChangeEvent) => {
|
||||
const event = e as IDBOpenOnUpgradeNeededEvent;
|
||||
event.target.result.createObjectStore(this.tableName);
|
||||
}
|
||||
);
|
||||
openDB.addEventListener("success", (e: Event) => {
|
||||
const event = e as IDBOpenDBRequestEvent;
|
||||
this.db = event.target.result;
|
||||
this.dbOpen = true;
|
||||
resolve();
|
||||
});
|
||||
openDB.addEventListener("error", () =>
|
||||
reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseUnavailable
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to IndexedDB database when all pending transactions
|
||||
* complete.
|
||||
*/
|
||||
closeConnection(): void {
|
||||
const db = this.db;
|
||||
if (db && this.dbOpen) {
|
||||
db.close();
|
||||
this.dbOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens database if it's not already open
|
||||
*/
|
||||
private async validateDbIsOpen(): Promise<void> {
|
||||
if (!this.dbOpen) {
|
||||
return this.open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves item from IndexedDB instance.
|
||||
* @param key
|
||||
*/
|
||||
async getItem(key: string): Promise<T | null> {
|
||||
await this.validateDbIsOpen();
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// TODO: Add timeouts?
|
||||
if (!this.db) {
|
||||
return reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseNotOpen
|
||||
)
|
||||
);
|
||||
}
|
||||
const transaction = this.db.transaction(
|
||||
[this.tableName],
|
||||
"readonly"
|
||||
);
|
||||
const objectStore = transaction.objectStore(this.tableName);
|
||||
const dbGet = objectStore.get(key);
|
||||
|
||||
dbGet.addEventListener("success", (e: Event) => {
|
||||
const event = e as IDBRequestEvent;
|
||||
this.closeConnection();
|
||||
resolve(event.target.result);
|
||||
});
|
||||
|
||||
dbGet.addEventListener("error", (e: Event) => {
|
||||
this.closeConnection();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds item to IndexedDB under given key
|
||||
* @param key
|
||||
* @param payload
|
||||
*/
|
||||
async setItem(key: string, payload: T): Promise<void> {
|
||||
await this.validateDbIsOpen();
|
||||
return new Promise<void>((resolve: Function, reject: Function) => {
|
||||
// TODO: Add timeouts?
|
||||
if (!this.db) {
|
||||
return reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseNotOpen
|
||||
)
|
||||
);
|
||||
}
|
||||
const transaction = this.db.transaction(
|
||||
[this.tableName],
|
||||
"readwrite"
|
||||
);
|
||||
|
||||
const objectStore = transaction.objectStore(this.tableName);
|
||||
|
||||
const dbPut = objectStore.put(payload, key);
|
||||
|
||||
dbPut.addEventListener("success", () => {
|
||||
this.closeConnection();
|
||||
resolve();
|
||||
});
|
||||
|
||||
dbPut.addEventListener("error", (e) => {
|
||||
this.closeConnection();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes item from IndexedDB under given key
|
||||
* @param key
|
||||
*/
|
||||
async removeItem(key: string): Promise<void> {
|
||||
await this.validateDbIsOpen();
|
||||
return new Promise<void>((resolve: Function, reject: Function) => {
|
||||
if (!this.db) {
|
||||
return reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseNotOpen
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const transaction = this.db.transaction(
|
||||
[this.tableName],
|
||||
"readwrite"
|
||||
);
|
||||
const objectStore = transaction.objectStore(this.tableName);
|
||||
const dbDelete = objectStore.delete(key);
|
||||
|
||||
dbDelete.addEventListener("success", () => {
|
||||
this.closeConnection();
|
||||
resolve();
|
||||
});
|
||||
|
||||
dbDelete.addEventListener("error", (e) => {
|
||||
this.closeConnection();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the keys from the storage object as an iterable array of strings.
|
||||
*/
|
||||
async getKeys(): Promise<string[]> {
|
||||
await this.validateDbIsOpen();
|
||||
return new Promise<string[]>((resolve: Function, reject: Function) => {
|
||||
if (!this.db) {
|
||||
return reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseNotOpen
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const transaction = this.db.transaction(
|
||||
[this.tableName],
|
||||
"readonly"
|
||||
);
|
||||
const objectStore = transaction.objectStore(this.tableName);
|
||||
const dbGetKeys = objectStore.getAllKeys();
|
||||
|
||||
dbGetKeys.addEventListener("success", (e: Event) => {
|
||||
const event = e as IDBRequestEvent;
|
||||
this.closeConnection();
|
||||
resolve(event.target.result);
|
||||
});
|
||||
|
||||
dbGetKeys.addEventListener("error", (e: Event) => {
|
||||
this.closeConnection();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Checks whether there is an object under the search key in the object store
|
||||
*/
|
||||
async containsKey(key: string): Promise<boolean> {
|
||||
await this.validateDbIsOpen();
|
||||
|
||||
return new Promise<boolean>((resolve: Function, reject: Function) => {
|
||||
if (!this.db) {
|
||||
return reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.databaseNotOpen
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const transaction = this.db.transaction(
|
||||
[this.tableName],
|
||||
"readonly"
|
||||
);
|
||||
const objectStore = transaction.objectStore(this.tableName);
|
||||
const dbContainsKey = objectStore.count(key);
|
||||
|
||||
dbContainsKey.addEventListener("success", (e: Event) => {
|
||||
const event = e as IDBRequestEvent;
|
||||
this.closeConnection();
|
||||
resolve(event.target.result === 1);
|
||||
});
|
||||
|
||||
dbContainsKey.addEventListener("error", (e: Event) => {
|
||||
this.closeConnection();
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the MSAL database. The database is deleted rather than cleared to make it possible
|
||||
* for client applications to downgrade to a previous MSAL version without worrying about forward compatibility issues
|
||||
* with IndexedDB database versions.
|
||||
*/
|
||||
async deleteDatabase(): Promise<boolean> {
|
||||
// Check if database being deleted exists
|
||||
|
||||
if (this.db && this.dbOpen) {
|
||||
this.closeConnection();
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve: Function, reject: Function) => {
|
||||
const deleteDbRequest = window.indexedDB.deleteDatabase(DB_NAME);
|
||||
const id = setTimeout(() => reject(false), 200); // Reject if events aren't raised within 200ms
|
||||
deleteDbRequest.addEventListener("success", () => {
|
||||
clearTimeout(id);
|
||||
return resolve(true);
|
||||
});
|
||||
deleteDbRequest.addEventListener("blocked", () => {
|
||||
clearTimeout(id);
|
||||
return resolve(true);
|
||||
});
|
||||
deleteDbRequest.addEventListener("error", () => {
|
||||
clearTimeout(id);
|
||||
return reject(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
19
backend/node_modules/@azure/msal-browser/src/cache/EncryptedData.ts
generated
vendored
Normal file
19
backend/node_modules/@azure/msal-browser/src/cache/EncryptedData.ts
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export type EncryptedData = {
|
||||
id: string;
|
||||
nonce: string;
|
||||
data: string;
|
||||
lastUpdatedAt: string;
|
||||
};
|
||||
|
||||
export function isEncrypted(data: object): data is EncryptedData {
|
||||
return (
|
||||
data.hasOwnProperty("id") &&
|
||||
data.hasOwnProperty("nonce") &&
|
||||
data.hasOwnProperty("data")
|
||||
);
|
||||
}
|
||||
41
backend/node_modules/@azure/msal-browser/src/cache/IAsyncStorage.ts
generated
vendored
Normal file
41
backend/node_modules/@azure/msal-browser/src/cache/IAsyncStorage.ts
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export interface IAsyncStorage<T> {
|
||||
/**
|
||||
* Get the item from the asynchronous storage object matching the given key.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
getItem(key: string, correlationId: string): Promise<T | null>;
|
||||
|
||||
/**
|
||||
* Sets the item in the asynchronous storage object with the given key.
|
||||
* @param key
|
||||
* @param value
|
||||
* @param correlationId
|
||||
*/
|
||||
setItem(key: string, value: T, correlationId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes the item in the asynchronous storage object matching the given key.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
removeItem(key: string, correlationId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get all the keys from the asynchronous storage object as an iterable array of strings.
|
||||
* @param correlationId
|
||||
*/
|
||||
getKeys(correlationId: string): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Returns true or false if the given key is present in the cache.
|
||||
* @param key
|
||||
* @param correlationId
|
||||
*/
|
||||
containsKey(key: string, correlationId: string): Promise<boolean>;
|
||||
}
|
||||
64
backend/node_modules/@azure/msal-browser/src/cache/IWindowStorage.ts
generated
vendored
Normal file
64
backend/node_modules/@azure/msal-browser/src/cache/IWindowStorage.ts
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { EncryptedData } from "./EncryptedData.js";
|
||||
|
||||
export interface IWindowStorage<T> {
|
||||
/**
|
||||
* Async initializer
|
||||
*/
|
||||
initialize(correlationId: string): Promise<void>;
|
||||
/**
|
||||
* Get the item from the window storage object matching the given key.
|
||||
* @param key
|
||||
*/
|
||||
getItem(key: string): T | null;
|
||||
|
||||
/**
|
||||
* Getter for sensitive data that may contain PII.
|
||||
*/
|
||||
getUserData(key: string): T | null;
|
||||
|
||||
/**
|
||||
* Sets the item in the window storage object with the given key.
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
setItem(key: string, value: T): void;
|
||||
|
||||
/**
|
||||
* Setter for sensitive data that may contain PII.
|
||||
*/
|
||||
setUserData(
|
||||
key: string,
|
||||
value: T,
|
||||
correlationId: string,
|
||||
timestamp: string,
|
||||
kmsi: boolean
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes the item in the window storage object matching the given key.
|
||||
* @param key
|
||||
*/
|
||||
removeItem(key: string): void;
|
||||
|
||||
/**
|
||||
* Get all the keys from the window storage object as an iterable array of strings.
|
||||
*/
|
||||
getKeys(): string[];
|
||||
|
||||
/**
|
||||
* Returns true or false if the given key is present in the cache.
|
||||
* @param key
|
||||
*/
|
||||
containsKey(key: string): boolean;
|
||||
|
||||
decryptData(
|
||||
key: string,
|
||||
data: EncryptedData,
|
||||
correlationId: string
|
||||
): Promise<object | null>;
|
||||
}
|
||||
516
backend/node_modules/@azure/msal-browser/src/cache/LocalStorage.ts
generated
vendored
Normal file
516
backend/node_modules/@azure/msal-browser/src/cache/LocalStorage.ts
generated
vendored
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
TokenKeys,
|
||||
IPerformanceClient,
|
||||
invokeAsync,
|
||||
Logger,
|
||||
invoke,
|
||||
} from "@azure/msal-common/browser";
|
||||
import * as BrowserPerformanceEvents from "../telemetry/BrowserPerformanceEvents.js";
|
||||
import * as BrowserRootPerformanceEvents from "../telemetry/BrowserRootPerformanceEvents.js";
|
||||
import {
|
||||
createNewGuid,
|
||||
decrypt,
|
||||
encrypt,
|
||||
generateBaseKey,
|
||||
generateHKDF,
|
||||
} from "../crypto/BrowserCrypto.js";
|
||||
import { base64DecToArr } from "../encode/Base64Decode.js";
|
||||
import { urlEncodeArr } from "../encode/Base64Encode.js";
|
||||
import {
|
||||
BrowserAuthErrorCodes,
|
||||
createBrowserAuthError,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import {
|
||||
BrowserConfigurationAuthErrorCodes,
|
||||
createBrowserConfigurationAuthError,
|
||||
} from "../error/BrowserConfigurationAuthError.js";
|
||||
import { CookieStorage, SameSiteOptions } from "./CookieStorage.js";
|
||||
import { IWindowStorage } from "./IWindowStorage.js";
|
||||
import { MemoryStorage } from "./MemoryStorage.js";
|
||||
import { getAccountKeys, getTokenKeys } from "./CacheHelpers.js";
|
||||
import * as CacheKeys from "./CacheKeys.js";
|
||||
import { EncryptedData, isEncrypted } from "./EncryptedData.js";
|
||||
|
||||
const ENCRYPTION_KEY = "msal.cache.encryption";
|
||||
const BROADCAST_CHANNEL_NAME = "msal.broadcast.cache";
|
||||
|
||||
type EncryptionCookie = {
|
||||
id: string;
|
||||
key: CryptoKey;
|
||||
};
|
||||
|
||||
export class LocalStorage implements IWindowStorage<string> {
|
||||
private clientId: string;
|
||||
private initialized: boolean;
|
||||
private memoryStorage: MemoryStorage<string>;
|
||||
private performanceClient: IPerformanceClient;
|
||||
private logger: Logger;
|
||||
private encryptionCookie?: EncryptionCookie;
|
||||
private broadcast: BroadcastChannel;
|
||||
|
||||
constructor(
|
||||
clientId: string,
|
||||
logger: Logger,
|
||||
performanceClient: IPerformanceClient
|
||||
) {
|
||||
if (!window.localStorage) {
|
||||
throw createBrowserConfigurationAuthError(
|
||||
BrowserConfigurationAuthErrorCodes.storageNotSupported
|
||||
);
|
||||
}
|
||||
this.memoryStorage = new MemoryStorage<string>();
|
||||
this.initialized = false;
|
||||
this.clientId = clientId;
|
||||
this.logger = logger;
|
||||
this.performanceClient = performanceClient;
|
||||
this.broadcast = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
||||
}
|
||||
|
||||
async initialize(correlationId: string): Promise<void> {
|
||||
const cookies = new CookieStorage();
|
||||
const cookieString = cookies.getItem(ENCRYPTION_KEY);
|
||||
let parsedCookie = { key: "", id: "" };
|
||||
if (cookieString) {
|
||||
try {
|
||||
parsedCookie = JSON.parse(cookieString);
|
||||
} catch (e) {}
|
||||
}
|
||||
if (parsedCookie.key && parsedCookie.id) {
|
||||
// Encryption key already exists, import
|
||||
const baseKey = invoke(
|
||||
base64DecToArr,
|
||||
BrowserPerformanceEvents.Base64Decode,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(parsedCookie.key);
|
||||
this.encryptionCookie = {
|
||||
id: parsedCookie.id,
|
||||
key: await invokeAsync(
|
||||
generateHKDF,
|
||||
BrowserPerformanceEvents.GenerateHKDF,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(baseKey),
|
||||
};
|
||||
} else {
|
||||
// Encryption key doesn't exist or is invalid, generate a new one
|
||||
const id = createNewGuid();
|
||||
const baseKey = await invokeAsync(
|
||||
generateBaseKey,
|
||||
BrowserPerformanceEvents.GenerateBaseKey,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)();
|
||||
const keyStr = invoke(
|
||||
urlEncodeArr,
|
||||
BrowserPerformanceEvents.UrlEncodeArr,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(new Uint8Array(baseKey));
|
||||
this.encryptionCookie = {
|
||||
id: id,
|
||||
key: await invokeAsync(
|
||||
generateHKDF,
|
||||
BrowserPerformanceEvents.GenerateHKDF,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(baseKey),
|
||||
};
|
||||
|
||||
const cookieData = {
|
||||
id: id,
|
||||
key: keyStr,
|
||||
};
|
||||
|
||||
cookies.setItem(
|
||||
ENCRYPTION_KEY,
|
||||
JSON.stringify(cookieData),
|
||||
0, // Expiration - 0 means cookie will be cleared at the end of the browser session
|
||||
true, // Secure flag
|
||||
SameSiteOptions.None // SameSite must be None to support iframed apps
|
||||
);
|
||||
}
|
||||
|
||||
await invokeAsync(
|
||||
this.importExistingCache.bind(this),
|
||||
BrowserPerformanceEvents.ImportExistingCache,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(correlationId);
|
||||
|
||||
// Register listener for cache updates in other tabs
|
||||
this.broadcast.addEventListener("message", (event: MessageEvent) => {
|
||||
this.updateCache(event, correlationId);
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
getItem(key: string): string | null {
|
||||
return window.localStorage.getItem(key);
|
||||
}
|
||||
|
||||
getUserData(key: string): string | null {
|
||||
if (!this.initialized) {
|
||||
throw createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.uninitializedPublicClientApplication
|
||||
);
|
||||
}
|
||||
return this.memoryStorage.getItem(key);
|
||||
}
|
||||
|
||||
async decryptData(
|
||||
key: string,
|
||||
data: EncryptedData,
|
||||
correlationId: string
|
||||
): Promise<object | null> {
|
||||
if (!this.initialized || !this.encryptionCookie) {
|
||||
throw createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.uninitializedPublicClientApplication
|
||||
);
|
||||
}
|
||||
|
||||
if (data.id !== this.encryptionCookie.id) {
|
||||
// Data was encrypted with a different key. It must be removed because it is from a previous session.
|
||||
this.performanceClient.incrementFields(
|
||||
{ encryptedCacheExpiredCount: 1 },
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const decryptedData = await invokeAsync(
|
||||
decrypt,
|
||||
BrowserPerformanceEvents.Decrypt,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
this.encryptionCookie.key,
|
||||
data.nonce,
|
||||
this.getContext(key),
|
||||
data.data
|
||||
);
|
||||
|
||||
if (!decryptedData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
...JSON.parse(decryptedData),
|
||||
lastUpdatedAt: data.lastUpdatedAt,
|
||||
};
|
||||
} catch (e) {
|
||||
this.performanceClient.incrementFields(
|
||||
{ encryptedCacheCorruptionCount: 1 },
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setItem(key: string, value: string): void {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
async setUserData(
|
||||
key: string,
|
||||
value: string,
|
||||
correlationId: string,
|
||||
timestamp: string,
|
||||
kmsi: boolean
|
||||
): Promise<void> {
|
||||
if (!this.initialized || !this.encryptionCookie) {
|
||||
throw createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.uninitializedPublicClientApplication
|
||||
);
|
||||
}
|
||||
|
||||
if (kmsi) {
|
||||
this.setItem(key, value);
|
||||
} else {
|
||||
const { data, nonce } = await invokeAsync(
|
||||
encrypt,
|
||||
BrowserPerformanceEvents.Encrypt,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(this.encryptionCookie.key, value, this.getContext(key));
|
||||
const encryptedData: EncryptedData = {
|
||||
id: this.encryptionCookie.id,
|
||||
nonce: nonce,
|
||||
data: data,
|
||||
lastUpdatedAt: timestamp,
|
||||
};
|
||||
this.setItem(key, JSON.stringify(encryptedData));
|
||||
}
|
||||
|
||||
this.memoryStorage.setItem(key, value);
|
||||
|
||||
// Notify other frames to update their in-memory cache
|
||||
this.broadcast.postMessage({
|
||||
key: key,
|
||||
value: value,
|
||||
context: this.getContext(key),
|
||||
});
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
if (this.memoryStorage.containsKey(key)) {
|
||||
this.memoryStorage.removeItem(key);
|
||||
this.broadcast.postMessage({
|
||||
key: key,
|
||||
value: null,
|
||||
context: this.getContext(key),
|
||||
});
|
||||
}
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(window.localStorage);
|
||||
}
|
||||
|
||||
containsKey(key: string): boolean {
|
||||
return window.localStorage.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all known MSAL keys from the cache
|
||||
*/
|
||||
clear(): void {
|
||||
// Removes all remaining MSAL cache items
|
||||
this.memoryStorage.clear();
|
||||
|
||||
const accountKeys = getAccountKeys(this);
|
||||
accountKeys.forEach((key) => this.removeItem(key));
|
||||
const tokenKeys = getTokenKeys(this.clientId, this);
|
||||
tokenKeys.idToken.forEach((key) => this.removeItem(key));
|
||||
tokenKeys.accessToken.forEach((key) => this.removeItem(key));
|
||||
tokenKeys.refreshToken.forEach((key) => this.removeItem(key));
|
||||
|
||||
// Clean up anything left
|
||||
this.getKeys().forEach((cacheKey: string) => {
|
||||
if (
|
||||
cacheKey.startsWith(CacheKeys.PREFIX) ||
|
||||
cacheKey.indexOf(this.clientId) !== -1
|
||||
) {
|
||||
this.removeItem(cacheKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to decrypt all known MSAL keys in localStorage and save them to inMemory storage
|
||||
* @returns
|
||||
*/
|
||||
private async importExistingCache(correlationId: string): Promise<void> {
|
||||
if (!this.encryptionCookie) {
|
||||
return;
|
||||
}
|
||||
|
||||
let accountKeys = getAccountKeys(this);
|
||||
accountKeys = await this.importArray(accountKeys, correlationId);
|
||||
// Write valid account keys back to map
|
||||
if (accountKeys.length) {
|
||||
this.setItem(
|
||||
CacheKeys.getAccountKeysCacheKey(),
|
||||
JSON.stringify(accountKeys)
|
||||
);
|
||||
} else {
|
||||
this.removeItem(CacheKeys.getAccountKeysCacheKey());
|
||||
}
|
||||
|
||||
const tokenKeys: TokenKeys = getTokenKeys(this.clientId, this);
|
||||
tokenKeys.idToken = await this.importArray(
|
||||
tokenKeys.idToken,
|
||||
correlationId
|
||||
);
|
||||
tokenKeys.accessToken = await this.importArray(
|
||||
tokenKeys.accessToken,
|
||||
correlationId
|
||||
);
|
||||
tokenKeys.refreshToken = await this.importArray(
|
||||
tokenKeys.refreshToken,
|
||||
correlationId
|
||||
);
|
||||
// Write valid token keys back to map
|
||||
if (
|
||||
tokenKeys.idToken.length ||
|
||||
tokenKeys.accessToken.length ||
|
||||
tokenKeys.refreshToken.length
|
||||
) {
|
||||
this.setItem(
|
||||
CacheKeys.getTokenKeysCacheKey(this.clientId),
|
||||
JSON.stringify(tokenKeys)
|
||||
);
|
||||
} else {
|
||||
this.removeItem(CacheKeys.getTokenKeysCacheKey(this.clientId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to decrypt and save cache entries
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
private async getItemFromEncryptedCache(
|
||||
key: string,
|
||||
correlationId: string
|
||||
): Promise<string | null> {
|
||||
if (!this.encryptionCookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rawCache = this.getItem(key);
|
||||
if (!rawCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let encObj: EncryptedData;
|
||||
try {
|
||||
encObj = JSON.parse(rawCache);
|
||||
} catch (e) {
|
||||
// Not a valid encrypted object, remove
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isEncrypted(encObj)) {
|
||||
// Data is not encrypted
|
||||
this.performanceClient.incrementFields(
|
||||
{ unencryptedCacheCount: 1 },
|
||||
correlationId
|
||||
);
|
||||
return rawCache;
|
||||
}
|
||||
|
||||
if (encObj.id !== this.encryptionCookie.id) {
|
||||
// Data was encrypted with a different key. It must be removed because it is from a previous session.
|
||||
this.performanceClient.incrementFields(
|
||||
{ encryptedCacheExpiredCount: 1 },
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
this.performanceClient.incrementFields(
|
||||
{ encryptedCacheCount: 1 },
|
||||
correlationId
|
||||
);
|
||||
|
||||
return invokeAsync(
|
||||
decrypt,
|
||||
BrowserPerformanceEvents.Decrypt,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
this.encryptionCookie.key,
|
||||
encObj.nonce,
|
||||
this.getContext(key),
|
||||
encObj.data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to decrypt and save an array of cache keys
|
||||
* @param arr
|
||||
* @returns Array of keys successfully imported
|
||||
*/
|
||||
private async importArray(
|
||||
arr: Array<string>,
|
||||
correlationId: string
|
||||
): Promise<Array<string>> {
|
||||
const importedArr: Array<string> = [];
|
||||
const promiseArr: Array<Promise<void>> = [];
|
||||
arr.forEach((key) => {
|
||||
const promise = this.getItemFromEncryptedCache(
|
||||
key,
|
||||
correlationId
|
||||
).then((value) => {
|
||||
if (value) {
|
||||
this.memoryStorage.setItem(key, value);
|
||||
importedArr.push(key);
|
||||
} else {
|
||||
// If value is empty, unencrypted or expired remove
|
||||
this.removeItem(key);
|
||||
}
|
||||
});
|
||||
promiseArr.push(promise);
|
||||
});
|
||||
|
||||
await Promise.all(promiseArr);
|
||||
return importedArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets encryption context for a given cache entry. This is clientId for app specific entries, empty string for shared entries
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
private getContext(key: string): string {
|
||||
let context = "";
|
||||
if (key.includes(this.clientId)) {
|
||||
context = this.clientId; // Used to bind encryption key to this appId
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private updateCache(event: MessageEvent, correlationId: string): void {
|
||||
this.logger.trace(
|
||||
"Updating internal cache from broadcast event",
|
||||
correlationId
|
||||
);
|
||||
const perfMeasurement = this.performanceClient.startMeasurement(
|
||||
BrowserRootPerformanceEvents.LocalStorageUpdated
|
||||
);
|
||||
perfMeasurement.add({ isBackground: true });
|
||||
|
||||
const { key, value, context } = event.data;
|
||||
if (!key) {
|
||||
this.logger.error("Broadcast event missing key", correlationId);
|
||||
perfMeasurement.end({ success: false, errorCode: "noKey" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (context && context !== this.clientId) {
|
||||
this.logger.trace(
|
||||
`Ignoring broadcast event from clientId: '${context}'`,
|
||||
correlationId
|
||||
);
|
||||
perfMeasurement.end({
|
||||
success: false,
|
||||
errorCode: "contextMismatch",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
this.memoryStorage.removeItem(key);
|
||||
this.logger.verbose(
|
||||
"Removed item from internal cache",
|
||||
correlationId
|
||||
);
|
||||
} else {
|
||||
this.memoryStorage.setItem(key, value);
|
||||
this.logger.verbose(
|
||||
"Updated item in internal cache",
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
perfMeasurement.end({ success: true });
|
||||
}
|
||||
}
|
||||
59
backend/node_modules/@azure/msal-browser/src/cache/MemoryStorage.ts
generated
vendored
Normal file
59
backend/node_modules/@azure/msal-browser/src/cache/MemoryStorage.ts
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { IWindowStorage } from "./IWindowStorage.js";
|
||||
|
||||
export class MemoryStorage<T> implements IWindowStorage<T> {
|
||||
private cache: Map<string, T>;
|
||||
|
||||
constructor() {
|
||||
this.cache = new Map<string, T>();
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
// Memory storage does not require initialization
|
||||
}
|
||||
|
||||
getItem(key: string): T | null {
|
||||
return this.cache.get(key) || null;
|
||||
}
|
||||
|
||||
getUserData(key: string): T | null {
|
||||
return this.getItem(key);
|
||||
}
|
||||
|
||||
setItem(key: string, value: T): void {
|
||||
this.cache.set(key, value);
|
||||
}
|
||||
|
||||
async setUserData(key: string, value: T): Promise<void> {
|
||||
this.setItem(key, value);
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
this.cache.delete(key);
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
const cacheKeys: string[] = [];
|
||||
this.cache.forEach((value: T, key: string) => {
|
||||
cacheKeys.push(key);
|
||||
});
|
||||
return cacheKeys;
|
||||
}
|
||||
|
||||
containsKey(key: string): boolean {
|
||||
return this.cache.has(key);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
decryptData(): Promise<object | null> {
|
||||
// Memory storage does not support encryption, so this method is a no-op
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
57
backend/node_modules/@azure/msal-browser/src/cache/SessionStorage.ts
generated
vendored
Normal file
57
backend/node_modules/@azure/msal-browser/src/cache/SessionStorage.ts
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
BrowserConfigurationAuthErrorCodes,
|
||||
createBrowserConfigurationAuthError,
|
||||
} from "../error/BrowserConfigurationAuthError.js";
|
||||
import { IWindowStorage } from "./IWindowStorage.js";
|
||||
|
||||
export class SessionStorage implements IWindowStorage<string> {
|
||||
constructor() {
|
||||
if (!window.sessionStorage) {
|
||||
throw createBrowserConfigurationAuthError(
|
||||
BrowserConfigurationAuthErrorCodes.storageNotSupported
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
// Session storage does not require initialization
|
||||
}
|
||||
|
||||
getItem(key: string): string | null {
|
||||
return window.sessionStorage.getItem(key);
|
||||
}
|
||||
|
||||
getUserData(key: string): string | null {
|
||||
return this.getItem(key);
|
||||
}
|
||||
|
||||
setItem(key: string, value: string): void {
|
||||
window.sessionStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
async setUserData(key: string, value: string): Promise<void> {
|
||||
this.setItem(key, value);
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
window.sessionStorage.removeItem(key);
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(window.sessionStorage);
|
||||
}
|
||||
|
||||
containsKey(key: string): boolean {
|
||||
return window.sessionStorage.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
decryptData(): Promise<object | null> {
|
||||
// Session storage does not support encryption, so this method is a no-op
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
523
backend/node_modules/@azure/msal-browser/src/cache/TokenCache.ts
generated
vendored
Normal file
523
backend/node_modules/@azure/msal-browser/src/cache/TokenCache.ts
generated
vendored
Normal file
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AccessTokenEntity,
|
||||
AccountEntity,
|
||||
AccountEntityUtils,
|
||||
Authority,
|
||||
AuthorityFactory,
|
||||
AuthorityOptions,
|
||||
AuthToken,
|
||||
buildAccountToCache,
|
||||
buildStaticAuthorityOptions,
|
||||
CacheHelpers,
|
||||
CacheRecord,
|
||||
ExternalTokenResponse,
|
||||
ICrypto,
|
||||
IdTokenEntity,
|
||||
invokeAsync,
|
||||
IPerformanceClient,
|
||||
Logger,
|
||||
RefreshTokenEntity,
|
||||
ScopeSet,
|
||||
StubPerformanceClient,
|
||||
TimeUtils,
|
||||
TokenClaims,
|
||||
} from "@azure/msal-common/browser";
|
||||
import { buildConfiguration, Configuration } from "../config/Configuration.js";
|
||||
import * as BrowserCrypto from "../crypto/BrowserCrypto.js";
|
||||
import { CryptoOps } from "../crypto/CryptoOps.js";
|
||||
import { base64Decode } from "../encode/Base64Decode.js";
|
||||
import {
|
||||
BrowserAuthErrorCodes,
|
||||
createBrowserAuthError,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import { EventHandler } from "../event/EventHandler.js";
|
||||
import type { SilentRequest } from "../request/SilentRequest.js";
|
||||
import type { AuthenticationResult } from "../response/AuthenticationResult.js";
|
||||
import * as BrowserPerformanceEvents from "../telemetry/BrowserPerformanceEvents.js";
|
||||
import * as BrowserRootPerformanceEvents from "../telemetry/BrowserRootPerformanceEvents.js";
|
||||
import { ApiId } from "../utils/BrowserConstants.js";
|
||||
import * as BrowserUtils from "../utils/BrowserUtils.js";
|
||||
import { BrowserCacheManager } from "./BrowserCacheManager.js";
|
||||
|
||||
export type LoadTokenOptions = {
|
||||
clientInfo?: string;
|
||||
expiresOn?: number;
|
||||
extendedExpiresOn?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* API to load tokens to msal-browser cache.
|
||||
* @param config - Object to configure the MSAL app.
|
||||
* @param request - Silent request containing authority, scopes, and account.
|
||||
* @param response - External token response to load into the cache.
|
||||
* @param options - Options controlling how tokens are loaded into the cache.
|
||||
* @param performanceClient - Optional performance client used for telemetry measurements.
|
||||
* @returns `AuthenticationResult` for the response that was loaded.
|
||||
*/
|
||||
export async function loadExternalTokens(
|
||||
config: Configuration,
|
||||
request: SilentRequest,
|
||||
response: ExternalTokenResponse,
|
||||
options: LoadTokenOptions,
|
||||
performanceClient: IPerformanceClient = new StubPerformanceClient()
|
||||
): Promise<AuthenticationResult> {
|
||||
BrowserUtils.blockNonBrowserEnvironment();
|
||||
|
||||
const browserConfig = buildConfiguration(config, true);
|
||||
|
||||
const correlationId =
|
||||
request.correlationId || BrowserCrypto.createNewGuid();
|
||||
|
||||
const rootMeasurement = performanceClient.startMeasurement(
|
||||
BrowserRootPerformanceEvents.LoadExternalTokens,
|
||||
correlationId
|
||||
);
|
||||
|
||||
try {
|
||||
const idTokenClaims = response.id_token
|
||||
? AuthToken.extractTokenClaims(response.id_token, base64Decode)
|
||||
: undefined;
|
||||
const kmsi = AuthToken.isKmsi(idTokenClaims || {});
|
||||
|
||||
const authorityOptions: AuthorityOptions = {
|
||||
protocolMode: browserConfig.system.protocolMode,
|
||||
knownAuthorities: browserConfig.auth.knownAuthorities,
|
||||
cloudDiscoveryMetadata: browserConfig.auth.cloudDiscoveryMetadata,
|
||||
authorityMetadata: browserConfig.auth.authorityMetadata,
|
||||
};
|
||||
|
||||
const logger = new Logger(browserConfig.system.loggerOptions || {});
|
||||
const cryptoOps = new CryptoOps(logger, browserConfig.telemetry.client);
|
||||
const storage = new BrowserCacheManager(
|
||||
browserConfig.auth.clientId,
|
||||
browserConfig.cache,
|
||||
cryptoOps,
|
||||
logger,
|
||||
browserConfig.telemetry.client,
|
||||
new EventHandler(logger),
|
||||
buildStaticAuthorityOptions(browserConfig.auth)
|
||||
);
|
||||
|
||||
const authorityString =
|
||||
request.authority || browserConfig.auth.authority;
|
||||
const authority = await AuthorityFactory.createDiscoveredInstance(
|
||||
Authority.generateAuthority(
|
||||
authorityString,
|
||||
request.azureCloudOptions
|
||||
),
|
||||
browserConfig.system.networkClient,
|
||||
storage,
|
||||
authorityOptions,
|
||||
logger,
|
||||
correlationId,
|
||||
performanceClient
|
||||
);
|
||||
|
||||
const cacheRecordAccount: AccountEntity = await invokeAsync(
|
||||
loadAccount,
|
||||
BrowserPerformanceEvents.LoadAccount,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
request,
|
||||
options.clientInfo || response.client_info || "",
|
||||
correlationId,
|
||||
storage,
|
||||
logger,
|
||||
cryptoOps,
|
||||
authority,
|
||||
idTokenClaims,
|
||||
performanceClient
|
||||
);
|
||||
|
||||
const idToken = await invokeAsync(
|
||||
loadIdToken,
|
||||
BrowserPerformanceEvents.LoadIdToken,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
response,
|
||||
cacheRecordAccount.homeAccountId,
|
||||
cacheRecordAccount.environment,
|
||||
cacheRecordAccount.realm,
|
||||
kmsi,
|
||||
correlationId,
|
||||
storage,
|
||||
logger,
|
||||
config.auth.clientId
|
||||
);
|
||||
|
||||
const accessToken = await invokeAsync(
|
||||
loadAccessToken,
|
||||
BrowserPerformanceEvents.LoadAccessToken,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
request,
|
||||
response,
|
||||
cacheRecordAccount.homeAccountId,
|
||||
cacheRecordAccount.environment,
|
||||
cacheRecordAccount.realm,
|
||||
kmsi,
|
||||
options,
|
||||
correlationId,
|
||||
storage,
|
||||
logger,
|
||||
config.auth.clientId
|
||||
);
|
||||
|
||||
const refreshToken = await invokeAsync(
|
||||
loadRefreshToken,
|
||||
BrowserPerformanceEvents.LoadRefreshToken,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(
|
||||
response,
|
||||
cacheRecordAccount.homeAccountId,
|
||||
cacheRecordAccount.environment,
|
||||
kmsi,
|
||||
correlationId,
|
||||
storage,
|
||||
logger,
|
||||
config.auth.clientId,
|
||||
performanceClient
|
||||
);
|
||||
|
||||
rootMeasurement.end(
|
||||
{ success: true },
|
||||
undefined,
|
||||
AccountEntityUtils.getAccountInfo(cacheRecordAccount)
|
||||
);
|
||||
|
||||
return generateAuthenticationResult(
|
||||
request,
|
||||
{
|
||||
account: cacheRecordAccount,
|
||||
idToken,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
},
|
||||
authority,
|
||||
idTokenClaims
|
||||
);
|
||||
} catch (error) {
|
||||
rootMeasurement.end({ success: false }, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to load account to msal-browser cache
|
||||
* @param idToken
|
||||
* @param environment
|
||||
* @param clientInfo
|
||||
* @param authorityType
|
||||
* @param requestHomeAccountId
|
||||
* @returns `AccountEntity`
|
||||
*/
|
||||
async function loadAccount(
|
||||
request: SilentRequest,
|
||||
clientInfo: string,
|
||||
correlationId: string,
|
||||
storage: BrowserCacheManager,
|
||||
logger: Logger,
|
||||
cryptoObj: ICrypto,
|
||||
authority: Authority,
|
||||
idTokenClaims?: TokenClaims,
|
||||
performanceClient?: IPerformanceClient
|
||||
): Promise<AccountEntity> {
|
||||
logger.verbose("TokenCache - loading account", correlationId);
|
||||
|
||||
if (request.account) {
|
||||
const accountEntity =
|
||||
AccountEntityUtils.createAccountEntityFromAccountInfo(
|
||||
request.account
|
||||
);
|
||||
await storage.setAccount(
|
||||
accountEntity,
|
||||
correlationId,
|
||||
AuthToken.isKmsi(idTokenClaims || {}),
|
||||
ApiId.loadExternalTokens
|
||||
);
|
||||
return accountEntity;
|
||||
} else if (!clientInfo && !idTokenClaims) {
|
||||
logger.error(
|
||||
"TokenCache - if an account is not provided on the request, clientInfo or idToken must be provided instead.",
|
||||
correlationId
|
||||
);
|
||||
throw createBrowserAuthError(BrowserAuthErrorCodes.unableToLoadToken);
|
||||
}
|
||||
|
||||
const homeAccountId = AccountEntityUtils.generateHomeAccountId(
|
||||
clientInfo,
|
||||
authority.authorityType,
|
||||
logger,
|
||||
cryptoObj,
|
||||
correlationId,
|
||||
idTokenClaims
|
||||
);
|
||||
|
||||
const claimsTenantId = idTokenClaims?.tid;
|
||||
|
||||
const cachedAccount = buildAccountToCache(
|
||||
storage,
|
||||
authority,
|
||||
homeAccountId,
|
||||
base64Decode,
|
||||
correlationId,
|
||||
idTokenClaims,
|
||||
clientInfo,
|
||||
authority.getPreferredCache(),
|
||||
claimsTenantId,
|
||||
undefined, // authCodePayload
|
||||
undefined, // nativeAccountId
|
||||
logger,
|
||||
performanceClient
|
||||
);
|
||||
|
||||
await storage.setAccount(
|
||||
cachedAccount,
|
||||
correlationId,
|
||||
AuthToken.isKmsi(idTokenClaims || {}),
|
||||
ApiId.loadExternalTokens
|
||||
);
|
||||
return cachedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to load id tokens to msal-browser cache
|
||||
* @param idToken
|
||||
* @param homeAccountId
|
||||
* @param environment
|
||||
* @param tenantId
|
||||
* @returns `IdTokenEntity`
|
||||
*/
|
||||
async function loadIdToken(
|
||||
response: ExternalTokenResponse,
|
||||
homeAccountId: string,
|
||||
environment: string,
|
||||
tenantId: string,
|
||||
kmsi: boolean,
|
||||
correlationId: string,
|
||||
storage: BrowserCacheManager,
|
||||
logger: Logger,
|
||||
clientId: string
|
||||
): Promise<IdTokenEntity | null> {
|
||||
if (!response.id_token) {
|
||||
logger.verbose(
|
||||
"TokenCache - no id token found in response",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.verbose("TokenCache - loading id token", correlationId);
|
||||
const idTokenEntity = CacheHelpers.createIdTokenEntity(
|
||||
homeAccountId,
|
||||
environment,
|
||||
response.id_token,
|
||||
clientId,
|
||||
tenantId
|
||||
);
|
||||
|
||||
await storage.setIdTokenCredential(idTokenEntity, correlationId, kmsi);
|
||||
return idTokenEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to load access tokens to msal-browser cache
|
||||
* @param request
|
||||
* @param response
|
||||
* @param homeAccountId
|
||||
* @param environment
|
||||
* @param tenantId
|
||||
* @returns `AccessTokenEntity`
|
||||
*/
|
||||
async function loadAccessToken(
|
||||
request: SilentRequest,
|
||||
response: ExternalTokenResponse,
|
||||
homeAccountId: string,
|
||||
environment: string,
|
||||
tenantId: string,
|
||||
kmsi: boolean,
|
||||
options: LoadTokenOptions,
|
||||
correlationId: string,
|
||||
storage: BrowserCacheManager,
|
||||
logger: Logger,
|
||||
clientId: string
|
||||
): Promise<AccessTokenEntity | null> {
|
||||
if (!response.access_token) {
|
||||
logger.verbose(
|
||||
"TokenCache - no access token found in response",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
} else if (!response.expires_in) {
|
||||
logger.error(
|
||||
"TokenCache - no expiration set on the access token. Cannot add it to the cache.",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
} else if (!response.scope && (!request.scopes || !request.scopes.length)) {
|
||||
logger.error(
|
||||
"TokenCache - scopes not specified in the request or response. Cannot add token to the cache.",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.verbose("TokenCache - loading access token", correlationId);
|
||||
|
||||
const scopes = response.scope
|
||||
? ScopeSet.fromString(response.scope)
|
||||
: new ScopeSet(request.scopes);
|
||||
const expiresOn =
|
||||
options.expiresOn || response.expires_in + TimeUtils.nowSeconds();
|
||||
|
||||
const extendedExpiresOn =
|
||||
options.extendedExpiresOn ||
|
||||
(response.ext_expires_in || response.expires_in) +
|
||||
TimeUtils.nowSeconds();
|
||||
|
||||
const accessTokenEntity = CacheHelpers.createAccessTokenEntity(
|
||||
homeAccountId,
|
||||
environment,
|
||||
response.access_token,
|
||||
clientId,
|
||||
tenantId,
|
||||
scopes.printScopes(),
|
||||
expiresOn,
|
||||
extendedExpiresOn,
|
||||
base64Decode
|
||||
);
|
||||
|
||||
await storage.setAccessTokenCredential(
|
||||
accessTokenEntity,
|
||||
correlationId,
|
||||
kmsi
|
||||
);
|
||||
return accessTokenEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to load refresh tokens to msal-browser cache
|
||||
* @param request
|
||||
* @param response
|
||||
* @param homeAccountId
|
||||
* @param environment
|
||||
* @returns `RefreshTokenEntity`
|
||||
*/
|
||||
async function loadRefreshToken(
|
||||
response: ExternalTokenResponse,
|
||||
homeAccountId: string,
|
||||
environment: string,
|
||||
kmsi: boolean,
|
||||
correlationId: string,
|
||||
storage: BrowserCacheManager,
|
||||
logger: Logger,
|
||||
clientId: string,
|
||||
performanceClient: IPerformanceClient
|
||||
): Promise<RefreshTokenEntity | null> {
|
||||
if (!response.refresh_token) {
|
||||
logger.verbose(
|
||||
"TokenCache - no refresh token found in response",
|
||||
correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const expiresOn = response.refresh_token_expires_in
|
||||
? response.refresh_token_expires_in + TimeUtils.nowSeconds()
|
||||
: undefined;
|
||||
performanceClient.addFields(
|
||||
{
|
||||
extRtExpiresOnSeconds: expiresOn,
|
||||
},
|
||||
correlationId
|
||||
);
|
||||
|
||||
logger.verbose("TokenCache - loading refresh token", correlationId);
|
||||
const refreshTokenEntity = CacheHelpers.createRefreshTokenEntity(
|
||||
homeAccountId,
|
||||
environment,
|
||||
response.refresh_token,
|
||||
clientId,
|
||||
response.foci,
|
||||
undefined, // userAssertionHash
|
||||
expiresOn
|
||||
);
|
||||
|
||||
await storage.setRefreshTokenCredential(
|
||||
refreshTokenEntity,
|
||||
correlationId,
|
||||
kmsi
|
||||
);
|
||||
return refreshTokenEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate an `AuthenticationResult` for the result.
|
||||
* @param request
|
||||
* @param idTokenObj
|
||||
* @param cacheRecord
|
||||
* @param authority
|
||||
* @returns `AuthenticationResult`
|
||||
*/
|
||||
function generateAuthenticationResult(
|
||||
request: SilentRequest,
|
||||
cacheRecord: CacheRecord & { account: AccountEntity },
|
||||
authority: Authority,
|
||||
idTokenClaims?: TokenClaims
|
||||
): AuthenticationResult {
|
||||
let accessToken: string = "";
|
||||
let responseScopes: Array<string> = [];
|
||||
let expiresOn: Date | null = null;
|
||||
let extExpiresOn: Date | undefined;
|
||||
|
||||
if (cacheRecord?.accessToken) {
|
||||
accessToken = cacheRecord.accessToken.secret;
|
||||
responseScopes = ScopeSet.fromString(
|
||||
cacheRecord.accessToken.target
|
||||
).asArray();
|
||||
// Access token expiresOn stored in seconds, converting to Date for AuthenticationResult
|
||||
expiresOn = TimeUtils.toDateFromSeconds(
|
||||
cacheRecord.accessToken.expiresOn
|
||||
);
|
||||
extExpiresOn = TimeUtils.toDateFromSeconds(
|
||||
cacheRecord.accessToken.extendedExpiresOn
|
||||
);
|
||||
}
|
||||
|
||||
const accountEntity = cacheRecord.account;
|
||||
|
||||
return {
|
||||
authority: authority.canonicalAuthority,
|
||||
uniqueId: cacheRecord.account.localAccountId,
|
||||
tenantId: cacheRecord.account.realm,
|
||||
scopes: responseScopes,
|
||||
account: AccountEntityUtils.getAccountInfo(accountEntity),
|
||||
idToken: cacheRecord.idToken?.secret || "",
|
||||
idTokenClaims: idTokenClaims || {},
|
||||
accessToken: accessToken,
|
||||
fromCache: true,
|
||||
expiresOn: expiresOn,
|
||||
correlationId: request.correlationId || "",
|
||||
requestId: "",
|
||||
extExpiresOn: extExpiresOn,
|
||||
familyId: cacheRecord.refreshToken?.familyId || "",
|
||||
tokenType: cacheRecord?.accessToken?.tokenType || "",
|
||||
state: request.state || "",
|
||||
cloudGraphHostName: accountEntity.cloudGraphHostName || "",
|
||||
msGraphHost: accountEntity.msGraphHost || "",
|
||||
fromPlatformBroker: false,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user