UNPKG

msal

Version:
842 lines 117 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { __assign, __awaiter, __generator } from "tslib"; import { AccessTokenKey } from "./cache/AccessTokenKey"; import { AccessTokenValue } from "./cache/AccessTokenValue"; import { ServerRequestParameters } from "./ServerRequestParameters"; import { AuthorityType } from "./authority/Authority"; import { ClientInfo } from "./ClientInfo"; import { IdToken } from "./IdToken"; import { AuthCache } from "./cache/AuthCache"; import { Account } from "./Account"; import { ScopeSet } from "./ScopeSet"; import { StringUtils } from "./utils/StringUtils"; import { WindowUtils } from "./utils/WindowUtils"; import { TokenUtils } from "./utils/TokenUtils"; import { TimeUtils } from "./utils/TimeUtils"; import { UrlUtils } from "./utils/UrlUtils"; import { RequestUtils } from "./utils/RequestUtils"; import { ResponseUtils } from "./utils/ResponseUtils"; import { AuthorityFactory } from "./authority/AuthorityFactory"; import { buildConfiguration } from "./Configuration"; import { ClientConfigurationError } from "./error/ClientConfigurationError"; import { AuthError } from "./error/AuthError"; import { ClientAuthError, ClientAuthErrorMessage } from "./error/ClientAuthError"; import { ServerError } from "./error/ServerError"; import { InteractionRequiredAuthError } from "./error/InteractionRequiredAuthError"; import { buildResponseStateOnly } from "./AuthResponse"; import TelemetryManager from "./telemetry/TelemetryManager"; import { API_EVENT_IDENTIFIER } from "./telemetry/ApiEvent"; import { Constants, ServerHashParamKeys, ResponseTypes, TemporaryCacheKeys, PersistentCacheKeys, ErrorCacheKeys, FramePrefix } from "./utils/Constants"; import { CryptoUtils } from "./utils/CryptoUtils"; import { TrustedAuthority } from "./authority/TrustedAuthority"; import { AuthCacheUtils } from "./utils/AuthCacheUtils"; // default authority var DEFAULT_AUTHORITY = "https://login.microsoftonline.com/common"; /** * UserAgentApplication class * * Object Instance that the developer can use to make loginXX OR acquireTokenXX functions */ var UserAgentApplication = /** @class */ (function () { /** * @constructor * Constructor for the UserAgentApplication used to instantiate the UserAgentApplication object * * Important attributes in the Configuration object for auth are: * - clientID: the application ID of your application. * You can obtain one by registering your application with our Application registration portal : https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview * - authority: the authority URL for your application. * * In Azure AD, authority is a URL indicating the Azure active directory that MSAL uses to obtain tokens. * It is of the form https://login.microsoftonline.com/&lt;Enter_the_Tenant_Info_Here&gt;. * If your application supports Accounts in one organizational directory, replace "Enter_the_Tenant_Info_Here" value with the Tenant Id or Tenant name (for example, contoso.microsoft.com). * If your application supports Accounts in any organizational directory, replace "Enter_the_Tenant_Info_Here" value with organizations. * If your application supports Accounts in any organizational directory and personal Microsoft accounts, replace "Enter_the_Tenant_Info_Here" value with common. * To restrict support to Personal Microsoft accounts only, replace "Enter_the_Tenant_Info_Here" value with consumers. * * * In Azure B2C, authority is of the form https://&lt;instance&gt;/tfp/&lt;tenant&gt;/&lt;policyName&gt;/ * * @param {@link (Configuration:type)} configuration object for the MSAL UserAgentApplication instance */ function UserAgentApplication(configuration) { // callbacks for token/error this.authResponseCallback = null; this.tokenReceivedCallback = null; this.errorReceivedCallback = null; // Set the Configuration this.config = buildConfiguration(configuration); this.logger = this.config.system.logger; this.clientId = this.config.auth.clientId; this.inCookie = this.config.cache.storeAuthStateInCookie; this.telemetryManager = this.getTelemetryManagerFromConfig(this.config.system.telemetry, this.clientId); TrustedAuthority.setTrustedAuthoritiesFromConfig(this.config.auth.validateAuthority, this.config.auth.knownAuthorities); AuthorityFactory.saveMetadataFromConfig(this.config.auth.authority, this.config.auth.authorityMetadata); // if no authority is passed, set the default: "https://login.microsoftonline.com/common" this.authority = this.config.auth.authority || DEFAULT_AUTHORITY; // cache keys msal - typescript throws an error if any value other than "localStorage" or "sessionStorage" is passed this.cacheStorage = new AuthCache(this.clientId, this.config.cache.cacheLocation, this.inCookie); // Initialize window handling code if (!window.activeRenewals) { window.activeRenewals = {}; } if (!window.renewStates) { window.renewStates = []; } if (!window.callbackMappedToRenewStates) { window.callbackMappedToRenewStates = {}; } if (!window.promiseMappedToRenewStates) { window.promiseMappedToRenewStates = {}; } window.msal = this; var urlHash = window.location.hash; var urlContainsHash = UrlUtils.urlContainsHash(urlHash); // check if back button is pressed WindowUtils.checkIfBackButtonIsPressed(this.cacheStorage); // On the server 302 - Redirect, handle this if (urlContainsHash && this.cacheStorage.isInteractionInProgress(true)) { var stateInfo = this.getResponseState(urlHash); if (stateInfo.method === Constants.interactionTypeRedirect) { this.handleRedirectAuthenticationResponse(urlHash); } } } Object.defineProperty(UserAgentApplication.prototype, "authority", { /** * Method to manage the authority URL. * * @returns {string} authority */ get: function () { return this.authorityInstance.CanonicalAuthority; }, /** * setter for the authority URL * @param {string} authority */ // If the developer passes an authority, create an instance set: function (val) { this.authorityInstance = AuthorityFactory.CreateInstance(val, this.config.auth.validateAuthority); }, enumerable: false, configurable: true }); /** * Get the current authority instance from the MSAL configuration object * * @returns {@link Authority} authority instance */ UserAgentApplication.prototype.getAuthorityInstance = function () { return this.authorityInstance; }; UserAgentApplication.prototype.handleRedirectCallback = function (authOrTokenCallback, errorReceivedCallback) { if (!authOrTokenCallback) { throw ClientConfigurationError.createInvalidCallbackObjectError(authOrTokenCallback); } // Set callbacks if (errorReceivedCallback) { this.tokenReceivedCallback = authOrTokenCallback; this.errorReceivedCallback = errorReceivedCallback; this.logger.warning("This overload for callback is deprecated - please change the format of the callbacks to a single callback as shown: (err: AuthError, response: AuthResponse)."); } else { this.authResponseCallback = authOrTokenCallback; } if (this.redirectError) { this.authErrorHandler(Constants.interactionTypeRedirect, this.redirectError, this.redirectResponse); } else if (this.redirectResponse) { this.authResponseHandler(Constants.interactionTypeRedirect, this.redirectResponse); } }; /** * Public API to verify if the URL contains the hash with known properties * @param hash */ UserAgentApplication.prototype.urlContainsHash = function (hash) { this.logger.verbose("UrlContainsHash has been called"); return UrlUtils.urlContainsHash(hash); }; UserAgentApplication.prototype.authResponseHandler = function (interactionType, response, resolve) { this.logger.verbose("AuthResponseHandler has been called"); this.cacheStorage.setInteractionInProgress(false); if (interactionType === Constants.interactionTypeRedirect) { this.logger.verbose("Interaction type is redirect"); if (this.errorReceivedCallback) { this.logger.verbose("Two callbacks were provided to handleRedirectCallback, calling success callback with response"); this.tokenReceivedCallback(response); } else if (this.authResponseCallback) { this.logger.verbose("One callback was provided to handleRedirectCallback, calling authResponseCallback with response"); this.authResponseCallback(null, response); } } else if (interactionType === Constants.interactionTypePopup) { this.logger.verbose("Interaction type is popup, resolving"); resolve(response); } else { throw ClientAuthError.createInvalidInteractionTypeError(); } }; UserAgentApplication.prototype.authErrorHandler = function (interactionType, authErr, response, reject) { this.logger.verbose("AuthErrorHandler has been called"); // set interaction_status to complete this.cacheStorage.setInteractionInProgress(false); if (interactionType === Constants.interactionTypeRedirect) { this.logger.verbose("Interaction type is redirect"); if (this.errorReceivedCallback) { this.logger.verbose("Two callbacks were provided to handleRedirectCallback, calling error callback"); this.errorReceivedCallback(authErr, response.accountState); } else if (this.authResponseCallback) { this.logger.verbose("One callback was provided to handleRedirectCallback, calling authResponseCallback with error"); this.authResponseCallback(authErr, response); } else { this.logger.verbose("handleRedirectCallback has not been called and no callbacks are registered, throwing error"); throw authErr; } } else if (interactionType === Constants.interactionTypePopup) { this.logger.verbose("Interaction type is popup, rejecting"); reject(authErr); } else { throw ClientAuthError.createInvalidInteractionTypeError(); } }; // #endregion /** * Use when initiating the login process by redirecting the user's browser to the authorization endpoint. * @param {@link (AuthenticationParameters:type)} */ UserAgentApplication.prototype.loginRedirect = function (userRequest) { this.logger.verbose("LoginRedirect has been called"); // validate request var request = RequestUtils.validateRequest(userRequest, true, this.clientId, Constants.interactionTypeRedirect); this.acquireTokenInteractive(Constants.interactionTypeRedirect, true, request, null, null); }; /** * Use when you want to obtain an access_token for your API by redirecting the user's browser window to the authorization endpoint. * @param {@link (AuthenticationParameters:type)} * * To renew idToken, please pass clientId as the only scope in the Authentication Parameters */ UserAgentApplication.prototype.acquireTokenRedirect = function (userRequest) { this.logger.verbose("AcquireTokenRedirect has been called"); // validate request var request = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypeRedirect); this.acquireTokenInteractive(Constants.interactionTypeRedirect, false, request, null, null); }; /** * Use when initiating the login process via opening a popup window in the user's browser * * @param {@link (AuthenticationParameters:type)} * * @returns {Promise.<AuthResponse>} - a promise that is fulfilled when this function has completed, or rejected if an error was raised. Returns the {@link AuthResponse} object */ UserAgentApplication.prototype.loginPopup = function (userRequest) { var _this = this; this.logger.verbose("LoginPopup has been called"); // validate request var request = RequestUtils.validateRequest(userRequest, true, this.clientId, Constants.interactionTypePopup); var apiEvent = this.telemetryManager.createAndStartApiEvent(request.correlationId, API_EVENT_IDENTIFIER.LoginPopup); return new Promise(function (resolve, reject) { _this.acquireTokenInteractive(Constants.interactionTypePopup, true, request, resolve, reject); }) .then(function (resp) { _this.logger.verbose("Successfully logged in"); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, true); return resp; }) .catch(function (error) { _this.cacheStorage.resetTempCacheItems(request.state); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, false, error.errorCode); throw error; }); }; /** * Use when you want to obtain an access_token for your API via opening a popup window in the user's browser * @param {@link AuthenticationParameters} * * To renew idToken, please pass clientId as the only scope in the Authentication Parameters * @returns {Promise.<AuthResponse>} - a promise that is fulfilled when this function has completed, or rejected if an error was raised. Returns the {@link AuthResponse} object */ UserAgentApplication.prototype.acquireTokenPopup = function (userRequest) { var _this = this; this.logger.verbose("AcquireTokenPopup has been called"); // validate request var request = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypePopup); var apiEvent = this.telemetryManager.createAndStartApiEvent(request.correlationId, API_EVENT_IDENTIFIER.AcquireTokenPopup); return new Promise(function (resolve, reject) { _this.acquireTokenInteractive(Constants.interactionTypePopup, false, request, resolve, reject); }) .then(function (resp) { _this.logger.verbose("Successfully acquired token"); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, true); return resp; }) .catch(function (error) { _this.cacheStorage.resetTempCacheItems(request.state); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, false, error.errorCode); throw error; }); }; // #region Acquire Token /** * Use when initiating the login process or when you want to obtain an access_token for your API, * either by redirecting the user's browser window to the authorization endpoint or via opening a popup window in the user's browser. * @param {@link (AuthenticationParameters:type)} * * To renew idToken, please pass clientId as the only scope in the Authentication Parameters */ UserAgentApplication.prototype.acquireTokenInteractive = function (interactionType, isLoginCall, request, resolve, reject) { var _this = this; this.logger.verbose("AcquireTokenInteractive has been called"); // block the request if made from the hidden iframe WindowUtils.blockReloadInHiddenIframes(); try { this.cacheStorage.setInteractionInProgress(true); } catch (e) { // If already in progress, do not proceed var thrownError = isLoginCall ? ClientAuthError.createLoginInProgressError() : ClientAuthError.createAcquireTokenInProgressError(); var stateOnlyResponse = buildResponseStateOnly(this.getAccountState(request.state)); this.cacheStorage.resetTempCacheItems(request.state); this.authErrorHandler(interactionType, thrownError, stateOnlyResponse, reject); return; } if (interactionType === Constants.interactionTypeRedirect) { this.cacheStorage.setItem(TemporaryCacheKeys.REDIRECT_REQUEST, "" + Constants.inProgress + Constants.resourceDelimiter + request.state); } // Get the account object if a session exists var account; if (request && request.account && !isLoginCall) { account = request.account; this.logger.verbose("Account set from request"); } else { account = this.getAccount(); this.logger.verbose("Account set from MSAL Cache"); } // If no session exists, prompt the user to login. if (!account && !ServerRequestParameters.isSSOParam(request)) { if (isLoginCall) { // extract ADAL id_token if exists var adalIdToken = this.extractADALIdToken(); // silent login if ADAL id_token is retrieved successfully - SSO if (adalIdToken && !request.scopes) { this.logger.info("ADAL's idToken exists. Extracting login information from ADAL's idToken"); var tokenRequest = this.buildIDTokenRequest(request); this.silentLogin = true; this.acquireTokenSilent(tokenRequest).then(function (response) { _this.silentLogin = false; _this.logger.info("Unified cache call is successful"); _this.authResponseHandler(interactionType, response, resolve); return; }, function (error) { _this.silentLogin = false; _this.logger.error("Error occurred during unified cache ATS: " + error); // proceed to login since ATS failed _this.acquireTokenHelper(null, interactionType, isLoginCall, request, resolve, reject); }); } // No ADAL token found, proceed to login else { this.logger.verbose("Login call but no token found, proceed to login"); this.acquireTokenHelper(null, interactionType, isLoginCall, request, resolve, reject); } } // AcquireToken call, but no account or context given, so throw error else { this.logger.verbose("AcquireToken call, no context or account given"); this.logger.info("User login is required"); var stateOnlyResponse = buildResponseStateOnly(this.getAccountState(request.state)); this.cacheStorage.resetTempCacheItems(request.state); this.authErrorHandler(interactionType, ClientAuthError.createUserLoginRequiredError(), stateOnlyResponse, reject); return; } } // User session exists else { this.logger.verbose("User session exists, login not required"); this.acquireTokenHelper(account, interactionType, isLoginCall, request, resolve, reject); } }; /** * @hidden * @ignore * Helper function to acquireToken * */ UserAgentApplication.prototype.acquireTokenHelper = function (account, interactionType, isLoginCall, request, resolve, reject) { return __awaiter(this, void 0, void 0, function () { var requestSignature, serverAuthenticationRequest, acquireTokenAuthority, popUpWindow, responseType, loginStartPage, urlNavigate, hash, error_1, navigate, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: this.logger.verbose("AcquireTokenHelper has been called"); this.logger.verbose("Interaction type: " + interactionType + ". isLoginCall: " + isLoginCall); requestSignature = request.scopes ? request.scopes.join(" ").toLowerCase() : Constants.oidcScopes.join(" "); this.logger.verbosePii("Request signature: " + requestSignature); acquireTokenAuthority = (request && request.authority) ? AuthorityFactory.CreateInstance(request.authority, this.config.auth.validateAuthority, request.authorityMetadata) : this.authorityInstance; _a.label = 1; case 1: _a.trys.push([1, 11, , 12]); if (!!acquireTokenAuthority.hasCachedMetadata()) return [3 /*break*/, 3]; this.logger.verbose("No cached metadata for authority"); return [4 /*yield*/, AuthorityFactory.saveMetadataFromNetwork(acquireTokenAuthority, this.telemetryManager, request.correlationId)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: this.logger.verbose("Cached metadata found for authority"); _a.label = 4; case 4: responseType = isLoginCall ? ResponseTypes.id_token : this.getTokenType(account, request.scopes); loginStartPage = request.redirectStartPage || window.location.href; serverAuthenticationRequest = new ServerRequestParameters(acquireTokenAuthority, this.clientId, responseType, this.getRedirectUri(request && request.redirectUri), request.scopes, request.state, request.correlationId); this.logger.verbose("Finished building server authentication request"); this.updateCacheEntries(serverAuthenticationRequest, account, isLoginCall, loginStartPage); this.logger.verbose("Updating cache entries"); // populate QueryParameters (sid/login_hint) and any other extraQueryParameters set by the developer serverAuthenticationRequest.populateQueryParams(account, request); this.logger.verbose("Query parameters populated from account"); urlNavigate = UrlUtils.createNavigateUrl(serverAuthenticationRequest) + Constants.response_mode_fragment; // set state in cache if (interactionType === Constants.interactionTypeRedirect) { if (!isLoginCall) { this.cacheStorage.setItem(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.STATE_ACQ_TOKEN, request.state), serverAuthenticationRequest.state, this.inCookie); this.logger.verbose("State cached for redirect"); this.logger.verbosePii("State cached: " + serverAuthenticationRequest.state); } else { this.logger.verbose("Interaction type redirect but login call is true. State not cached"); } } else if (interactionType === Constants.interactionTypePopup) { window.renewStates.push(serverAuthenticationRequest.state); window.requestType = isLoginCall ? Constants.login : Constants.renewToken; this.logger.verbose("State saved to window"); this.logger.verbosePii("State saved: " + serverAuthenticationRequest.state); // Register callback to capture results from server this.registerCallback(serverAuthenticationRequest.state, requestSignature, resolve, reject); } else { this.logger.verbose("Invalid interaction error. State not cached"); throw ClientAuthError.createInvalidInteractionTypeError(); } if (!(interactionType === Constants.interactionTypePopup)) return [3 /*break*/, 9]; this.logger.verbose("Interaction type is popup. Generating popup window"); // Generate a popup window try { popUpWindow = this.openPopup(urlNavigate, "msal", Constants.popUpWidth, Constants.popUpHeight); // Push popup window handle onto stack for tracking WindowUtils.trackPopup(popUpWindow); } catch (e) { this.logger.info(ClientAuthErrorMessage.popUpWindowError.code + ":" + ClientAuthErrorMessage.popUpWindowError.desc); this.cacheStorage.setItem(ErrorCacheKeys.ERROR, ClientAuthErrorMessage.popUpWindowError.code); this.cacheStorage.setItem(ErrorCacheKeys.ERROR_DESC, ClientAuthErrorMessage.popUpWindowError.desc); if (reject) { reject(ClientAuthError.createPopupWindowError()); return [2 /*return*/]; } } if (!popUpWindow) return [3 /*break*/, 8]; _a.label = 5; case 5: _a.trys.push([5, 7, , 8]); return [4 /*yield*/, WindowUtils.monitorPopupForHash(popUpWindow, this.config.system.loadFrameTimeout, urlNavigate, this.logger)]; case 6: hash = _a.sent(); this.handleAuthenticationResponse(hash); // Request completed successfully, set to completed this.cacheStorage.setInteractionInProgress(false); this.logger.info("Closing popup window"); // TODO: Check how this can be extracted for any framework specific code? if (this.config.framework.isAngular) { this.broadcast("msal:popUpHashChanged", hash); } WindowUtils.closePopups(); return [3 /*break*/, 8]; case 7: error_1 = _a.sent(); if (reject) { reject(error_1); } if (this.config.framework.isAngular) { this.broadcast("msal:popUpClosed", error_1.errorCode + Constants.resourceDelimiter + error_1.errorMessage); } else { // Request failed, set to canceled this.cacheStorage.setInteractionInProgress(false); popUpWindow.close(); } return [3 /*break*/, 8]; case 8: return [3 /*break*/, 10]; case 9: // If onRedirectNavigate is implemented, invoke it and provide urlNavigate if (request.onRedirectNavigate) { this.logger.verbose("Invoking onRedirectNavigate callback"); navigate = request.onRedirectNavigate(urlNavigate); // Returning false from onRedirectNavigate will stop navigation if (navigate !== false) { this.logger.verbose("onRedirectNavigate did not return false, navigating"); this.navigateWindow(urlNavigate); } else { this.logger.verbose("onRedirectNavigate returned false, stopping navigation"); } } else { // Otherwise, perform navigation this.logger.verbose("Navigating window to urlNavigate"); this.navigateWindow(urlNavigate); } _a.label = 10; case 10: return [3 /*break*/, 12]; case 11: err_1 = _a.sent(); this.logger.error(err_1); this.cacheStorage.resetTempCacheItems(request.state); this.authErrorHandler(interactionType, ClientAuthError.createEndpointResolutionError(err_1.toString), buildResponseStateOnly(request.state), reject); if (popUpWindow) { popUpWindow.close(); } return [3 /*break*/, 12]; case 12: return [2 /*return*/]; } }); }); }; /** * API interfacing idToken request when applications already have a session/hint acquired by authorization client applications * @param request */ UserAgentApplication.prototype.ssoSilent = function (request) { this.logger.verbose("ssoSilent has been called"); // throw an error on an empty request if (!request) { throw ClientConfigurationError.createEmptyRequestError(); } // throw an error on no hints passed if (!request.sid && !request.loginHint) { throw ClientConfigurationError.createSsoSilentError(); } return this.acquireTokenSilent(__assign(__assign({}, request), { scopes: Constants.oidcScopes })); }; /** * Use this function to obtain a token before every call to the API / resource provider * * MSAL return's a cached token when available * Or it send's a request to the STS to obtain a new token using a hidden iframe. * * @param {@link AuthenticationParameters} * * To renew idToken, please pass clientId as the only scope in the Authentication Parameters * @returns {Promise.<AuthResponse>} - a promise that is fulfilled when this function has completed, or rejected if an error was raised. Returns the {@link AuthResponse} object * */ UserAgentApplication.prototype.acquireTokenSilent = function (userRequest) { var _this = this; this.logger.verbose("AcquireTokenSilent has been called"); // validate the request var request = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypeSilent); var apiEvent = this.telemetryManager.createAndStartApiEvent(request.correlationId, API_EVENT_IDENTIFIER.AcquireTokenSilent); var requestSignature = RequestUtils.createRequestSignature(request); return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { var scope, account, adalIdToken, responseType, serverAuthenticationRequest, adalIdTokenObject, userContainedClaims, authErr, cacheResultResponse, logMessage, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: // block the request if made from the hidden iframe WindowUtils.blockReloadInHiddenIframes(); scope = request.scopes.join(" ").toLowerCase(); this.logger.verbosePii("Serialized scopes: " + scope); if (request.account) { account = request.account; this.logger.verbose("Account set from request"); } else { account = this.getAccount(); this.logger.verbose("Account set from MSAL Cache"); } adalIdToken = this.cacheStorage.getItem(Constants.adalIdToken); // In the event of no account being passed in the config, no session id, and no pre-existing adalIdToken, user will need to log in if (!account && !(request.sid || request.loginHint) && StringUtils.isEmpty(adalIdToken)) { this.logger.info("User login is required"); // The promise rejects with a UserLoginRequiredError, which should be caught and user should be prompted to log in interactively return [2 /*return*/, reject(ClientAuthError.createUserLoginRequiredError())]; } responseType = this.getTokenType(account, request.scopes); this.logger.verbose("Response type: " + responseType); serverAuthenticationRequest = new ServerRequestParameters(AuthorityFactory.CreateInstance(request.authority, this.config.auth.validateAuthority, request.authorityMetadata), this.clientId, responseType, this.getRedirectUri(request.redirectUri), request.scopes, request.state, request.correlationId); this.logger.verbose("Finished building server authentication request"); // populate QueryParameters (sid/login_hint) and any other extraQueryParameters set by the developer if (ServerRequestParameters.isSSOParam(request) || account) { serverAuthenticationRequest.populateQueryParams(account, request, null, true); this.logger.verbose("Query parameters populated from existing SSO or account"); } // if user didn't pass login_hint/sid and adal's idtoken is present, extract the login_hint from the adalIdToken else if (!account && !StringUtils.isEmpty(adalIdToken)) { adalIdTokenObject = TokenUtils.extractIdToken(adalIdToken); this.logger.verbose("ADAL's idToken exists. Extracting login information from ADAL's idToken to populate query parameters"); serverAuthenticationRequest.populateQueryParams(account, null, adalIdTokenObject, true); } else { this.logger.verbose("No additional query parameters added"); } userContainedClaims = request.claimsRequest || serverAuthenticationRequest.claimsValue; // If request.forceRefresh is set to true, force a request for a new token instead of getting it from the cache if (!userContainedClaims && !request.forceRefresh) { try { cacheResultResponse = this.getCachedToken(serverAuthenticationRequest, account); } catch (e) { authErr = e; } } if (!cacheResultResponse) return [3 /*break*/, 1]; this.logger.verbose("Token found in cache lookup"); this.logger.verbosePii("Scopes found: " + JSON.stringify(cacheResultResponse.scopes)); resolve(cacheResultResponse); return [2 /*return*/, null]; case 1: if (!authErr) return [3 /*break*/, 2]; this.logger.infoPii(authErr.errorCode + ":" + authErr.errorMessage); reject(authErr); return [2 /*return*/, null]; case 2: logMessage = void 0; if (userContainedClaims) { logMessage = "Skipped cache lookup since claims were given"; } else if (request.forceRefresh) { logMessage = "Skipped cache lookup since request.forceRefresh option was set to true"; } else { logMessage = "No valid token found in cache lookup"; } this.logger.verbose(logMessage); // Cache result can return null if cache is empty. In that case, set authority to default value if no authority is passed to the API. if (!serverAuthenticationRequest.authorityInstance) { serverAuthenticationRequest.authorityInstance = request.authority ? AuthorityFactory.CreateInstance(request.authority, this.config.auth.validateAuthority, request.authorityMetadata) : this.authorityInstance; } this.logger.verbosePii("Authority instance: " + serverAuthenticationRequest.authority); _a.label = 3; case 3: _a.trys.push([3, 7, , 8]); if (!!serverAuthenticationRequest.authorityInstance.hasCachedMetadata()) return [3 /*break*/, 5]; this.logger.verbose("No cached metadata for authority"); return [4 /*yield*/, AuthorityFactory.saveMetadataFromNetwork(serverAuthenticationRequest.authorityInstance, this.telemetryManager, request.correlationId)]; case 4: _a.sent(); this.logger.verbose("Authority has been updated with endpoint discovery response"); return [3 /*break*/, 6]; case 5: this.logger.verbose("Cached metadata found for authority"); _a.label = 6; case 6: /* * refresh attempt with iframe * Already renewing for this scope, callback when we get the token. */ if (window.activeRenewals[requestSignature]) { this.logger.verbose("Renewing token in progress. Registering callback"); // Active renewals contains the state for each renewal. this.registerCallback(window.activeRenewals[requestSignature], requestSignature, resolve, reject); } else { if (request.scopes && ScopeSet.onlyContainsOidcScopes(request.scopes)) { /* * App uses idToken to send to api endpoints * Default scope is tracked as OIDC scopes to store this token */ this.logger.verbose("OpenID Connect scopes only, renewing idToken"); this.silentLogin = true; this.renewIdToken(requestSignature, resolve, reject, account, serverAuthenticationRequest); } else { // renew access token this.logger.verbose("Renewing access token"); this.renewToken(requestSignature, resolve, reject, account, serverAuthenticationRequest); } } return [3 /*break*/, 8]; case 7: err_2 = _a.sent(); this.logger.error(err_2); reject(ClientAuthError.createEndpointResolutionError(err_2.toString())); return [2 /*return*/, null]; case 8: return [2 /*return*/]; } }); }); }) .then(function (res) { _this.logger.verbose("Successfully acquired token"); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, true); return res; }) .catch(function (error) { _this.cacheStorage.resetTempCacheItems(request.state); _this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, false, error.errorCode); throw error; }); }; // #endregion // #region Popup Window Creation /** * @hidden * * Configures popup window for login. * * @param urlNavigate * @param title * @param popUpWidth * @param popUpHeight * @ignore * @hidden */ UserAgentApplication.prototype.openPopup = function (urlNavigate, title, popUpWidth, popUpHeight) { this.logger.verbose("OpenPopup has been called"); try { /** * adding winLeft and winTop to account for dual monitor * using screenLeft and screenTop for IE8 and earlier */ var winLeft = window.screenLeft ? window.screenLeft : window.screenX; var winTop = window.screenTop ? window.screenTop : window.screenY; /** * window.innerWidth displays browser window"s height and width excluding toolbars * using document.documentElement.clientWidth for IE8 and earlier */ var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; var left = ((width / 2) - (popUpWidth / 2)) + winLeft; var top_1 = ((height / 2) - (popUpHeight / 2)) + winTop; // open the window var popupWindow = window.open(urlNavigate, title, "width=" + popUpWidth + ", height=" + popUpHeight + ", top=" + top_1 + ", left=" + left + ", scrollbars=yes"); if (!popupWindow) { throw ClientAuthError.createPopupWindowError(); } if (popupWindow.focus) { popupWindow.focus(); } return popupWindow; } catch (e) { this.cacheStorage.setInteractionInProgress(false); throw ClientAuthError.createPopupWindowError(e.toString()); } }; // #endregion // #region Iframe Management /** * @hidden * Calling _loadFrame but with a timeout to signal failure in loadframeStatus. Callbacks are left. * registered when network errors occur and subsequent token requests for same resource are registered to the pending request. * @ignore */ UserAgentApplication.prototype.loadIframeTimeout = function (urlNavigate, frameName, requestSignature) { return __awaiter(this, void 0, void 0, function () { var expectedState, iframe, _a, hash, error_2; return __generator(this, function (_b) { switch (_b.label) { case 0: expectedState = window.activeRenewals[requestSignature]; this.logger.verbosePii("Set loading state to pending for: " + requestSignature + ":" + expectedState); this.cacheStorage.setItem(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.RENEW_STATUS, expectedState), Constants.inProgress); if (!this.config.system.navigateFrameWait) return [3 /*break*/, 2]; return [4 /*yield*/, WindowUtils.loadFrame(urlNavigate, frameName, this.config.system.navigateFrameWait, this.logger)]; case 1: _a = _b.sent(); return [3 /*break*/, 3]; case 2: _a = WindowUtils.loadFrameSync(urlNavigate, frameName, this.logger); _b.label = 3; case 3: iframe = _a; _b.label = 4; case 4: _b.trys.push([4, 6, , 7]); return [4 /*yield*/, WindowUtils.monitorIframeForHash(iframe.contentWindow, this.config.system.loadFrameTimeout, urlNavigate, this.logger)]; case 5: hash = _b.sent(); if (hash) { this.handleAuthenticationResponse(hash); } return [3 /*break*/, 7]; case 6: error_2 = _b.sent(); if (this.cacheStorage.getItem(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.RENEW_STATUS, expectedState)) === Constants.inProgress) { // fail the iframe session if it's in pending state this.logger.verbose("Loading frame has timed out after: " + (this.config.system.loadFrameTimeout / 1000) + " seconds for scope/authority " + requestSignature + ":" + expectedState); // Error after timeout if (expectedState && window.callbackMappedToRenewStates[expectedState]) { window.callbackMappedToRenewStates[expectedState](null, error_2); } this.cacheStorage.removeItem(AuthCache.generateTemporaryCacheKey(TemporaryCacheKeys.RENEW_STATUS, expectedState)); } WindowUtils.removeHiddenIframe(iframe); throw error_2; case 7: WindowUtils.removeHiddenIframe(iframe); return [2 /*return*/]; } }); }); }; // #endregion // #region General Helpers /** * @hidden * Used to redirect the browser to the STS authorization endpoint * @param {string} urlNavigate - URL of the authorization endpoint */ UserAgentApplication.prototype.navigateWindow = function (urlNavigate, popupWindow) { // Navigate if valid URL if (urlNavigate && !StringUtils.isEmpty(urlNavigate)) { var navigateWindow = popupWindow ? popupWindow : window; var logMessage = popupWindow ? "Navigated Popup window to:" + urlNavigate : "Navigate to:" + urlNavigate; this.logger.infoPii(logMessage); navigateWindow.location.assign(urlNavigate); } else { this.logger.info("Navigate url is empty"); throw AuthError.createUnexpectedError("Navigate url is empty"); } }; /** * @hidden * Used to add the developer requested callback to the array of callbacks for the specified scopes. The updated array is stored on the window object * @param {string} expectedState - Unique state identifier (guid). * @param {string} scope - Developer requested permissions. Not all scopes are guaranteed to be included in the access token returned. * @param {Function} resolve - The resolve function of the promise object. * @param {Function} reject - The reject function of the promise object. * @ignore */ UserAgentApplication.prototype.registerCallback = function (expectedState, requestSignature, resolve, reject) { var _this = this; // track active renewals window.activeRenewals[requestSignature] = expectedState; // initialize callbacks mapped array if (!window.promiseMappedToRenewStates[expectedState]) { window.promiseMappedToRenewStates[expectedState] = []; } // indexing on the current state, push the callback params to callbacks mapped window.promiseMappedToRenewStates[expectedState].push({ resolve: resolve, reject: reject }); // Store the server response in the current window?? if (!window.callbackMappedToRenewStates[expectedState]) { window.callbackMappedToRenewStates[expectedState] = function (response, error) { // reset active renewals delete window.activeRenewals[requestSignature]; // for all promiseMappedtoRenewStates for a given 'state' - call the reject/resolve with error/token respectively for (var i = 0; i < window.promiseMappedToRenewStates[expectedSta