Skip to content

Commit

Permalink
Merge branch 'dev' into throw-without-initialize
Browse files Browse the repository at this point in the history
  • Loading branch information
hectormmg committed Jul 24, 2023
2 parents 724846c + c4b8a98 commit 3de9243
Show file tree
Hide file tree
Showing 32 changed files with 842 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Added OIDCOptions parameter to config",
"packageName": "@azure/msal-browser",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Added OIDCOptions parameter to config",
"packageName": "@azure/msal-common",
"email": "[email protected]",
"dependentChangeType": "patch"
}
41 changes: 38 additions & 3 deletions lib/msal-browser/src/config/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import {
DEFAULT_SYSTEM_OPTIONS,
Constants,
ProtocolMode,
OIDCOptions,
ServerResponseType,
LogLevel,
StubbedNetworkModule,
AzureCloudInstance,
AzureCloudOptions,
ApplicationTelemetry,
ClientConfigurationError
} from "@azure/msal-common";
import { BrowserUtils } from "../utils/BrowserUtils";
import {
Expand Down Expand Up @@ -74,6 +77,10 @@ export type BrowserAuthOptions = {
* Enum that represents the protocol that msal follows. Used for configuring proper endpoints.
*/
protocolMode?: ProtocolMode;
/**
* Enum that configures options for the OIDC protocol mode.
*/
OIDCOptions?: OIDCOptions;
/**
* Enum that represents the Azure Cloud to use.
*/
Expand All @@ -84,6 +91,10 @@ export type BrowserAuthOptions = {
skipAuthorityMetadataCache?: boolean;
};

export type InternalAuthOptions = Required<BrowserAuthOptions> & {
OIDCOptions: Required<OIDCOptions>;
};

/**
* Use this to configure the below cache configuration options:
*/
Expand Down Expand Up @@ -204,7 +215,7 @@ export type Configuration = {
};

export type BrowserConfiguration = {
auth: Required<BrowserAuthOptions>;
auth: InternalAuthOptions;
cache: Required<CacheOptions>;
system: Required<BrowserSystemOptions>;
telemetry: Required<BrowserTelemetryOptions>;
Expand All @@ -229,7 +240,7 @@ export function buildConfiguration(
isBrowserEnvironment: boolean
): BrowserConfiguration {
// Default auth options for browser
const DEFAULT_AUTH_OPTIONS: Required<BrowserAuthOptions> = {
const DEFAULT_AUTH_OPTIONS: InternalAuthOptions = {
clientId: Constants.EMPTY_STRING,
authority: `${Constants.DEFAULT_AUTHORITY}`,
knownAuthorities: [],
Expand All @@ -240,6 +251,10 @@ export function buildConfiguration(
navigateToLoginRequestUrl: true,
clientCapabilities: [],
protocolMode: ProtocolMode.AAD,
OIDCOptions: {
serverResponseType: ServerResponseType.FRAGMENT,
defaultScopes: [Constants.OPENID_SCOPE, Constants.PROFILE_SCOPE, Constants.OFFLINE_ACCESS_SCOPE]
},
azureCloudOptions: {
azureCloudInstance: AzureCloudInstance.None,
tenant: Constants.EMPTY_STRING,
Expand Down Expand Up @@ -310,11 +325,31 @@ export function buildConfiguration(
},
};

// Throw an error if user has set OIDCOptions without being in OIDC protocol mode
if(userInputAuth?.protocolMode !== ProtocolMode.OIDC &&
userInputAuth?.OIDCOptions) {
// Logger has not been created yet
// eslint-disable-next-line no-console
console.warn(ClientConfigurationError.createCannotSetOIDCOptionsError());
}

// Throw an error if user has set allowNativeBroker to true without being in AAD protocol mode
if(userInputAuth?.protocolMode &&
userInputAuth.protocolMode !== ProtocolMode.AAD &&
providedSystemOptions?.allowNativeBroker) {
throw ClientConfigurationError.createCannotAllowNativeBrokerError();
}

const overlayedConfig: BrowserConfiguration = {
auth: { ...DEFAULT_AUTH_OPTIONS, ...userInputAuth },
auth: {
...DEFAULT_AUTH_OPTIONS,
...userInputAuth,
OIDCOptions: { ...DEFAULT_AUTH_OPTIONS.OIDCOptions, ...userInputAuth?.OIDCOptions }
},
cache: { ...DEFAULT_CACHE_OPTIONS, ...userInputCache },
system: { ...DEFAULT_BROWSER_SYSTEM_OPTIONS, ...providedSystemOptions },
telemetry: { ...DEFAULT_TELEMETRY_OPTIONS, ...userInputTelemetry },
};

return overlayedConfig;
}
15 changes: 12 additions & 3 deletions lib/msal-browser/src/controllers/StandardController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
InProgressPerformanceEvent,
RequestThumbprint,
ServerError,
ServerResponseType,
UrlString
} from "@azure/msal-common";
import {
BrowserCacheManager,
Expand Down Expand Up @@ -321,14 +323,21 @@ export class StandardController implements IController {
// Block token acquisition before initialize has been called
BrowserUtils.blockCallsBeforeInitialize(this.initialized);

let foundServerResponse = hash;

if(this.config.auth.OIDCOptions?.serverResponseType === ServerResponseType.QUERY) {
const url = window.location.href;
foundServerResponse = UrlString.parseQueryServerResponse(url);
}

const loggedInAccounts = this.getAllAccounts();
if (this.isBrowserEnvironment) {
/**
* Store the promise on the PublicClientApplication instance if this is the first invocation of handleRedirectPromise,
* otherwise return the promise from the first invocation. Prevents race conditions when handleRedirectPromise is called
* several times concurrently.
*/
const redirectResponseKey = hash || Constants.EMPTY_STRING;
const redirectResponseKey = foundServerResponse || Constants.EMPTY_STRING;
let response = this.redirectResponse.get(redirectResponseKey);
if (typeof response === "undefined") {
this.eventHandler.emitEvent(
Expand All @@ -350,7 +359,7 @@ export class StandardController implements IController {
this.nativeExtensionProvider
) &&
this.nativeExtensionProvider &&
!hash
!foundServerResponse
) {
this.logger.trace(
"handleRedirectPromise - acquiring token from native platform"
Expand Down Expand Up @@ -382,7 +391,7 @@ export class StandardController implements IController {
const redirectClient =
this.createRedirectClient(correlationId);
redirectResponse =
redirectClient.handleRedirectPromise(hash);
redirectClient.handleRedirectPromise(foundServerResponse);
}

response = redirectResponse
Expand Down
1 change: 1 addition & 0 deletions lib/msal-browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export {
LogLevel,
// Protocol Mode
ProtocolMode,
ServerResponseType,
PromptValue,
// Server Response
ExternalTokenResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export abstract class BaseInteractionClient {
this.logger.verbose("getDiscoveredAuthority called");
const authorityOptions: AuthorityOptions = {
protocolMode: this.config.auth.protocolMode,
OIDCOptions: this.config.auth.OIDCOptions,
knownAuthorities: this.config.auth.knownAuthorities,
cloudDiscoveryMetadata: this.config.auth.cloudDiscoveryMetadata,
authorityMetadata: this.config.auth.authorityMetadata,
Expand Down
69 changes: 62 additions & 7 deletions lib/msal-browser/src/interaction_client/PopupClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
IPerformanceClient,
Logger,
ICrypto,
ProtocolMode,
ServerResponseType,
} from "@azure/msal-common";
import { StandardInteractionClient } from "./StandardInteractionClient";
import { EventType } from "../event/EventType";
Expand Down Expand Up @@ -413,6 +415,42 @@ export class PopupClient extends StandardInteractionClient {
);
this.logger.verbose("Auth code client created");

try {
authClient.authority.endSessionEndpoint;
} catch {
if (validRequest.account?.homeAccountId && validRequest.postLogoutRedirectUri && authClient.authority.protocolMode === ProtocolMode.OIDC){
this.browserStorage.removeAccount(validRequest.account?.homeAccountId);

this.eventHandler.emitEvent(
EventType.LOGOUT_SUCCESS,
InteractionType.Popup,
validRequest
);

if (mainWindowRedirectUri){
const navigationOptions: NavigationOptions = {
apiId: ApiId.logoutPopup,
timeout: this.config.system.redirectNavigationTimeout,
noHistory: false,
};
const absoluteUrl = UrlString.getAbsoluteUrl(
mainWindowRedirectUri,
BrowserUtils.getCurrentUri()
);
await this.navigationClient.navigateInternal(
absoluteUrl,
navigationOptions
);
}

if (popup) {
popup.close();
}

return;
}
}

// Create logout string and navigate user window to logout.
const logoutUri: string = authClient.getLogoutUri(validRequest);

Expand Down Expand Up @@ -540,16 +578,16 @@ export class PopupClient extends StandardInteractionClient {
return;
}

let href: string = Constants.EMPTY_STRING;
let hash: string = Constants.EMPTY_STRING;
let href = Constants.EMPTY_STRING;
let serverResponseString = Constants.EMPTY_STRING;
try {
/*
* Will throw if cross origin,
* which should be caught and ignored
* since we need the interval to keep running while on STS UI.
*/
href = popupWindow.location.href;
hash = popupWindow.location.hash;
serverResponseString = this.extractServerResponseStringFromPopup(popupWindow, href);
} catch (e) {}

// Don't process blank pages or cross domain
Expand All @@ -567,24 +605,24 @@ export class PopupClient extends StandardInteractionClient {
*/
ticks++;

if (hash) {
if (serverResponseString) {
this.logger.verbose(
"PopupHandler.monitorPopupForHash - found hash in url"
);
clearInterval(intervalId);
this.cleanPopup(popupWindow);

if (UrlString.hashContainsKnownProperties(hash)) {
if (UrlString.hashContainsKnownProperties(serverResponseString)) {
this.logger.verbose(
"PopupHandler.monitorPopupForHash - hash contains known properties, returning."
);
resolve(hash);
resolve(serverResponseString);
} else {
this.logger.error(
"PopupHandler.monitorPopupForHash - found hash in url but it does not contain known properties. Check that your router is not changing the hash prematurely."
);
this.logger.errorPii(
`PopupHandler.monitorPopupForHash - hash found: ${hash}`
`PopupHandler.monitorPopupForHash - hash found: ${serverResponseString}`
);
reject(
BrowserAuthError.createHashDoesNotContainKnownPropertiesError()
Expand Down Expand Up @@ -833,4 +871,21 @@ export class PopupClient extends StandardInteractionClient {
const homeAccountId = request.account && request.account.homeAccountId;
return `${BrowserConstants.POPUP_NAME_PREFIX}.${this.config.auth.clientId}.${homeAccountId}.${this.correlationId}`;
}

/**
* Extracts the server response from the popup window
*/
extractServerResponseStringFromPopup(
popupWindow: Window,
href: string
): string {
let serverResponseString;
if(this.config.auth.OIDCOptions?.serverResponseType === ServerResponseType.QUERY) {
serverResponseString = UrlString.parseQueryServerResponse(href);
}
else {
serverResponseString = popupWindow.location.hash;
}
return serverResponseString;
}
}
20 changes: 19 additions & 1 deletion lib/msal-browser/src/interaction_client/RedirectClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
Logger,
IPerformanceClient,
PerformanceEvents,
ProtocolMode,
} from "@azure/msal-common";
import { StandardInteractionClient } from "./StandardInteractionClient";
import {
Expand Down Expand Up @@ -195,7 +196,6 @@ export class RedirectClient extends StandardInteractionClient {
);
return null;
}

const responseHash = this.getRedirectResponseHash(
hash || window.location.hash
);
Expand Down Expand Up @@ -507,6 +507,24 @@ export class RedirectClient extends StandardInteractionClient {
);
this.logger.verbose("Auth code client created");

if(authClient.authority.protocolMode === ProtocolMode.OIDC) {
try {
authClient.authority.endSessionEndpoint;
} catch {
if (validLogoutRequest.account?.homeAccountId){
this.browserStorage.removeAccount(validLogoutRequest.account?.homeAccountId);

this.eventHandler.emitEvent(
EventType.LOGOUT_SUCCESS,
InteractionType.Redirect,
validLogoutRequest
);

return;
}
}
}

// Create logout string and navigate user window to logout.
const logoutUri: string =
authClient.getLogoutUri(validLogoutRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export abstract class StandardInteractionClient extends BaseInteractionClient {
);
const authorityOptions: AuthorityOptions = {
protocolMode: this.config.auth.protocolMode,
OIDCOptions: this.config.auth.OIDCOptions,
knownAuthorities: this.config.auth.knownAuthorities,
cloudDiscoveryMetadata: this.config.auth.cloudDiscoveryMetadata,
authorityMetadata: this.config.auth.authorityMetadata,
Expand Down Expand Up @@ -437,12 +438,13 @@ export abstract class StandardInteractionClient extends BaseInteractionClient {
PerformanceEvents.InitializeBaseRequest,
this.correlationId
);

const validatedRequest: AuthorizationUrlRequest = {
...(await this.initializeBaseRequest(request)),
redirectUri: redirectUri,
state: state,
nonce: request.nonce || this.browserCrypto.createNewGuid(),
responseMode: ResponseMode.FRAGMENT,
responseMode: this.config.auth.OIDCOptions.serverResponseType as ResponseMode,
};

const account =
Expand Down
Loading

0 comments on commit 3de9243

Please sign in to comment.