Skip to content

Commit 167ca67

Browse files
committed
2 parents 00873a3 + 504d352 commit 167ca67

File tree

120 files changed

+4240
-3512
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+4240
-3512
lines changed

build/sdl-tasks.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ steps:
1010
optionsXS: 1
1111
optionsHMENABLE: 0
1212

13-
- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
14-
displayName: 'Run CredScan3'
15-
inputs:
16-
scanFolder: './'
17-
debugMode: false
13+
# - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
14+
# displayName: 'Run CredScan3'
15+
# inputs:
16+
# scanFolder: './'
17+
# debugMode: false
1818

1919
- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1
2020
displayName: 'Post Analysis'
2121
inputs:
22-
CredScan: true
22+
CredScan: false
2323
PoliCheck: true

lib/msal-browser/src/app/PublicClientApplication.ts

Lines changed: 175 additions & 86 deletions
Large diffs are not rendered by default.

lib/msal-browser/src/cache/BrowserStorage.ts

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License.
44
*/
5-
import { ICacheStorage, Constants, PersistentCacheKeys, TemporaryCacheKeys, InMemoryCache } from "@azure/msal-common";
5+
import { ICacheStorage, Constants, PersistentCacheKeys, InMemoryCache, StringUtils, AuthorizationCodeRequest, ICrypto } from "@azure/msal-common";
66
import { CacheOptions } from "../config/Configuration";
77
import { BrowserAuthError } from "../error/BrowserAuthError";
88
import { BrowserConfigurationAuthError } from "../error/BrowserConfigurationAuthError";
9-
import { BrowserConstants } from "../utils/BrowserConstants";
9+
import { BrowserConstants, TemporaryCacheKeys } from "../utils/BrowserConstants";
1010

1111
// Cookie life calculation (hours * minutes * seconds * ms)
1212
const COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
@@ -152,12 +152,13 @@ export class BrowserStorage implements ICacheStorage {
152152
* Will also clear the cookie item if storeAuthStateInCookie is set to true.
153153
* @param key
154154
*/
155-
removeItem(key: string): void {
155+
removeItem(key: string): boolean {
156156
const msalKey = this.generateCacheKey(key);
157157
this.windowStorage.removeItem(msalKey);
158158
if (this.cacheConfig.storeAuthStateInCookie) {
159159
this.clearItemCookie(msalKey);
160160
}
161+
return true;
161162
}
162163

163164
/**
@@ -268,7 +269,133 @@ export class BrowserStorage implements ICacheStorage {
268269
/**
269270
* Dummy implementation until browser cache is migrated
270271
*/
271-
setCache() {
272+
setCache(): void {
272273
// sets nothing
273274
}
275+
276+
/**
277+
* Create authorityKey to cache authority
278+
* @param state
279+
*/
280+
generateAuthorityKey(state: string): string {
281+
return `${TemporaryCacheKeys.AUTHORITY}${Constants.RESOURCE_DELIM}${state}`;
282+
}
283+
284+
/**
285+
* Create Nonce key to cache nonce
286+
* @param state
287+
*/
288+
generateNonceKey(state: string): string {
289+
return `${TemporaryCacheKeys.NONCE_IDTOKEN}${Constants.RESOURCE_DELIM}${state}`;
290+
}
291+
292+
/**
293+
* Sets the cacheKey for and stores the authority information in cache
294+
* @param state
295+
* @param authority
296+
*/
297+
setAuthorityCache(authority: string, state: string): void {
298+
// Cache authorityKey
299+
const authorityKey = this.generateAuthorityKey(state);
300+
this.setItem(authorityKey, authority);
301+
}
302+
303+
/**
304+
* Updates account, authority, and state in cache
305+
* @param serverAuthenticationRequest
306+
* @param account
307+
*/
308+
updateCacheEntries(state: string, nonce: string, authorityInstance: string): void {
309+
// Cache the request state
310+
this.setItem(TemporaryCacheKeys.REQUEST_STATE, state);
311+
312+
// Cache the nonce
313+
this.setItem(this.generateNonceKey(state), nonce);
314+
315+
// Cache authorityKey
316+
this.setAuthorityCache(authorityInstance, state);
317+
}
318+
319+
/**
320+
* Reset all temporary cache items
321+
* @param state
322+
*/
323+
resetRequestCache(state: string): void {
324+
// check state and remove associated cache items
325+
this.getKeys().forEach(key => {
326+
if (!StringUtils.isEmpty(state) && key.indexOf(state) !== -1) {
327+
const splitKey = key.split(Constants.RESOURCE_DELIM);
328+
const keyState = splitKey.length > 1 ? splitKey[splitKey.length-1]: null;
329+
if (keyState === state) {
330+
this.removeItem(key);
331+
}
332+
}
333+
});
334+
335+
// delete generic interactive request parameters
336+
this.removeItem(TemporaryCacheKeys.REQUEST_STATE);
337+
this.removeItem(TemporaryCacheKeys.REQUEST_PARAMS);
338+
this.removeItem(TemporaryCacheKeys.ORIGIN_URI);
339+
}
340+
341+
cleanRequest(): void {
342+
// Interaction is completed - remove interaction status.
343+
this.removeItem(BrowserConstants.INTERACTION_STATUS_KEY);
344+
const cachedState = this.getItem(TemporaryCacheKeys.REQUEST_STATE);
345+
this.resetRequestCache(cachedState || "");
346+
}
347+
348+
cacheCodeRequest(authCodeRequest: AuthorizationCodeRequest, browserCrypto: ICrypto): void {
349+
this.setItem(TemporaryCacheKeys.REQUEST_PARAMS, browserCrypto.base64Encode(JSON.stringify(authCodeRequest)));
350+
}
351+
352+
/**
353+
* Gets the token exchange parameters from the cache. Throws an error if nothing is found.
354+
*/
355+
getCachedRequest(state: string, browserCrypto: ICrypto): AuthorizationCodeRequest {
356+
try {
357+
// Get token request from cache and parse as TokenExchangeParameters.
358+
const encodedTokenRequest = this.getItem(TemporaryCacheKeys.REQUEST_PARAMS);
359+
const parsedRequest = JSON.parse(browserCrypto.base64Decode(encodedTokenRequest)) as AuthorizationCodeRequest;
360+
this.removeItem(TemporaryCacheKeys.REQUEST_PARAMS);
361+
// Get cached authority and use if no authority is cached with request.
362+
if (StringUtils.isEmpty(parsedRequest.authority)) {
363+
const authorityKey: string = this.generateAuthorityKey(state);
364+
const cachedAuthority: string = this.getItem(authorityKey);
365+
parsedRequest.authority = cachedAuthority;
366+
}
367+
return parsedRequest;
368+
} catch (err) {
369+
throw BrowserAuthError.createTokenRequestCacheError(err);
370+
}
371+
}
372+
373+
/*
374+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
375+
* @param key
376+
* @param value
377+
* @param type
378+
*/
379+
setItemInMemory(key: string, value: object, type?: string): void {
380+
if (key && value && type)
381+
return;
382+
}
383+
384+
/**
385+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
386+
* @param key
387+
* @param type
388+
*/
389+
getItemFromMemory(key: string, type?: string): object {
390+
return key && type ? {} : {};
391+
};
392+
393+
/**
394+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
395+
* @param key
396+
* @param type
397+
*/
398+
removeItemFromMemory(key: string, type?: string): boolean {
399+
return key && type ? true : false;
400+
};
274401
}

lib/msal-browser/src/config/Configuration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License.
44
*/
5-
import { SystemOptions, LoggerOptions, INetworkModule, LogLevel, DEFAULT_SYSTEM_OPTIONS } from "@azure/msal-common";
5+
import { SystemOptions, LoggerOptions, INetworkModule, LogLevel, DEFAULT_SYSTEM_OPTIONS, Constants } from "@azure/msal-common";
66
import { BrowserUtils } from "../utils/BrowserUtils";
77
import { BrowserConstants } from "../utils/BrowserConstants";
88

@@ -64,7 +64,7 @@ export type Configuration = {
6464
// Default auth options for browser
6565
const DEFAULT_AUTH_OPTIONS: BrowserAuthOptions = {
6666
clientId: "",
67-
authority: null,
67+
authority: `${Constants.DEFAULT_AUTHORITY}/`,
6868
knownAuthorities: [],
6969
redirectUri: () => BrowserUtils.getCurrentUri(),
7070
postLogoutRedirectUri: () => BrowserUtils.getCurrentUri(),

lib/msal-browser/src/error/BrowserAuthError.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ export const BrowserAuthErrorMessage = {
7171
silentPromptValueError: {
7272
code: "silent_prompt_value_error",
7373
desc: "The value given for the prompt value is not valid for silent requests - must be set to 'none'."
74-
}
74+
},
75+
tokenRequestCacheError: {
76+
code: "token_request_cache_error",
77+
desc: "The token request could not be fetched from the cache correctly."
78+
},
7579
};
7680

7781
/**
@@ -215,4 +219,13 @@ export class BrowserAuthError extends AuthError {
215219
static createSilentPromptValueError(givenPrompt: string): BrowserAuthError {
216220
return new BrowserAuthError(BrowserAuthErrorMessage.silentPromptValueError.code, `${BrowserAuthErrorMessage.silentPromptValueError.desc} Given value: ${givenPrompt}`);
217221
}
222+
223+
/**
224+
* Creates an error thrown when the token request could not be retrieved from the cache
225+
* @param errDetail
226+
*/
227+
static createTokenRequestCacheError(errDetail: string): BrowserAuthError {
228+
return new BrowserAuthError(BrowserAuthErrorMessage.tokenRequestCacheError.code,
229+
`${BrowserAuthErrorMessage.tokenRequestCacheError.desc} Error Detail: ${errDetail}`);
230+
}
218231
}

lib/msal-browser/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ export type { AuthCallback } from "./types/AuthCallback";
1212
// Common Object Formats
1313
export {
1414
// Request
15-
AuthenticationParameters,
16-
TokenExchangeParameters,
15+
TokenRenewParameters,
1716
// Response
18-
AuthResponse,
1917
TokenResponse,
2018
// Error
2119
InteractionRequiredAuthError,

lib/msal-browser/src/interaction_handler/InteractionHandler.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License.
44
*/
5-
import { SPAClient, TokenResponse, StringUtils } from "@azure/msal-common";
5+
import { SPAClient, TokenResponse, StringUtils, AuthorizationCodeRequest, ProtocolUtils } from "@azure/msal-common";
66
import { BrowserStorage } from "../cache/BrowserStorage";
77
import { BrowserAuthError } from "../error/BrowserAuthError";
8+
import { TemporaryCacheKeys } from "../utils/BrowserConstants";
89

910
/**
1011
* Abstract class which defines operations for a browser interaction handling class.
@@ -13,6 +14,7 @@ export abstract class InteractionHandler {
1314

1415
protected authModule: SPAClient;
1516
protected browserStorage: BrowserStorage;
17+
protected authCodeRequest: AuthorizationCodeRequest;
1618

1719
constructor(authCodeModule: SPAClient, storageImpl: BrowserStorage) {
1820
this.authModule = authCodeModule;
@@ -23,7 +25,7 @@ export abstract class InteractionHandler {
2325
* Function to enable user interaction.
2426
* @param requestUrl
2527
*/
26-
abstract initiateAuthRequest(requestUrl: string): Window | Promise<HTMLIFrameElement>;
28+
abstract initiateAuthRequest(requestUrl: string, authCodeRequest: AuthorizationCodeRequest): Window | Promise<HTMLIFrameElement>;
2729

2830
/**
2931
* Function to handle response parameters from hash.
@@ -35,10 +37,23 @@ export abstract class InteractionHandler {
3537
throw BrowserAuthError.createEmptyHashError(locationHash);
3638
}
3739

40+
// Get cached items
41+
const requestState = this.browserStorage.getItem(TemporaryCacheKeys.REQUEST_STATE);
42+
const cachedNonceKey = this.browserStorage.generateNonceKey(requestState);
43+
const cachedNonce = this.browserStorage.getItem(cachedNonceKey);
44+
3845
// Handle code response.
39-
const codeResponse = this.authModule.handleFragmentResponse(locationHash);
40-
46+
const authCode = this.authModule.handleFragmentResponse(locationHash, requestState);
47+
48+
// Assign code to request
49+
this.authCodeRequest.code = authCode;
50+
51+
// Extract user state.
52+
const userState = ProtocolUtils.getUserRequestState(requestState);
53+
4154
// Acquire token with retrieved code.
42-
return this.authModule.acquireToken(codeResponse);
55+
const tokenResponse = await this.authModule.acquireToken(this.authCodeRequest, userState, cachedNonce);
56+
this.browserStorage.cleanRequest();
57+
return tokenResponse;
4358
}
4459
}

lib/msal-browser/src/interaction_handler/PopupHandler.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License.
44
*/
5-
import { UrlString, StringUtils, Constants, SPAClient } from "@azure/msal-common";
5+
import { UrlString, StringUtils, Constants, SPAClient, AuthorizationCodeRequest } from "@azure/msal-common";
66
import { InteractionHandler } from "./InteractionHandler";
77
import { BrowserAuthError } from "../error/BrowserAuthError";
88
import { BrowserConstants } from "../utils/BrowserConstants";
@@ -27,9 +27,11 @@ export class PopupHandler extends InteractionHandler {
2727
* Opens a popup window with given request Url.
2828
* @param requestUrl
2929
*/
30-
initiateAuthRequest(requestUrl: string): Window {
30+
initiateAuthRequest(requestUrl: string, authCodeRequest: AuthorizationCodeRequest): Window {
3131
// Check that request url is not empty.
3232
if (!StringUtils.isEmpty(requestUrl)) {
33+
// Save auth code request
34+
this.authCodeRequest = authCodeRequest;
3335
// Set interaction status in the library.
3436
this.browserStorage.setItem(BrowserConstants.INTERACTION_STATUS_KEY, BrowserConstants.INTERACTION_IN_PROGRESS_VALUE);
3537
this.authModule.logger.infoPii("Navigate to:" + requestUrl);
@@ -150,8 +152,7 @@ export class PopupHandler extends InteractionHandler {
150152
* Event callback to unload main window.
151153
*/
152154
unloadWindow(e: Event): void {
153-
this.authModule.cancelRequest();
154-
this.browserStorage.removeItem(BrowserConstants.INTERACTION_STATUS_KEY);
155+
this.browserStorage.cleanRequest();
155156
this.currentWindow.close();
156157
// Guarantees browser unload will happen, so no other errors will be thrown.
157158
delete e["returnValue"];

lib/msal-browser/src/interaction_handler/RedirectHandler.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License.
44
*/
5-
import { StringUtils, TemporaryCacheKeys, TokenResponse } from "@azure/msal-common";
5+
import { StringUtils, TokenResponse, AuthorizationCodeRequest, ICrypto, ProtocolUtils } from "@azure/msal-common";
66
import { InteractionHandler } from "./InteractionHandler";
77
import { BrowserAuthError } from "../error/BrowserAuthError";
8-
import { BrowserConstants } from "../utils/BrowserConstants";
8+
import { BrowserConstants, TemporaryCacheKeys } from "../utils/BrowserConstants";
99
import { BrowserUtils } from "../utils/BrowserUtils";
1010

1111
export class RedirectHandler extends InteractionHandler {
@@ -14,12 +14,13 @@ export class RedirectHandler extends InteractionHandler {
1414
* Redirects window to given URL.
1515
* @param urlNavigate
1616
*/
17-
initiateAuthRequest(requestUrl: string): Window {
17+
initiateAuthRequest(requestUrl: string, authCodeRequest: AuthorizationCodeRequest, browserCrypto?: ICrypto): Window {
1818
// Navigate if valid URL
1919
if (!StringUtils.isEmpty(requestUrl)) {
2020
// Set interaction status in the library.
2121
this.browserStorage.setItem(TemporaryCacheKeys.ORIGIN_URI, BrowserUtils.getCurrentUri());
2222
this.browserStorage.setItem(BrowserConstants.INTERACTION_STATUS_KEY, BrowserConstants.INTERACTION_IN_PROGRESS_VALUE);
23+
this.browserStorage.cacheCodeRequest(authCodeRequest, browserCrypto);
2324
this.authModule.logger.infoPii("Navigate to:" + requestUrl);
2425
const isIframedApp = BrowserUtils.isInIframe();
2526
if (isIframedApp) {
@@ -41,19 +42,34 @@ export class RedirectHandler extends InteractionHandler {
4142
* Handle authorization code response in the window.
4243
* @param hash
4344
*/
44-
async handleCodeResponse(locationHash: string): Promise<TokenResponse> {
45+
async handleCodeResponse(locationHash: string, browserCrypto?: ICrypto): Promise<TokenResponse> {
4546
// Check that location hash isn't empty.
4647
if (StringUtils.isEmpty(locationHash)) {
4748
throw BrowserAuthError.createEmptyHashError(locationHash);
4849
}
4950

5051
// Interaction is completed - remove interaction status.
5152
this.browserStorage.removeItem(BrowserConstants.INTERACTION_STATUS_KEY);
53+
54+
// Get cached items
55+
const requestState = this.browserStorage.getItem(TemporaryCacheKeys.REQUEST_STATE);
56+
const cachedNonceKey = this.browserStorage.generateNonceKey(requestState);
57+
const cachedNonce = this.browserStorage.getItem(cachedNonceKey);
58+
this.authCodeRequest = this.browserStorage.getCachedRequest(requestState, browserCrypto);
59+
5260
// Handle code response.
53-
const codeResponse = this.authModule.handleFragmentResponse(locationHash);
61+
const authCode = this.authModule.handleFragmentResponse(locationHash, requestState);
62+
this.authCodeRequest.code = authCode;
63+
5464
// Hash was processed successfully - remove from cache
5565
this.browserStorage.removeItem(TemporaryCacheKeys.URL_HASH);
66+
67+
// Extract user state.
68+
const userState = ProtocolUtils.getUserRequestState(requestState);
69+
5670
// Acquire token with retrieved code.
57-
return this.authModule.acquireToken(codeResponse);
71+
const tokenResponse = await this.authModule.acquireToken(this.authCodeRequest, userState, cachedNonce);
72+
this.browserStorage.cleanRequest();
73+
return tokenResponse;
5874
}
5975
}

0 commit comments

Comments
 (0)