@azure/msal-browser 3.27.0 → 4.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/IPublicClientApplication.mjs +1 -1
- package/dist/app/PublicClientApplication.mjs +1 -1
- package/dist/app/PublicClientNext.mjs +1 -1
- package/dist/broker/nativeBroker/NativeMessageHandler.mjs +1 -1
- package/dist/broker/nativeBroker/NativeStatusCodes.mjs +1 -1
- package/dist/cache/AccountManager.mjs +1 -1
- package/dist/cache/AsyncMemoryStorage.mjs +1 -1
- package/dist/cache/BrowserCacheManager.d.ts +9 -85
- package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
- package/dist/cache/BrowserCacheManager.mjs +55 -398
- package/dist/cache/BrowserCacheManager.mjs.map +1 -1
- package/dist/cache/CacheHelpers.d.ts +16 -0
- package/dist/cache/CacheHelpers.d.ts.map +1 -0
- package/dist/cache/CacheHelpers.mjs +46 -0
- package/dist/cache/CacheHelpers.mjs.map +1 -0
- package/dist/cache/CookieStorage.d.ts +17 -0
- package/dist/cache/CookieStorage.d.ts.map +1 -0
- package/dist/cache/CookieStorage.mjs +73 -0
- package/dist/cache/CookieStorage.mjs.map +1 -0
- package/dist/cache/DatabaseStorage.mjs +1 -1
- package/dist/cache/ITokenCache.d.ts +1 -1
- package/dist/cache/ITokenCache.d.ts.map +1 -1
- package/dist/cache/IWindowStorage.d.ts +12 -0
- package/dist/cache/IWindowStorage.d.ts.map +1 -1
- package/dist/cache/LocalStorage.d.ts +43 -0
- package/dist/cache/LocalStorage.d.ts.map +1 -0
- package/dist/cache/LocalStorage.mjs +201 -0
- package/dist/cache/LocalStorage.mjs.map +1 -0
- package/dist/cache/MemoryStorage.d.ts +3 -0
- package/dist/cache/MemoryStorage.d.ts.map +1 -1
- package/dist/cache/MemoryStorage.mjs +10 -1
- package/dist/cache/MemoryStorage.mjs.map +1 -1
- package/dist/cache/SessionStorage.d.ts +13 -0
- package/dist/cache/SessionStorage.d.ts.map +1 -0
- package/dist/cache/SessionStorage.mjs +43 -0
- package/dist/cache/SessionStorage.mjs.map +1 -0
- package/dist/cache/TokenCache.d.ts +1 -1
- package/dist/cache/TokenCache.d.ts.map +1 -1
- package/dist/cache/TokenCache.mjs +15 -15
- package/dist/cache/TokenCache.mjs.map +1 -1
- package/dist/config/Configuration.mjs +1 -1
- package/dist/controllers/ControllerFactory.mjs +1 -1
- package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
- package/dist/controllers/NestedAppAuthController.mjs +4 -4
- package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
- package/dist/controllers/StandardController.d.ts.map +1 -1
- package/dist/controllers/StandardController.mjs +3 -2
- package/dist/controllers/StandardController.mjs.map +1 -1
- package/dist/controllers/UnknownOperatingContextController.mjs +1 -1
- package/dist/crypto/BrowserCrypto.d.ts +27 -0
- package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
- package/dist/crypto/BrowserCrypto.mjs +83 -3
- package/dist/crypto/BrowserCrypto.mjs.map +1 -1
- package/dist/crypto/CryptoOps.mjs +1 -1
- package/dist/crypto/PkceGenerator.mjs +1 -1
- package/dist/crypto/SignedHttpRequest.mjs +1 -1
- package/dist/encode/Base64Decode.d.ts +5 -0
- package/dist/encode/Base64Decode.d.ts.map +1 -1
- package/dist/encode/Base64Decode.mjs +2 -2
- package/dist/encode/Base64Decode.mjs.map +1 -1
- package/dist/encode/Base64Encode.mjs +1 -1
- package/dist/error/BrowserAuthError.mjs +1 -1
- package/dist/error/BrowserAuthErrorCodes.mjs +1 -1
- package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
- package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
- package/dist/error/NativeAuthError.mjs +1 -1
- package/dist/error/NativeAuthErrorCodes.mjs +1 -1
- package/dist/error/NestedAppAuthError.mjs +1 -1
- package/dist/event/EventHandler.mjs +1 -1
- package/dist/event/EventMessage.mjs +1 -1
- package/dist/event/EventType.mjs +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
- package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
- package/dist/interaction_client/NativeInteractionClient.d.ts +1 -1
- package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
- package/dist/interaction_client/NativeInteractionClient.mjs +4 -4
- package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
- package/dist/interaction_client/PopupClient.mjs +1 -1
- package/dist/interaction_client/RedirectClient.mjs +1 -1
- package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
- package/dist/interaction_client/SilentCacheClient.mjs +1 -1
- package/dist/interaction_client/SilentIframeClient.mjs +1 -1
- package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
- package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
- package/dist/interaction_client/StandardInteractionClient.mjs +5 -8
- package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
- package/dist/interaction_handler/InteractionHandler.mjs +1 -1
- package/dist/interaction_handler/RedirectHandler.mjs +1 -1
- package/dist/interaction_handler/SilentHandler.mjs +1 -1
- package/dist/naa/BridgeError.mjs +1 -1
- package/dist/naa/BridgeProxy.mjs +1 -1
- package/dist/naa/BridgeStatusCode.mjs +1 -1
- package/dist/naa/mapping/NestedAppAuthAdapter.mjs +1 -1
- package/dist/navigation/NavigationClient.mjs +1 -1
- package/dist/network/FetchClient.mjs +1 -1
- package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
- package/dist/operatingcontext/NestedAppOperatingContext.mjs +1 -1
- package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
- package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
- package/dist/packageMetadata.d.ts +1 -1
- package/dist/packageMetadata.d.ts.map +1 -1
- package/dist/packageMetadata.mjs +2 -2
- package/dist/request/RequestHelpers.mjs +1 -1
- package/dist/response/ResponseHandler.mjs +1 -1
- package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
- package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
- package/dist/utils/BrowserConstants.mjs +1 -1
- package/dist/utils/BrowserProtocolUtils.mjs +1 -1
- package/dist/utils/BrowserUtils.mjs +1 -1
- package/lib/msal-browser.cjs +587 -622
- package/lib/msal-browser.cjs.map +1 -1
- package/lib/msal-browser.js +587 -622
- package/lib/msal-browser.js.map +1 -1
- package/lib/msal-browser.min.js +65 -66
- package/lib/types/cache/BrowserCacheManager.d.ts +9 -85
- package/lib/types/cache/BrowserCacheManager.d.ts.map +1 -1
- package/lib/types/cache/CacheHelpers.d.ts +16 -0
- package/lib/types/cache/CacheHelpers.d.ts.map +1 -0
- package/lib/types/cache/CookieStorage.d.ts +17 -0
- package/lib/types/cache/CookieStorage.d.ts.map +1 -0
- package/lib/types/cache/ITokenCache.d.ts +1 -1
- package/lib/types/cache/ITokenCache.d.ts.map +1 -1
- package/lib/types/cache/IWindowStorage.d.ts +12 -0
- package/lib/types/cache/IWindowStorage.d.ts.map +1 -1
- package/lib/types/cache/LocalStorage.d.ts +43 -0
- package/lib/types/cache/LocalStorage.d.ts.map +1 -0
- package/lib/types/cache/MemoryStorage.d.ts +3 -0
- package/lib/types/cache/MemoryStorage.d.ts.map +1 -1
- package/lib/types/cache/SessionStorage.d.ts +13 -0
- package/lib/types/cache/SessionStorage.d.ts.map +1 -0
- package/lib/types/cache/TokenCache.d.ts +1 -1
- package/lib/types/cache/TokenCache.d.ts.map +1 -1
- package/lib/types/controllers/NestedAppAuthController.d.ts.map +1 -1
- package/lib/types/controllers/StandardController.d.ts.map +1 -1
- package/lib/types/crypto/BrowserCrypto.d.ts +27 -0
- package/lib/types/crypto/BrowserCrypto.d.ts.map +1 -1
- package/lib/types/encode/Base64Decode.d.ts +5 -0
- package/lib/types/encode/Base64Decode.d.ts.map +1 -1
- package/lib/types/index.d.ts +3 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/interaction_client/NativeInteractionClient.d.ts +1 -1
- package/lib/types/interaction_client/NativeInteractionClient.d.ts.map +1 -1
- package/lib/types/interaction_client/StandardInteractionClient.d.ts.map +1 -1
- package/lib/types/packageMetadata.d.ts +1 -1
- package/lib/types/packageMetadata.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/cache/BrowserCacheManager.ts +85 -552
- package/src/cache/CacheHelpers.ts +52 -0
- package/src/cache/CookieStorage.ts +98 -0
- package/src/cache/ITokenCache.ts +1 -1
- package/src/cache/IWindowStorage.ts +14 -0
- package/src/cache/LocalStorage.ts +270 -0
- package/src/cache/MemoryStorage.ts +12 -0
- package/src/cache/SessionStorage.ts +52 -0
- package/src/cache/TokenCache.ts +19 -19
- package/src/controllers/NestedAppAuthController.ts +3 -3
- package/src/controllers/StandardController.ts +3 -1
- package/src/crypto/BrowserCrypto.ts +120 -1
- package/src/encode/Base64Decode.ts +1 -1
- package/src/index.ts +3 -1
- package/src/interaction_client/NativeInteractionClient.ts +3 -3
- package/src/interaction_client/StandardInteractionClient.ts +5 -8
- package/src/packageMetadata.ts +1 -1
- package/dist/cache/BrowserStorage.d.ts +0 -12
- package/dist/cache/BrowserStorage.d.ts.map +0 -1
- package/dist/cache/BrowserStorage.mjs +0 -41
- package/dist/cache/BrowserStorage.mjs.map +0 -1
- package/lib/types/cache/BrowserStorage.d.ts +0 -12
- package/lib/types/cache/BrowserStorage.d.ts.map +0 -1
- package/src/cache/BrowserStorage.ts +0 -52
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TokenKeys } from "@azure/msal-common/browser";
|
|
7
|
+
import { StaticCacheKeys } from "../utils/BrowserConstants.js";
|
|
8
|
+
import { IWindowStorage } from "./IWindowStorage.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns a list of cache keys for all known accounts
|
|
12
|
+
* @param storage
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export function getAccountKeys(storage: IWindowStorage<string>): Array<string> {
|
|
16
|
+
const accountKeys = storage.getItem(StaticCacheKeys.ACCOUNT_KEYS);
|
|
17
|
+
if (accountKeys) {
|
|
18
|
+
return JSON.parse(accountKeys);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns a list of cache keys for all known tokens
|
|
26
|
+
* @param clientId
|
|
27
|
+
* @param storage
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function getTokenKeys(
|
|
31
|
+
clientId: string,
|
|
32
|
+
storage: IWindowStorage<string>
|
|
33
|
+
): TokenKeys {
|
|
34
|
+
const item = storage.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${clientId}`);
|
|
35
|
+
if (item) {
|
|
36
|
+
const tokenKeys = JSON.parse(item);
|
|
37
|
+
if (
|
|
38
|
+
tokenKeys &&
|
|
39
|
+
tokenKeys.hasOwnProperty("idToken") &&
|
|
40
|
+
tokenKeys.hasOwnProperty("accessToken") &&
|
|
41
|
+
tokenKeys.hasOwnProperty("refreshToken")
|
|
42
|
+
) {
|
|
43
|
+
return tokenKeys as TokenKeys;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
idToken: [],
|
|
49
|
+
accessToken: [],
|
|
50
|
+
refreshToken: [],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ClientAuthErrorCodes,
|
|
8
|
+
createClientAuthError,
|
|
9
|
+
} from "@azure/msal-common/browser";
|
|
10
|
+
import { IWindowStorage } from "./IWindowStorage.js";
|
|
11
|
+
|
|
12
|
+
// Cookie life calculation (hours * minutes * seconds * ms)
|
|
13
|
+
const COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
export class CookieStorage implements IWindowStorage<string> {
|
|
16
|
+
initialize(): Promise<void> {
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getItem(key: string): string | null {
|
|
21
|
+
const name = `${encodeURIComponent(key)}`;
|
|
22
|
+
const cookieList = document.cookie.split(";");
|
|
23
|
+
for (let i = 0; i < cookieList.length; i++) {
|
|
24
|
+
const cookie = cookieList[i];
|
|
25
|
+
const [key, ...rest] = decodeURIComponent(cookie).trim().split("=");
|
|
26
|
+
const value = rest.join("=");
|
|
27
|
+
|
|
28
|
+
if (key === name) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getUserData(): string | null {
|
|
36
|
+
throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setItem(
|
|
40
|
+
key: string,
|
|
41
|
+
value: string,
|
|
42
|
+
cookieLifeDays?: number,
|
|
43
|
+
secure: boolean = true
|
|
44
|
+
): void {
|
|
45
|
+
let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(
|
|
46
|
+
value
|
|
47
|
+
)};path=/;SameSite=Lax;`;
|
|
48
|
+
|
|
49
|
+
if (cookieLifeDays) {
|
|
50
|
+
const expireTime = getCookieExpirationTime(cookieLifeDays);
|
|
51
|
+
cookieStr += `expires=${expireTime};`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (secure) {
|
|
55
|
+
cookieStr += "Secure;";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
document.cookie = cookieStr;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async setUserData(): Promise<void> {
|
|
62
|
+
return Promise.reject(
|
|
63
|
+
createClientAuthError(ClientAuthErrorCodes.methodNotImplemented)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
removeItem(key: string): void {
|
|
68
|
+
// Setting expiration to -1 removes it
|
|
69
|
+
this.setItem(key, "", -1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getKeys(): string[] {
|
|
73
|
+
const cookieList = document.cookie.split(";");
|
|
74
|
+
const keys: Array<string> = [];
|
|
75
|
+
cookieList.forEach((cookie) => {
|
|
76
|
+
const cookieParts = decodeURIComponent(cookie).trim().split("=");
|
|
77
|
+
keys.push(cookieParts[0]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return keys;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
containsKey(key: string): boolean {
|
|
84
|
+
return this.getKeys().includes(key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get cookie expiration time
|
|
90
|
+
* @param cookieLifeDays
|
|
91
|
+
*/
|
|
92
|
+
export function getCookieExpirationTime(cookieLifeDays: number): string {
|
|
93
|
+
const today = new Date();
|
|
94
|
+
const expr = new Date(
|
|
95
|
+
today.getTime() + cookieLifeDays * COOKIE_LIFE_MULTIPLIER
|
|
96
|
+
);
|
|
97
|
+
return expr.toUTCString();
|
|
98
|
+
}
|
package/src/cache/ITokenCache.ts
CHANGED
|
@@ -4,12 +4,21 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export interface IWindowStorage<T> {
|
|
7
|
+
/**
|
|
8
|
+
* Async initializer
|
|
9
|
+
*/
|
|
10
|
+
initialize(): Promise<void>;
|
|
7
11
|
/**
|
|
8
12
|
* Get the item from the window storage object matching the given key.
|
|
9
13
|
* @param key
|
|
10
14
|
*/
|
|
11
15
|
getItem(key: string): T | null;
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Getter for sensitive data that may contain PII.
|
|
19
|
+
*/
|
|
20
|
+
getUserData(key: string): T | null;
|
|
21
|
+
|
|
13
22
|
/**
|
|
14
23
|
* Sets the item in the window storage object with the given key.
|
|
15
24
|
* @param key
|
|
@@ -17,6 +26,11 @@ export interface IWindowStorage<T> {
|
|
|
17
26
|
*/
|
|
18
27
|
setItem(key: string, value: T): void;
|
|
19
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Setter for sensitive data that may contain PII.
|
|
31
|
+
*/
|
|
32
|
+
setUserData(key: string, value: T): Promise<void>;
|
|
33
|
+
|
|
20
34
|
/**
|
|
21
35
|
* Removes the item in the window storage object matching the given key.
|
|
22
36
|
* @param key
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Constants, TokenKeys } from "@azure/msal-common/browser";
|
|
7
|
+
import {
|
|
8
|
+
createNewGuid,
|
|
9
|
+
decrypt,
|
|
10
|
+
encrypt,
|
|
11
|
+
generateBaseKey,
|
|
12
|
+
generateHKDF,
|
|
13
|
+
} from "../crypto/BrowserCrypto.js";
|
|
14
|
+
import { base64DecToArr } from "../encode/Base64Decode.js";
|
|
15
|
+
import { urlEncodeArr } from "../encode/Base64Encode.js";
|
|
16
|
+
import {
|
|
17
|
+
BrowserAuthErrorCodes,
|
|
18
|
+
createBrowserAuthError,
|
|
19
|
+
} from "../error/BrowserAuthError.js";
|
|
20
|
+
import {
|
|
21
|
+
BrowserConfigurationAuthErrorCodes,
|
|
22
|
+
createBrowserConfigurationAuthError,
|
|
23
|
+
} from "../error/BrowserConfigurationAuthError.js";
|
|
24
|
+
import { CookieStorage } from "./CookieStorage.js";
|
|
25
|
+
import { IWindowStorage } from "./IWindowStorage.js";
|
|
26
|
+
import { MemoryStorage } from "./MemoryStorage.js";
|
|
27
|
+
import { getAccountKeys, getTokenKeys } from "./CacheHelpers.js";
|
|
28
|
+
|
|
29
|
+
const ENCRYPTION_KEY = "msal.cache.encryption";
|
|
30
|
+
|
|
31
|
+
type EncryptionCookie = {
|
|
32
|
+
id: string;
|
|
33
|
+
key: CryptoKey;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type EncryptedData = {
|
|
37
|
+
id: string;
|
|
38
|
+
nonce: string;
|
|
39
|
+
data: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export class LocalStorage implements IWindowStorage<string> {
|
|
43
|
+
private clientId: string;
|
|
44
|
+
private initialized: boolean;
|
|
45
|
+
private memoryStorage: MemoryStorage<string>;
|
|
46
|
+
private encryptionCookie?: EncryptionCookie;
|
|
47
|
+
|
|
48
|
+
constructor(clientId: string) {
|
|
49
|
+
if (!window.localStorage) {
|
|
50
|
+
throw createBrowserConfigurationAuthError(
|
|
51
|
+
BrowserConfigurationAuthErrorCodes.storageNotSupported
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
this.memoryStorage = new MemoryStorage<string>();
|
|
55
|
+
this.initialized = false;
|
|
56
|
+
this.clientId = clientId;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async initialize(): Promise<void> {
|
|
60
|
+
this.initialized = true;
|
|
61
|
+
|
|
62
|
+
const cookies = new CookieStorage();
|
|
63
|
+
const cookieString = cookies.getItem(ENCRYPTION_KEY);
|
|
64
|
+
let parsedCookie = { key: "", id: "" };
|
|
65
|
+
if (cookieString) {
|
|
66
|
+
try {
|
|
67
|
+
parsedCookie = JSON.parse(cookieString);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// TODO: Log telemetry but don't throw
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (parsedCookie.key && parsedCookie.id) {
|
|
73
|
+
// Encryption key already exists, import
|
|
74
|
+
this.encryptionCookie = {
|
|
75
|
+
id: parsedCookie.id,
|
|
76
|
+
key: await generateHKDF(base64DecToArr(parsedCookie.key)),
|
|
77
|
+
};
|
|
78
|
+
await this.importExistingCache();
|
|
79
|
+
} else {
|
|
80
|
+
// Encryption key doesn't exist or is invalid, generate a new one and clear existing cache
|
|
81
|
+
this.clear();
|
|
82
|
+
const id = createNewGuid();
|
|
83
|
+
const baseKey = await generateBaseKey();
|
|
84
|
+
const keyStr = urlEncodeArr(new Uint8Array(baseKey));
|
|
85
|
+
this.encryptionCookie = {
|
|
86
|
+
id: id,
|
|
87
|
+
key: await generateHKDF(baseKey),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const cookieData = {
|
|
91
|
+
id: id,
|
|
92
|
+
key: keyStr,
|
|
93
|
+
};
|
|
94
|
+
cookies.setItem(ENCRYPTION_KEY, JSON.stringify(cookieData));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getItem(key: string): string | null {
|
|
99
|
+
return window.localStorage.getItem(key);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getUserData(key: string): string | null {
|
|
103
|
+
if (!this.initialized) {
|
|
104
|
+
throw createBrowserAuthError(
|
|
105
|
+
BrowserAuthErrorCodes.uninitializedPublicClientApplication
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return this.memoryStorage.getItem(key);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setItem(key: string, value: string): void {
|
|
112
|
+
window.localStorage.setItem(key, value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async setUserData(key: string, value: string): Promise<void> {
|
|
116
|
+
if (!this.initialized || !this.encryptionCookie) {
|
|
117
|
+
throw createBrowserAuthError(
|
|
118
|
+
BrowserAuthErrorCodes.uninitializedPublicClientApplication
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const { data, nonce } = await encrypt(
|
|
123
|
+
this.encryptionCookie.key,
|
|
124
|
+
value,
|
|
125
|
+
this.getContext(key)
|
|
126
|
+
);
|
|
127
|
+
const encryptedData: EncryptedData = {
|
|
128
|
+
id: this.encryptionCookie.id,
|
|
129
|
+
nonce: nonce,
|
|
130
|
+
data: data,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.memoryStorage.setItem(key, value);
|
|
134
|
+
this.setItem(key, JSON.stringify(encryptedData));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
removeItem(key: string): void {
|
|
138
|
+
this.memoryStorage.removeItem(key);
|
|
139
|
+
window.localStorage.removeItem(key);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getKeys(): string[] {
|
|
143
|
+
return Object.keys(window.localStorage);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
containsKey(key: string): boolean {
|
|
147
|
+
return window.localStorage.hasOwnProperty(key);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Removes all known MSAL keys from the cache
|
|
152
|
+
*/
|
|
153
|
+
clear(): void {
|
|
154
|
+
// Removes all remaining MSAL cache items
|
|
155
|
+
this.memoryStorage.clear();
|
|
156
|
+
|
|
157
|
+
const accountKeys = getAccountKeys(this);
|
|
158
|
+
accountKeys.forEach((key) => this.removeItem(key));
|
|
159
|
+
const tokenKeys = getTokenKeys(this.clientId, this);
|
|
160
|
+
tokenKeys.idToken.forEach((key) => this.removeItem(key));
|
|
161
|
+
tokenKeys.accessToken.forEach((key) => this.removeItem(key));
|
|
162
|
+
tokenKeys.refreshToken.forEach((key) => this.removeItem(key));
|
|
163
|
+
|
|
164
|
+
// Clean up anything left
|
|
165
|
+
this.getKeys().forEach((cacheKey: string) => {
|
|
166
|
+
if (
|
|
167
|
+
cacheKey.startsWith(Constants.CACHE_PREFIX) ||
|
|
168
|
+
cacheKey.indexOf(this.clientId) !== -1
|
|
169
|
+
) {
|
|
170
|
+
this.removeItem(cacheKey);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Helper to decrypt all known MSAL keys in localStorage and save them to inMemory storage
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
private async importExistingCache(): Promise<void> {
|
|
180
|
+
if (!this.encryptionCookie) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const accountKeys = getAccountKeys(this);
|
|
185
|
+
await this.importArray(accountKeys);
|
|
186
|
+
|
|
187
|
+
const tokenKeys: TokenKeys = getTokenKeys(this.clientId, this);
|
|
188
|
+
await Promise.all([
|
|
189
|
+
this.importArray(tokenKeys.idToken),
|
|
190
|
+
this.importArray(tokenKeys.accessToken),
|
|
191
|
+
this.importArray(tokenKeys.refreshToken),
|
|
192
|
+
]);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Helper to decrypt and save cache entries
|
|
197
|
+
* @param key
|
|
198
|
+
* @returns
|
|
199
|
+
*/
|
|
200
|
+
private async getItemFromEncryptedCache(
|
|
201
|
+
key: string
|
|
202
|
+
): Promise<string | null> {
|
|
203
|
+
if (!this.encryptionCookie) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const rawCache = this.getItem(key);
|
|
208
|
+
if (!rawCache) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let encObj: EncryptedData;
|
|
213
|
+
try {
|
|
214
|
+
encObj = JSON.parse(rawCache);
|
|
215
|
+
if (!encObj.id || !encObj.nonce || !encObj.data) {
|
|
216
|
+
throw "Not encrypted!"; // TODO: Typed error
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (encObj.id !== this.encryptionCookie.id) {
|
|
220
|
+
throw "Old item!"; // TODO: Typed error
|
|
221
|
+
}
|
|
222
|
+
} catch (e) {
|
|
223
|
+
// Not a valid encrypted object, remove
|
|
224
|
+
this.removeItem(key);
|
|
225
|
+
// TODO: Log to telemetry
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return decrypt(
|
|
230
|
+
this.encryptionCookie.key,
|
|
231
|
+
encObj.nonce,
|
|
232
|
+
this.getContext(key),
|
|
233
|
+
encObj.data
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Helper to decrypt and save an array of cache keys
|
|
239
|
+
* @param arr
|
|
240
|
+
*/
|
|
241
|
+
private async importArray(arr: Array<string>): Promise<void> {
|
|
242
|
+
const promiseArr: Array<Promise<void>> = [];
|
|
243
|
+
arr.forEach((key) => {
|
|
244
|
+
const promise = this.getItemFromEncryptedCache(key).then(
|
|
245
|
+
(value) => {
|
|
246
|
+
if (value) {
|
|
247
|
+
this.memoryStorage.setItem(key, value);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
promiseArr.push(promise);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
await Promise.all(promiseArr);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Gets encryption context for a given cache entry. This is clientId for app specific entries, empty string for shared entries
|
|
259
|
+
* @param key
|
|
260
|
+
* @returns
|
|
261
|
+
*/
|
|
262
|
+
private getContext(key: string): string {
|
|
263
|
+
let context = "";
|
|
264
|
+
if (key.includes(this.clientId)) {
|
|
265
|
+
context = this.clientId; // Used to bind encryption key to this appId
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return context;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -12,14 +12,26 @@ export class MemoryStorage<T> implements IWindowStorage<T> {
|
|
|
12
12
|
this.cache = new Map<string, T>();
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
async initialize(): Promise<void> {
|
|
16
|
+
// Memory storage does not require initialization
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
getItem(key: string): T | null {
|
|
16
20
|
return this.cache.get(key) || null;
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
getUserData(key: string): T | null {
|
|
24
|
+
return this.getItem(key);
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
setItem(key: string, value: T): void {
|
|
20
28
|
this.cache.set(key, value);
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
async setUserData(key: string, value: T): Promise<void> {
|
|
32
|
+
this.setItem(key, value);
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
removeItem(key: string): void {
|
|
24
36
|
this.cache.delete(key);
|
|
25
37
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
BrowserConfigurationAuthErrorCodes,
|
|
8
|
+
createBrowserConfigurationAuthError,
|
|
9
|
+
} from "../error/BrowserConfigurationAuthError.js";
|
|
10
|
+
import { IWindowStorage } from "./IWindowStorage.js";
|
|
11
|
+
|
|
12
|
+
export class SessionStorage implements IWindowStorage<string> {
|
|
13
|
+
constructor() {
|
|
14
|
+
if (!window.sessionStorage) {
|
|
15
|
+
throw createBrowserConfigurationAuthError(
|
|
16
|
+
BrowserConfigurationAuthErrorCodes.storageNotSupported
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async initialize(): Promise<void> {
|
|
22
|
+
// Session storage does not require initialization
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getItem(key: string): string | null {
|
|
26
|
+
return window.sessionStorage.getItem(key);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getUserData(key: string): string | null {
|
|
30
|
+
return this.getItem(key);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setItem(key: string, value: string): void {
|
|
34
|
+
window.sessionStorage.setItem(key, value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async setUserData(key: string, value: string): Promise<void> {
|
|
38
|
+
this.setItem(key, value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
removeItem(key: string): void {
|
|
42
|
+
window.sessionStorage.removeItem(key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getKeys(): string[] {
|
|
46
|
+
return Object.keys(window.sessionStorage);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
containsKey(key: string): boolean {
|
|
50
|
+
return window.sessionStorage.hasOwnProperty(key);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/cache/TokenCache.ts
CHANGED
|
@@ -75,11 +75,11 @@ export class TokenCache implements ITokenCache {
|
|
|
75
75
|
* @param options
|
|
76
76
|
* @returns `AuthenticationResult` for the response that was loaded.
|
|
77
77
|
*/
|
|
78
|
-
loadExternalTokens(
|
|
78
|
+
async loadExternalTokens(
|
|
79
79
|
request: SilentRequest,
|
|
80
80
|
response: ExternalTokenResponse,
|
|
81
81
|
options: LoadTokenOptions
|
|
82
|
-
): AuthenticationResult {
|
|
82
|
+
): Promise<AuthenticationResult> {
|
|
83
83
|
if (!this.isBrowserEnvironment) {
|
|
84
84
|
throw createBrowserAuthError(
|
|
85
85
|
BrowserAuthErrorCodes.nonBrowserEnvironment
|
|
@@ -112,21 +112,21 @@ export class TokenCache implements ITokenCache {
|
|
|
112
112
|
)
|
|
113
113
|
: undefined;
|
|
114
114
|
|
|
115
|
-
const cacheRecordAccount: AccountEntity = this.loadAccount(
|
|
115
|
+
const cacheRecordAccount: AccountEntity = await this.loadAccount(
|
|
116
116
|
request,
|
|
117
117
|
options.clientInfo || response.client_info || "",
|
|
118
118
|
idTokenClaims,
|
|
119
119
|
authority
|
|
120
120
|
);
|
|
121
121
|
|
|
122
|
-
const idToken = this.loadIdToken(
|
|
122
|
+
const idToken = await this.loadIdToken(
|
|
123
123
|
response,
|
|
124
124
|
cacheRecordAccount.homeAccountId,
|
|
125
125
|
cacheRecordAccount.environment,
|
|
126
126
|
cacheRecordAccount.realm
|
|
127
127
|
);
|
|
128
128
|
|
|
129
|
-
const accessToken = this.loadAccessToken(
|
|
129
|
+
const accessToken = await this.loadAccessToken(
|
|
130
130
|
request,
|
|
131
131
|
response,
|
|
132
132
|
cacheRecordAccount.homeAccountId,
|
|
@@ -135,7 +135,7 @@ export class TokenCache implements ITokenCache {
|
|
|
135
135
|
options
|
|
136
136
|
);
|
|
137
137
|
|
|
138
|
-
const refreshToken = this.loadRefreshToken(
|
|
138
|
+
const refreshToken = await this.loadRefreshToken(
|
|
139
139
|
response,
|
|
140
140
|
cacheRecordAccount.homeAccountId,
|
|
141
141
|
cacheRecordAccount.environment
|
|
@@ -163,19 +163,19 @@ export class TokenCache implements ITokenCache {
|
|
|
163
163
|
* @param requestHomeAccountId
|
|
164
164
|
* @returns `AccountEntity`
|
|
165
165
|
*/
|
|
166
|
-
private loadAccount(
|
|
166
|
+
private async loadAccount(
|
|
167
167
|
request: SilentRequest,
|
|
168
168
|
clientInfo: string,
|
|
169
169
|
idTokenClaims?: TokenClaims,
|
|
170
170
|
authority?: Authority
|
|
171
|
-
): AccountEntity {
|
|
171
|
+
): Promise<AccountEntity> {
|
|
172
172
|
this.logger.verbose("TokenCache - loading account");
|
|
173
173
|
|
|
174
174
|
if (request.account) {
|
|
175
175
|
const accountEntity = AccountEntity.createFromAccountInfo(
|
|
176
176
|
request.account
|
|
177
177
|
);
|
|
178
|
-
this.storage.setAccount(accountEntity);
|
|
178
|
+
await this.storage.setAccount(accountEntity);
|
|
179
179
|
return accountEntity;
|
|
180
180
|
} else if (!authority || (!clientInfo && !idTokenClaims)) {
|
|
181
181
|
this.logger.error(
|
|
@@ -210,7 +210,7 @@ export class TokenCache implements ITokenCache {
|
|
|
210
210
|
this.logger
|
|
211
211
|
);
|
|
212
212
|
|
|
213
|
-
this.storage.setAccount(cachedAccount);
|
|
213
|
+
await this.storage.setAccount(cachedAccount);
|
|
214
214
|
return cachedAccount;
|
|
215
215
|
}
|
|
216
216
|
|
|
@@ -222,12 +222,12 @@ export class TokenCache implements ITokenCache {
|
|
|
222
222
|
* @param tenantId
|
|
223
223
|
* @returns `IdTokenEntity`
|
|
224
224
|
*/
|
|
225
|
-
private loadIdToken(
|
|
225
|
+
private async loadIdToken(
|
|
226
226
|
response: ExternalTokenResponse,
|
|
227
227
|
homeAccountId: string,
|
|
228
228
|
environment: string,
|
|
229
229
|
tenantId: string
|
|
230
|
-
): IdTokenEntity | null {
|
|
230
|
+
): Promise<IdTokenEntity | null> {
|
|
231
231
|
if (!response.id_token) {
|
|
232
232
|
this.logger.verbose("TokenCache - no id token found in response");
|
|
233
233
|
return null;
|
|
@@ -242,7 +242,7 @@ export class TokenCache implements ITokenCache {
|
|
|
242
242
|
tenantId
|
|
243
243
|
);
|
|
244
244
|
|
|
245
|
-
this.storage.setIdTokenCredential(idTokenEntity);
|
|
245
|
+
await this.storage.setIdTokenCredential(idTokenEntity);
|
|
246
246
|
return idTokenEntity;
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -255,14 +255,14 @@ export class TokenCache implements ITokenCache {
|
|
|
255
255
|
* @param tenantId
|
|
256
256
|
* @returns `AccessTokenEntity`
|
|
257
257
|
*/
|
|
258
|
-
private loadAccessToken(
|
|
258
|
+
private async loadAccessToken(
|
|
259
259
|
request: SilentRequest,
|
|
260
260
|
response: ExternalTokenResponse,
|
|
261
261
|
homeAccountId: string,
|
|
262
262
|
environment: string,
|
|
263
263
|
tenantId: string,
|
|
264
264
|
options: LoadTokenOptions
|
|
265
|
-
): AccessTokenEntity | null {
|
|
265
|
+
): Promise<AccessTokenEntity | null> {
|
|
266
266
|
if (!response.access_token) {
|
|
267
267
|
this.logger.verbose(
|
|
268
268
|
"TokenCache - no access token found in response"
|
|
@@ -309,7 +309,7 @@ export class TokenCache implements ITokenCache {
|
|
|
309
309
|
base64Decode
|
|
310
310
|
);
|
|
311
311
|
|
|
312
|
-
this.storage.setAccessTokenCredential(accessTokenEntity);
|
|
312
|
+
await this.storage.setAccessTokenCredential(accessTokenEntity);
|
|
313
313
|
return accessTokenEntity;
|
|
314
314
|
}
|
|
315
315
|
|
|
@@ -321,11 +321,11 @@ export class TokenCache implements ITokenCache {
|
|
|
321
321
|
* @param environment
|
|
322
322
|
* @returns `RefreshTokenEntity`
|
|
323
323
|
*/
|
|
324
|
-
private loadRefreshToken(
|
|
324
|
+
private async loadRefreshToken(
|
|
325
325
|
response: ExternalTokenResponse,
|
|
326
326
|
homeAccountId: string,
|
|
327
327
|
environment: string
|
|
328
|
-
): RefreshTokenEntity | null {
|
|
328
|
+
): Promise<RefreshTokenEntity | null> {
|
|
329
329
|
if (!response.refresh_token) {
|
|
330
330
|
this.logger.verbose(
|
|
331
331
|
"TokenCache - no refresh token found in response"
|
|
@@ -344,7 +344,7 @@ export class TokenCache implements ITokenCache {
|
|
|
344
344
|
response.refresh_token_expires_in
|
|
345
345
|
);
|
|
346
346
|
|
|
347
|
-
this.storage.setRefreshTokenCredential(refreshTokenEntity);
|
|
347
|
+
await this.storage.setRefreshTokenCredential(refreshTokenEntity);
|
|
348
348
|
return refreshTokenEntity;
|
|
349
349
|
}
|
|
350
350
|
|