microsoft-authentication-library-for-js: handleRedirectCallback does not return a promise

Library

  • msal@1.2.2 from npm install msal

Description

In my Vue.js project I have a simple SignIn button that calls the correct login method (loginPopup or loginRedirect) depending on a config file for the app. Authentication in the app happens by triggering the signIn action from the vuex store. This works perfectly fine for the loginPopup method but for the loginRedirect method it throws the following error:

image

app\src\store\auth\actions.js

import auth from 'src/services/auth/authService'

export const signIn = ({ commit, dispatch }) => {
  commit('SET_BUTTON', { name: 'signInButton', updates: { disabled: true } })

  auth.signIn()
    .then(loginResponse => {
      console.log("id_token acquired at: " + new Date().toString())
      console.log(loginResponse);

      dispatch('setSignedInAccount')
    })
    .catch(error => {
      console.log(error);
      commit('SET_BUTTON', { name: 'signInButton', updates: { show: true, disabled: false } })
    });
}

export const setSignedInAccount = ({ commit }) => {
  let account = auth.getAccount()
  if (account) {
    commit('SET_BUTTON', { name: 'signInButton', updates: { show: false } })
    commit('SET_ACCOUNT', account)
  }
}

export const signOut = () => {
  if (auth.getAccount()) {
    auth.signOut()
  }
}

You can see that auth.signIn() expects a Promise by the use of .then and .catch. This approach works fine when msal is configured with acquireTokenPopup but not with handleRedirectCallback.

This is by design when I read the docs.:

For authentication methods with redirect flows (loginRedirect and acquireTokenRedirect), in MSAL.js 1.2.x or earlier, you will need to explicitly register a callback for success or error through handleRedirectCallback() method. This is needed since redirect flows do not return promises as the methods with a pop-up experience do. This became optional in MSAL.js version 1.3.0.

So to also make the signIn button work with the loginRedirect I would need to return a Promise which I tried to do like this:

authRedirect.js

export const authRedirectCallback = (error, response) => {
  return new Promise((resolve, reject) => {
    if (error) {reject(error)}
    else resolve(response)
  })
}
msal.handleRedirectCallback(authRedirectCallback)

export const signIn = () => msal.loginRedirect(loginRequest)

As I’m a newbie I might have gotten things wrong becuase it doesn’t seem to work. Can you have a look at how I should return a promise so the action in the Vuex store with the .then and the .catch will still work?

authPopup.js

export const signIn = () => msal.loginPopup(loginRequest)

Thank you for your help.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 20

Most upvoted comments

@DarkLite1 The redirect flow can be confusing and we could do a better job of documenting how it works, but in the meantime I’ll try to clear up the confusion. As @m-sterspace pointed out, redirecting away from the page means you are creating a whole new instance of the application when you return. This means that calling a redirect method cannot return anything. Rather what happens is, the page is redirected away, you do your signin and you are redirected back to your application with the response in the hash.

This is where things got a little muddy, pre-1.3.0. In order to process this hash and cache it, the developer needed to call handleRedirectCallback() which kicks off the processing and then calls the user defined function that was passed. However, if navigateToRequestUrl was set to true, another redirect would be kicked off while processing the hash, meaning that the user-defined function was run on a previous instance of the application, the results of which have now been lost. If you find this is happening to you, try setting navigateToRequestUrl to false. The downside of doing this is that you need to have every page that might acquire a token, registered in the app portal as a redirect uri.

In version 1.3.0 we made some changes to this behavior. Now processing of the hash is done on initialization of msal and it is done on the final page after all redirects are complete. Calling handleRedirectCallback() now just reaches into msal to return the result/error that occurred while processing the hash to the function you defined.

In the handleRedirectCallback you should do the same things you would have done in the .then clause of your popup promise if a result was returned. If an error was returned execute your .catch logic. I highly suggest upgrading to 1.3.0 as this flow becomes a little clearer. If you don’t want to try the beta, we should have the mainline release coming soon.

We apologize for how confusing this is and we will certainly try to do a better job of documenting it. As there are already tickets open for documentation, however, I’m going to close this. Please do let me know if you have additional questions.

@DarkLite1 @tnorling thank you very much for the discussion and explanation. In the middle of this thread I kind of realized that I must be doing something wrong since Microsoft’s javascript apps do not require you to login every hour like mine do and now this is all making a lot more sense as to why.

In regards to managing the token myself, I don’t think it’s a lack of capability of the library so much as a documentation gap that has lead me to managing it myself. It was a pretty big struggle to get the redirect flow working at all and I wasn’t completely clear on what the different parts of the api were doing in relation to that flow, what my app should be doing when, and why each part matters. Once my application was able to get a hold of an actual, verifiable, access token, I was not going to let it go for dear life.

That does all make sense for the most part now, though I’m still a little unclear on the whole sid / loginhint option … Do I need to pass that for acquiretokensilent to last longer than the hour of the token? And where do the values for those come from? Presumably the sid would be determined by AAD once a session is created, and would then have to be stored by the application in local storage or a cookie to last longer than a single application instance… but for the loginhint option, what are the requirements there? Should the loginhint be unique to a user, or to an application, or to a session? Can I just hardcode it as something like “my-javascript-application”?

@DarkLite1 The tokens are cached in localstorage or sessionstorage by msal so there should not be a need to store them separately on your own. acquireTokenSilent will always look for a cached token before attempting to make a network call. Therefore the recommendation is to call acquireTokenSilent and if it throws an InteractionRequired error call acquireTokenRedirect or acquireTokenPopup