diff --git a/lib/msal-core/README.md b/lib/msal-core/README.md index 336e2dc377..7eba0212cd 100644 --- a/lib/msal-core/README.md +++ b/lib/msal-core/README.md @@ -110,7 +110,7 @@ Before using MSAL.js you will need to [register an application in Azure AD](http `UserAgentApplication` can be configured with a variety of different options, detailed in our [Wiki](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL.js-1.0.0-api-release#configuration-options), but the only required parameter is `auth.clientId`. -After instantiating your instance, if you plan on using a redirect flow (`loginRedirect` and `acquireTokenRedirect`), you must register a callback handlers using `handleRedirectCallback(authCallback)` where `authCallback = function(AuthError, AuthResponse)`. The callback function is called after the authentication request is completed either successfully or with a failure. This is not required for the popup flows since they return promises. +After instantiating your instance, if you plan on using a redirect flow in MSAL 1.2.x or earlier (`loginRedirect` and `acquireTokenRedirect`), you must register a callback handler using `handleRedirectCallback(authCallback)` where `authCallback = function(AuthError, AuthResponse)`. As of MSAL 1.3.0 this is optional. The callback function is called after the authentication request is completed either successfully or with a failure. This is not required for the popup flows since they return promises. ```JavaScript import * as Msal from "msal"; diff --git a/lib/msal-core/src/Configuration.ts b/lib/msal-core/src/Configuration.ts index b1d1b0db67..605e1d0e5e 100644 --- a/lib/msal-core/src/Configuration.ts +++ b/lib/msal-core/src/Configuration.ts @@ -115,8 +115,8 @@ const DEFAULT_AUTH_OPTIONS: AuthOptions = { clientId: "", authority: null, validateAuthority: true, - redirectUri: () => UrlUtils.getDefaultRedirectUri(), - postLogoutRedirectUri: () => UrlUtils.getDefaultRedirectUri(), + redirectUri: () => UrlUtils.getCurrentUrl(), + postLogoutRedirectUri: () => UrlUtils.getCurrentUrl(), navigateToLoginRequestUrl: true }; diff --git a/lib/msal-core/src/UserAgentApplication.ts b/lib/msal-core/src/UserAgentApplication.ts index 83e16afad0..24b0eb8b17 100644 --- a/lib/msal-core/src/UserAgentApplication.ts +++ b/lib/msal-core/src/UserAgentApplication.ts @@ -150,7 +150,8 @@ export class UserAgentApplication { // state variables private silentAuthenticationState: string; private silentLogin: boolean; - private redirectCallbacksSet: boolean; + private redirectResponse: AuthResponse; + private redirectError: AuthError; // Authority Functionality protected authorityInstance: Authority; @@ -208,9 +209,6 @@ export class UserAgentApplication { // Set the Configuration this.config = buildConfiguration(configuration); - // Set the callback boolean - this.redirectCallbacksSet = false; - this.logger = this.config.system.logger; this.clientId = this.config.auth.clientId; this.inCookie = this.config.cache.storeAuthStateInCookie; @@ -238,7 +236,7 @@ export class UserAgentApplication { // On the server 302 - Redirect, handle this if (urlContainsHash && !WindowUtils.isInIframe() && !WindowUtils.isInPopup()) { - this.handleAuthenticationResponse(urlHash); + this.handleRedirectAuthenticationResponse(urlHash); } } @@ -255,7 +253,6 @@ export class UserAgentApplication { handleRedirectCallback(authCallback: authResponseCallback): void; handleRedirectCallback(authOrTokenCallback: authResponseCallback | tokenReceivedCallback, errorReceivedCallback?: errorReceivedCallback): void { if (!authOrTokenCallback) { - this.redirectCallbacksSet = false; throw ClientConfigurationError.createInvalidCallbackObjectError(authOrTokenCallback); } @@ -268,12 +265,10 @@ export class UserAgentApplication { this.authResponseCallback = authOrTokenCallback as authResponseCallback; } - this.redirectCallbacksSet = true; - - // On the server 302 - Redirect, handle this - const cachedHash = this.cacheStorage.getItem(TemporaryCacheKeys.URL_HASH); - if (cachedHash) { - this.processCallBack(cachedHash, null); + if (this.redirectError) { + this.authErrorHandler(Constants.interactionTypeRedirect, this.redirectError, this.redirectResponse); + } else if (this.redirectResponse) { + this.authResponseHandler(Constants.interactionTypeRedirect, this.redirectResponse); } } @@ -322,7 +317,7 @@ export class UserAgentApplication { */ loginRedirect(userRequest?: AuthenticationParameters): void { // validate request - const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, true, this.clientId, Constants.interactionTypeRedirect, this.redirectCallbacksSet); + const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, true, this.clientId); this.acquireTokenInteractive(Constants.interactionTypeRedirect, true, request, null, null); } @@ -334,7 +329,7 @@ export class UserAgentApplication { */ acquireTokenRedirect(userRequest: AuthenticationParameters): void { // validate request - const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypeRedirect, this.redirectCallbacksSet); + const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, false, this.clientId); this.acquireTokenInteractive(Constants.interactionTypeRedirect, false, request, null, null); } @@ -347,7 +342,7 @@ export class UserAgentApplication { */ loginPopup(userRequest?: AuthenticationParameters): Promise { // validate request - const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, true, this.clientId, Constants.interactionTypePopup); + const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, true, this.clientId); return new Promise((resolve, reject) => { this.acquireTokenInteractive(Constants.interactionTypePopup, true, request, resolve, reject); @@ -366,7 +361,7 @@ export class UserAgentApplication { */ acquireTokenPopup(userRequest: AuthenticationParameters): Promise { // validate request - const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypePopup); + const request: AuthenticationParameters = RequestUtils.validateRequest(userRequest, false, this.clientId); return new Promise((resolve, reject) => { this.acquireTokenInteractive(Constants.interactionTypePopup, false, request, resolve, reject); @@ -991,9 +986,6 @@ export class UserAgentApplication { authErr = err; } - // remove hash from the cache - this.cacheStorage.removeItem(TemporaryCacheKeys.URL_HASH); - try { // Clear the cookie in the hash this.cacheStorage.clearMsalCookie(stateInfo.state); @@ -1011,12 +1003,13 @@ export class UserAgentApplication { response.tokenType = ServerHashParamKeys.ID_TOKEN; } if (!parentCallback) { - this.authResponseHandler(Constants.interactionTypeRedirect, response); + this.redirectResponse = response; return; } } else if (!parentCallback) { + this.redirectResponse = buildResponseStateOnly(accountState); + this.redirectError = authErr; this.cacheStorage.resetTempCacheItems(stateInfo.state); - this.authErrorHandler(Constants.interactionTypeRedirect, authErr, buildResponseStateOnly(accountState)); return; } @@ -1029,64 +1022,56 @@ export class UserAgentApplication { /** * @hidden - * This method must be called for processing the response received from the STS. It extracts the hash, processes the token or error information and saves it in the cache. It then - * calls the registered callbacks in case of redirect or resolves the promises with the result. + * This method must be called for processing the response received from the STS if using popups or iframes. It extracts the hash, processes the token or error + * information and saves it in the cache. It then resolves the promises with the result. * @param {string} [hash=window.location.hash] - Hash fragment of Url. */ private handleAuthenticationResponse(hash: string): void { // retrieve the hash const locationHash = hash || window.location.hash; - // Check if the current flow is popup or hidden iframe - const iframeWithHash = WindowUtils.getIframeWithHash(locationHash); - const popUpWithHash = WindowUtils.getPopUpWithHash(locationHash); - const isPopupOrIframe = !!(iframeWithHash || popUpWithHash); - // if (window.parent !== window), by using self, window.parent becomes equal to window in getResponseState method specifically const stateInfo = this.getResponseState(locationHash); - let tokenResponseCallback: (response: AuthResponse, error: AuthError) => void = null; + const tokenResponseCallback = window.callbackMappedToRenewStates[stateInfo.state]; + this.processCallBack(locationHash, stateInfo, tokenResponseCallback); + // If current window is opener, close all windows + WindowUtils.closePopups(); + } + + /** + * @hidden + * This method must be called for processing the response received from the STS when using redirect flows. It extracts the hash, processes the token or error + * information and saves it in the cache. The result can then be accessed by user registered callbacks. + * @param {string} [hash=window.location.hash] - Hash fragment of Url. + */ + private handleRedirectAuthenticationResponse(hash: string): void { this.logger.info("Returned from redirect url"); - // If parent window is the msal instance which opened the current window (iframe) - if (isPopupOrIframe) { - tokenResponseCallback = window.callbackMappedToRenewStates[stateInfo.state]; - } else { - // Redirect cases - tokenResponseCallback = null; - // if set to navigate to loginRequest page post login - if (this.config.auth.navigateToLoginRequestUrl) { - this.cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, locationHash); - if (window.parent === window) { - const loginRequestUrl = this.cacheStorage.getItem(`${TemporaryCacheKeys.LOGIN_REQUEST}${Constants.resourceDelimiter}${stateInfo.state}`, this.inCookie); - - // Redirect to home page if login request url is null (real null or the string null) - if (!loginRequestUrl || loginRequestUrl === "null") { - this.logger.error("Unable to get valid login request url from cache, redirecting to home page"); - window.location.href = "/"; - } else { - window.location.href = loginRequestUrl; - } - } - return; - } - else { - window.location.hash = ""; - } + + // clear hash from window + window.location.hash = ""; + + // if (window.parent !== window), by using self, window.parent becomes equal to window in getResponseState method specifically + const stateInfo = this.getResponseState(hash); - if (!this.redirectCallbacksSet) { - // We reached this point too early - cache hash, return and process in handleRedirectCallbacks - this.cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, locationHash); + // if set to navigate to loginRequest page post login + if (this.config.auth.navigateToLoginRequestUrl && window.parent === window) { + const loginRequestUrl = this.cacheStorage.getItem(`${TemporaryCacheKeys.LOGIN_REQUEST}${Constants.resourceDelimiter}${stateInfo.state}`, this.inCookie); + const currentUrl = UrlUtils.getCurrentUrl(); + + // Redirect to home page if login request url is null (real null or the string null) + if (!loginRequestUrl || loginRequestUrl === "null") { + this.logger.error("Unable to get valid login request url from cache, redirecting to home page"); + window.location.href = "/"; + return; + } else if (currentUrl !== loginRequestUrl) { + window.location.href = `${loginRequestUrl}${hash}`; return; } } - this.processCallBack(locationHash, stateInfo, tokenResponseCallback); - - // If current window is opener, close all windows - if (isPopupOrIframe) { - WindowUtils.closePopups(); - } + this.processCallBack(hash, stateInfo, null); } /** @@ -1857,10 +1842,6 @@ export class UserAgentApplication { * @returns {boolean} true/false */ public getLoginInProgress(): boolean { - const pendingCallback = this.cacheStorage.getItem(TemporaryCacheKeys.URL_HASH); - if (pendingCallback) { - return true; - } return this.cacheStorage.getItem(TemporaryCacheKeys.INTERACTION_STATUS) === Constants.inProgress; } diff --git a/lib/msal-core/src/utils/RequestUtils.ts b/lib/msal-core/src/utils/RequestUtils.ts index 0b8e29ba1e..0369603690 100644 --- a/lib/msal-core/src/utils/RequestUtils.ts +++ b/lib/msal-core/src/utils/RequestUtils.ts @@ -21,25 +21,18 @@ export class RequestUtils { * * @param request * @param isLoginCall - * @param requestType - * @param redirectCallbacksSet * @param cacheStorage * @param clientId * * validates all request parameters and generates a consumable request object */ - static validateRequest(request: AuthenticationParameters, isLoginCall: boolean, clientId: string, requestType?: string, redirectCallbacksSet?: boolean): AuthenticationParameters { + static validateRequest(request: AuthenticationParameters, isLoginCall: boolean, clientId: string): AuthenticationParameters { // Throw error if request is empty for acquire * calls if(!isLoginCall && !request) { throw ClientConfigurationError.createEmptyRequestError(); } - // Throw error if callbacks are not set before redirect - if(requestType == Constants.interactionTypeRedirect && !redirectCallbacksSet) { - throw ClientConfigurationError.createRedirectCallbacksNotSetError(); - } - let scopes: Array; let extraQueryParameters: StringDict; diff --git a/lib/msal-core/src/utils/UrlUtils.ts b/lib/msal-core/src/utils/UrlUtils.ts index 8daa4bf829..16395b32e2 100644 --- a/lib/msal-core/src/utils/UrlUtils.ts +++ b/lib/msal-core/src/utils/UrlUtils.ts @@ -98,7 +98,7 @@ export class UrlUtils { /** * Returns current window URL as redirect uri */ - static getDefaultRedirectUri(): string { + static getCurrentUrl(): string { return window.location.href.split("?")[0].split("#")[0]; } diff --git a/lib/msal-core/test/UserAgentApplication.spec.ts b/lib/msal-core/test/UserAgentApplication.spec.ts index aa41c38696..a461482f41 100644 --- a/lib/msal-core/test/UserAgentApplication.spec.ts +++ b/lib/msal-core/test/UserAgentApplication.spec.ts @@ -112,6 +112,7 @@ describe("UserAgentApplication.ts Class", function () { let accessTokenKey : AccessTokenKey; let accessTokenValue : AccessTokenValue; let account : Account; + let config: Configuration; const setTestCacheItems = function () { accessTokenKey = { @@ -226,12 +227,6 @@ describe("UserAgentApplication.ts Class", function () { window.location = oldWindowLocation; }); - it("throws error if loginRedirect is called without calling setRedirectCallbacks", function (done) { - expect(msal.getRedirectUri()).to.be.equal(TEST_URIS.TEST_REDIR_URI); - expect(msal.loginRedirect.bind(msal)).to.throw(ClientConfigurationError); - done(); - }); - it("throws error if null argument is passed to either argument of setRedirectCallbacks", (done) => { expect(() => msal.handleRedirectCallback(null)).to.throw(ClientConfigurationError); done(); @@ -689,25 +684,31 @@ describe("UserAgentApplication.ts Class", function () { beforeEach(function () { cacheStorage = new AuthCache(TEST_CONFIG.MSAL_CLIENT_ID, "sessionStorage", true); - const config: Configuration = { + config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - redirectUri: TEST_URIS.TEST_REDIR_URI + redirectUri: TEST_URIS.TEST_REDIR_URI, + navigateToLoginRequestUrl: false } }; - msal = new UserAgentApplication(config); + setAuthInstanceStubs(); setTestCacheItems(); }); afterEach(function() { + window.location.hash = ""; + config = {auth: {clientId: ""}}; cacheStorage.clear(); sinon.restore(); }); - it("Calls the error callback if two callbacks are sent", function (done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM); + it("Calls the error callback if two callbacks are sent", function () { + window.location.hash = TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, accountState: string) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof ServerError).to.be.true; @@ -716,15 +717,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(TEST_ERROR_DESC); expect(error.message).to.include(TEST_ERROR_DESC); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(tokenReceivedCallback, checkErrorFromServer); }); - it("Calls the token callback if two callbacks are sent", function (done) { + it("Calls the token callback if two callbacks are sent", function () { cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE); - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM); + window.location.hash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; + + msal = new UserAgentApplication(config); const checkResponseFromServer = function(response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; @@ -732,15 +734,16 @@ describe("UserAgentApplication.ts Class", function () { expect(response.tokenType).to.be.eq(ServerHashParamKeys.ID_TOKEN); expect(response.tenantId).to.be.eq(TEST_CONFIG.MSAL_TENANT_ID); expect(response.accountState).to.include(TEST_USER_STATE_NUM); - done(); }; msal.handleRedirectCallback(checkResponseFromServer, errorReceivedCallback); }); - it("Calls the response callback if single callback is sent", function (done) { + it("Calls the response callback if single callback is sent", function () { cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); - cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE); - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM); + cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE) + window.location.hash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; + + msal = new UserAgentApplication(config); const checkResponseFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; @@ -748,7 +751,6 @@ describe("UserAgentApplication.ts Class", function () { expect(response.tokenType).to.be.eq(ServerHashParamKeys.ID_TOKEN); expect(response.tenantId).to.be.eq(TEST_CONFIG.MSAL_TENANT_ID); expect(response.accountState).to.include(TEST_USER_STATE_NUM); - done(); }; msal.handleRedirectCallback(checkResponseFromServer); }); @@ -1068,41 +1070,47 @@ describe("UserAgentApplication.ts Class", function () { beforeEach(function () { cacheStorage = new AuthCache(TEST_CONFIG.MSAL_CLIENT_ID, "sessionStorage", true); - const config: Configuration = { + config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - redirectUri: TEST_URIS.TEST_REDIR_URI + redirectUri: TEST_URIS.TEST_REDIR_URI, + navigateToLoginRequestUrl: false } }; - msal = new UserAgentApplication(config); + setAuthInstanceStubs(); setTestCacheItems(); }); afterEach(function() { + window.location.hash = ""; + config = {auth: {clientId: ""}}; cacheStorage.clear(); sinon.restore(); }); - it("tests saveTokenForHash in case of response", function(done) { - const successHash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; + it("tests saveTokenForHash in case of response", function() { + window.location.hash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE); - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, successHash); + + msal = new UserAgentApplication(config); + const checkRespFromServer = function(response: AuthResponse) { expect(response.uniqueId).to.be.eq(TEST_UNIQUE_ID); expect(response.tokenType).to.be.eq(ServerHashParamKeys.ID_TOKEN); expect(response.tenantId).to.be.eq(TEST_CONFIG.MSAL_TENANT_ID); expect(response.accountState).to.be.eq(TEST_USER_STATE_NUM); expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; - done(); }; msal.handleRedirectCallback(checkRespFromServer, errorReceivedCallback); }); - it("tests saveTokenForHash in case of error", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of error", function() { + window.location.hash = TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof ServerError).to.be.true; @@ -1111,63 +1119,69 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(TEST_ERROR_DESC); expect(error.message).to.include(TEST_ERROR_DESC); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); // TEST_SERVER_ERROR_SUBCODE_CANCEL - it("tests saveTokenForHash in case of non-consentable scopes / return to the application without consenting", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_SERVER_ERROR_SUBCODE_CANCEL + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of non-consentable scopes / return to the application without consenting", function() { + window.location.hash = TEST_SERVER_ERROR_SUBCODE_CANCEL + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof ServerError).to.be.true; expect(error.name).to.include("ServerError"); expect(error.errorCode).to.include(TEST_ACCESS_DENIED); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests if you get the state back in errorReceived callback, if state is a number", function (done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM); + it("tests if you get the state back in errorReceived callback, if state is a number", function () { + window.location.hash = TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); const checkErrorHasState = function(error: AuthError, response: AuthResponse) { expect(response.accountState).to.include(TEST_USER_STATE_NUM); - done(); }; msal.handleRedirectCallback(checkErrorHasState); }); - it("tests if you get the state back in errorReceived callback, if state is a url", function (done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_URL); + it("tests if you get the state back in errorReceived callback, if state is a url", function () { + window.location.hash = TEST_HASHES.TEST_ERROR_HASH + TEST_USER_STATE_URL; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorHasState = function(error: AuthError, response: AuthResponse) { expect(response.accountState).to.include(TEST_USER_STATE_URL); - done(); }; msal.handleRedirectCallback(checkErrorHasState); }); - it("tests that isCallback correctly identifies url hash", function (done) { + it("tests that isCallback correctly identifies url hash", function () { + msal = new UserAgentApplication(config); + expect(msal.isCallback("not a callback")).to.be.false; expect(msal.isCallback("#error_description=someting_wrong")).to.be.true; expect(msal.isCallback("#/error_description=someting_wrong")).to.be.true; expect(msal.isCallback("#access_token=token123")).to.be.true; expect(msal.isCallback("#id_token=idtoken234")).to.be.true; - done(); }); - it("tests that expiresIn returns the correct date for access tokens", function (done) { + it("tests that expiresIn returns the correct date for access tokens", function () { sinon.stub(TimeUtils, "now").returns(TEST_TOKEN_LIFETIMES.BASELINE_DATE_CHECK); const acquireTokenAccountKey = AuthCache.generateAcquireTokenAccountKey(account.homeAccountIdentifier, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(acquireTokenAccountKey, JSON.stringify(account)); - const successHash = TEST_HASHES.TEST_SUCCESS_ACCESS_TOKEN_HASH + TEST_USER_STATE_NUM; + window.location.hash = TEST_HASHES.TEST_SUCCESS_ACCESS_TOKEN_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_ACQ_TOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE); - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, successHash); + + msal = new UserAgentApplication(config); + const checkRespFromServer = function(response: AuthResponse) { expect(response.uniqueId).to.be.eq(TEST_UNIQUE_ID); @@ -1176,18 +1190,19 @@ describe("UserAgentApplication.ts Class", function () { expect(response.accountState).to.be.eq(TEST_USER_STATE_NUM); expect(response.expiresOn.getTime()).to.be.eq((TEST_TOKEN_LIFETIMES.BASELINE_DATE_CHECK + TEST_TOKEN_LIFETIMES.DEFAULT_EXPIRES_IN) * 1000); expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; - done(); }; msal.handleRedirectCallback(checkRespFromServer, errorReceivedCallback); }); - it("tests that expiresIn returns the correct date for id tokens", function (done) { + it("tests that expiresIn returns the correct date for id tokens", function () { const acquireTokenAccountKey = AuthCache.generateAcquireTokenAccountKey(account.homeAccountIdentifier, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(acquireTokenAccountKey, JSON.stringify(account)); - const successHash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; + window.location.hash = TEST_HASHES.TEST_SUCCESS_ID_TOKEN_HASH + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); cacheStorage.setItem(`${TemporaryCacheKeys.NONCE_IDTOKEN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, TEST_NONCE); - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, successHash); + + msal = new UserAgentApplication(config); + const checkRespFromServer = function(response: AuthResponse) { expect(response.uniqueId).to.be.eq(TEST_UNIQUE_ID); expect(response.tokenType).to.be.eq(ServerHashParamKeys.ID_TOKEN); @@ -1195,7 +1210,6 @@ describe("UserAgentApplication.ts Class", function () { expect(response.accountState).to.be.eq(TEST_USER_STATE_NUM); expect(response.expiresOn.getTime()).to.be.eq(TEST_TOKEN_LIFETIMES.TEST_ID_TOKEN_EXP * 1000); expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; - done(); }; msal.handleRedirectCallback(checkRespFromServer, errorReceivedCallback); }); @@ -1205,25 +1219,30 @@ describe("UserAgentApplication.ts Class", function () { beforeEach(function () { cacheStorage = new AuthCache(TEST_CONFIG.MSAL_CLIENT_ID, "sessionStorage", true); - const config: Configuration = { + config = { auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - redirectUri: TEST_URIS.TEST_REDIR_URI + redirectUri: TEST_URIS.TEST_REDIR_URI, + navigateToLoginRequestUrl: false } }; - msal = new UserAgentApplication(config); + setAuthInstanceStubs(); setTestCacheItems(); }); afterEach(function() { + config = {auth: {clientId: ""}}; cacheStorage.clear(); sinon.restore(); }); - it("tests saveTokenForHash in case of interaction_required error code", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_INTERACTION_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of interaction_required error code", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_INTERACTION_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1232,14 +1251,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(TEST_ERROR_DESC); expect(error.message).to.include(TEST_ERROR_DESC); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests saveTokenForHash in case of interaction_required error code and description", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_INTERACTION_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of interaction_required error code and description", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_INTERACTION_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1250,14 +1271,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(InteractionRequiredAuthErrorMessage.interactionRequired.code); expect(error.message).to.include(InteractionRequiredAuthErrorMessage.interactionRequired.code); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests saveTokenForHash in case of login_required error code", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_LOGIN_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of login_required error code", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_LOGIN_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1266,14 +1289,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(TEST_ERROR_DESC); expect(error.message).to.include(TEST_ERROR_DESC); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests saveTokenForHash in case of login_required error code and description", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_LOGIN_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of login_required error code and description", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_LOGIN_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1284,14 +1309,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(InteractionRequiredAuthErrorMessage.loginRequired.code); expect(error.message).to.include(InteractionRequiredAuthErrorMessage.loginRequired.code); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests saveTokenForHash in case of consent_required error code", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_CONSENT_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of consent_required error code", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_CONSENT_REQ_ERROR_HASH1 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1300,14 +1327,16 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(TEST_ERROR_DESC); expect(error.message).to.include(TEST_ERROR_DESC); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); }); - it("tests saveTokenForHash in case of consent_required error code and description", function(done) { - cacheStorage.setItem(TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_CONSENT_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM); + it("tests saveTokenForHash in case of consent_required error code and description", function() { + window.location.hash = TemporaryCacheKeys.URL_HASH, TEST_HASHES.TEST_CONSENT_REQ_ERROR_HASH2 + TEST_USER_STATE_NUM; cacheStorage.setItem(`${TemporaryCacheKeys.STATE_LOGIN}|RANDOM-GUID-HERE|${TEST_USER_STATE_NUM}`, "RANDOM-GUID-HERE|" + TEST_USER_STATE_NUM); + + msal = new UserAgentApplication(config); + const checkErrorFromServer = function(error: AuthError, response: AuthResponse) { expect(cacheStorage.getItem(TemporaryCacheKeys.URL_HASH)).to.be.null; expect(error instanceof InteractionRequiredAuthError).to.be.true; @@ -1318,7 +1347,6 @@ describe("UserAgentApplication.ts Class", function () { expect(error.errorMessage).to.include(InteractionRequiredAuthErrorMessage.consentRequired.code); expect(error.message).to.include(InteractionRequiredAuthErrorMessage.consentRequired.code); expect(error.stack).to.include("UserAgentApplication.spec.ts"); - done(); }; msal.handleRedirectCallback(checkErrorFromServer); });