@azure/msal-browser
Version:
Microsoft Authentication Library for js
258 lines (241 loc) • 9.42 kB
text/typescript
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import {
ICrypto,
INetworkModule,
Logger,
AccountInfo,
AccountEntity,
UrlString,
ServerTelemetryManager,
ServerTelemetryRequest,
createClientConfigurationError,
ClientConfigurationErrorCodes,
Authority,
AuthorityOptions,
AuthorityFactory,
IPerformanceClient,
PerformanceEvents,
AzureCloudOptions,
invokeAsync,
StringDict,
} from "@azure/msal-common/browser";
import { BrowserConfiguration } from "../config/Configuration.js";
import { BrowserCacheManager } from "../cache/BrowserCacheManager.js";
import { EventHandler } from "../event/EventHandler.js";
import { EndSessionRequest } from "../request/EndSessionRequest.js";
import { RedirectRequest } from "../request/RedirectRequest.js";
import { PopupRequest } from "../request/PopupRequest.js";
import { SsoSilentRequest } from "../request/SsoSilentRequest.js";
import { version } from "../packageMetadata.js";
import { BrowserConstants } from "../utils/BrowserConstants.js";
import * as BrowserUtils from "../utils/BrowserUtils.js";
import { INavigationClient } from "../navigation/INavigationClient.js";
import { NativeMessageHandler } from "../broker/nativeBroker/NativeMessageHandler.js";
import { AuthenticationResult } from "../response/AuthenticationResult.js";
import { ClearCacheRequest } from "../request/ClearCacheRequest.js";
import { createNewGuid } from "../crypto/BrowserCrypto.js";
export abstract class BaseInteractionClient {
protected config: BrowserConfiguration;
protected browserStorage: BrowserCacheManager;
protected browserCrypto: ICrypto;
protected networkClient: INetworkModule;
protected logger: Logger;
protected eventHandler: EventHandler;
protected navigationClient: INavigationClient;
protected nativeMessageHandler: NativeMessageHandler | undefined;
protected correlationId: string;
protected performanceClient: IPerformanceClient;
constructor(
config: BrowserConfiguration,
storageImpl: BrowserCacheManager,
browserCrypto: ICrypto,
logger: Logger,
eventHandler: EventHandler,
navigationClient: INavigationClient,
performanceClient: IPerformanceClient,
nativeMessageHandler?: NativeMessageHandler,
correlationId?: string
) {
this.config = config;
this.browserStorage = storageImpl;
this.browserCrypto = browserCrypto;
this.networkClient = this.config.system.networkClient;
this.eventHandler = eventHandler;
this.navigationClient = navigationClient;
this.nativeMessageHandler = nativeMessageHandler;
this.correlationId = correlationId || createNewGuid();
this.logger = logger.clone(
BrowserConstants.MSAL_SKU,
version,
this.correlationId
);
this.performanceClient = performanceClient;
}
abstract acquireToken(
request: RedirectRequest | PopupRequest | SsoSilentRequest
): Promise<AuthenticationResult | void>;
abstract logout(
request: EndSessionRequest | ClearCacheRequest | undefined
): Promise<void>;
protected async clearCacheOnLogout(
account?: AccountInfo | null
): Promise<void> {
if (account) {
if (
AccountEntity.accountInfoIsEqual(
account,
this.browserStorage.getActiveAccount(),
false
)
) {
this.logger.verbose("Setting active account to null");
this.browserStorage.setActiveAccount(null);
}
// Clear given account.
try {
await this.browserStorage.removeAccount(
AccountEntity.generateAccountCacheKey(account)
);
this.logger.verbose(
"Cleared cache items belonging to the account provided in the logout request."
);
} catch (error) {
this.logger.error(
"Account provided in logout request was not found. Local cache unchanged."
);
}
} else {
try {
this.logger.verbose(
"No account provided in logout request, clearing all cache items.",
this.correlationId
);
// Clear all accounts and tokens
await this.browserStorage.clear();
// Clear any stray keys from IndexedDB
await this.browserCrypto.clearKeystore();
} catch (e) {
this.logger.error(
"Attempted to clear all MSAL cache items and failed. Local cache unchanged."
);
}
}
}
/**
*
* Use to get the redirect uri configured in MSAL or null.
* @param requestRedirectUri
* @returns Redirect URL
*
*/
getRedirectUri(requestRedirectUri?: string): string {
this.logger.verbose("getRedirectUri called");
const redirectUri = requestRedirectUri || this.config.auth.redirectUri;
return UrlString.getAbsoluteUrl(
redirectUri,
BrowserUtils.getCurrentUri()
);
}
/**
*
* @param apiId
* @param correlationId
* @param forceRefresh
*/
protected initializeServerTelemetryManager(
apiId: number,
forceRefresh?: boolean
): ServerTelemetryManager {
this.logger.verbose("initializeServerTelemetryManager called");
const telemetryPayload: ServerTelemetryRequest = {
clientId: this.config.auth.clientId,
correlationId: this.correlationId,
apiId: apiId,
forceRefresh: forceRefresh || false,
wrapperSKU: this.browserStorage.getWrapperMetadata()[0],
wrapperVer: this.browserStorage.getWrapperMetadata()[1],
};
return new ServerTelemetryManager(
telemetryPayload,
this.browserStorage
);
}
/**
* Used to get a discovered version of the default authority.
* @param params {
* requestAuthority?: string;
* requestAzureCloudOptions?: AzureCloudOptions;
* requestExtraQueryParameters?: StringDict;
* account?: AccountInfo;
* }
*/
protected async getDiscoveredAuthority(params: {
requestAuthority?: string;
requestAzureCloudOptions?: AzureCloudOptions;
requestExtraQueryParameters?: StringDict;
account?: AccountInfo;
}): Promise<Authority> {
const { account } = params;
const instanceAwareEQ =
params.requestExtraQueryParameters &&
params.requestExtraQueryParameters.hasOwnProperty("instance_aware")
? params.requestExtraQueryParameters["instance_aware"]
: undefined;
this.performanceClient.addQueueMeasurement(
PerformanceEvents.StandardInteractionClientGetDiscoveredAuthority,
this.correlationId
);
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,
skipAuthorityMetadataCache:
this.config.auth.skipAuthorityMetadataCache,
};
// build authority string based on auth params, precedence - azureCloudInstance + tenant >> authority
const resolvedAuthority =
params.requestAuthority || this.config.auth.authority;
const resolvedInstanceAware = instanceAwareEQ?.length
? instanceAwareEQ === "true"
: this.config.auth.instanceAware;
const userAuthority =
account && resolvedInstanceAware
? this.config.auth.authority.replace(
UrlString.getDomainFromUrl(resolvedAuthority),
account.environment
)
: resolvedAuthority;
// fall back to the authority from config
const builtAuthority = Authority.generateAuthority(
userAuthority,
params.requestAzureCloudOptions ||
this.config.auth.azureCloudOptions
);
const discoveredAuthority = await invokeAsync(
AuthorityFactory.createDiscoveredInstance,
PerformanceEvents.AuthorityFactoryCreateDiscoveredInstance,
this.logger,
this.performanceClient,
this.correlationId
)(
builtAuthority,
this.config.system.networkClient,
this.browserStorage,
authorityOptions,
this.logger,
this.correlationId,
this.performanceClient
);
if (account && !discoveredAuthority.isAlias(account.environment)) {
throw createClientConfigurationError(
ClientConfigurationErrorCodes.authorityMismatch
);
}
return discoveredAuthority;
}
}