question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

MSAL does not return the new token after an acquireSilent()

See original GitHub issue

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Other... Please describe:

Browser:

  • Chromium version 74 (with Brave)
  • Firefox version XX
  • IE version XX
  • Edge version XX
  • Safari version XX

Library version

Library version: 1.0.1

Current behavior

  • I’m using MSAL with VueJS
  • I use the localStorage (and not the sessionStorage)
  • I use axios to intercept outgoing requests to my API, and inject the accessToken from acquireTokenSilent() as a bearer token

When the token expires, MSAL fetches a new one (using acquireTokenSilent()), updates the localStorage accordingly, but still returns the old one. As a result, when I call my API, I have 401 errors.

  • When I refresh the page, acquireTokenSilent() returns the new token.
  • When I open a new tab from the same app, the new tab acquireTokenSilent() returns the new token. However, the old tab still uses the old token

The localStorage just after a login: image

After a token refresh: image

The new token is present (in green), while the old one is still there in red. MSAL still returns the old one when calling acquireTokenSilent()

Expected behavior

MSAL should use the new token

Minimal reproduction of the problem with instructions

The ajax interceptor:

    axios.interceptors.request.use(
      async request => {
        await setToken(request)
        return request
      },
      error => {
        return Promise.reject(error)
      }
    )

    const setToken = async (request: any) => {
      const res = await authService.getAuthResponse() as Msal.AuthResponse
      request.headers.common['Authorization'] = `Bearer ${res.accessToken}` // This fails if MSAL requested a new token
    }

The authService:

  public async getAuthResponse(): Promise<null | Msal.AuthResponse> {
    const tokenRequest = {
      scopes: this.graphScopes
    }

    try {
      return await this.userAgentApp.acquireTokenSilent(tokenRequest)
    }
    catch (e) {
      this.userAgentApp.acquireTokenRedirect(tokenRequest)
      return null
    }
  }

UPDATED WORKAROUND:

To manually retrieve the correct token:

function extractMSALToken() {
  const timestamp = Math.floor((new Date()).getTime() / 1000)
  let token = null

  for (const key of Object.keys(localStorage)) {
    if (key.includes('"authority":')) {
      const val: any = JSON.parse(localStorage.getItem(key)!)

      if (val.expiresIn) {
        // We have a (possibly expired) token

        if (val.expiresIn > timestamp && val.idToken === val.accessToken) {
          console.log(key)
          // Found the correct token
          token = val.idToken
        }
        else {
          console.log('will remove ' + key)
          // Clear old data
          localStorage.removeItem(key)
        }
      }
    }
  }
  if (token) return token
  throw new Error('No valid token found')
}

Old workaround (invalid):

    const setToken = async (request: any) => {
      // Make sure that MSAL token is up-to-date
      const res = await authService.getAuthResponse() as Msal.AuthResponse
      // DIRTY FIX : directly fetch the token from the localStorage
      request.headers.common['Authorization'] = `Bearer ${localStorage.getItem('msal.idtoken')}`
      // request.headers.common['Authorization'] = `Bearer ${res.accessToken}` // This doesn't work
    }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:33 (16 by maintainers)

github_iconTop GitHub Comments

6reactions
tylerjwatsoncommented, Jul 7, 2019

Guys,

This is a severe issue with a Microsoft product. Can we please get an update and an ETA to fix?

Cheers

6reactions
scambiercommented, Jul 3, 2019

Updated workaround: getting msal.idtoken from the localStorage does not work reliably, as the value is sometimes (often) not correctly updated by msal.

This function seems to get the correct, up-to-date token more reliably:

function extractMSALToken() {
  const timestamp = Math.floor((new Date()).getTime() / 1000)
  let token = null

  for (const key of Object.keys(localStorage)) {
    if (key.includes('"authority":')) {
      const val: any = JSON.parse(localStorage.getItem(key)!)

      if (val.expiresIn) {
        // We have a (possibly expired) token

        if (val.expiresIn > timestamp && val.idToken === val.accessToken) {
          // Found the correct token
          token = val.idToken
        }
        else {
          // Clear old data
          localStorage.removeItem(key)
        }
      }
    }
  }
  if (token) return token
  throw new Error('No valid token found')
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Not getting token with acquireTokensilent but ... - Stack Overflow
Summarize the comments into the answer in order to archive this question: Giving account object in the request can resolve this issue.
Read more >
Single-page application: Acquire a token to call an API
If no access token is found or the access token found has expired, it attempts to use its refresh token to get a...
Read more >
Microsoft Authentication Library for JavaScript (MSAL.js)
It also enables your app to get tokens to access Microsoft Cloud services such ... Internet Explorer does not have native Promise support,...
Read more >
React Msal Dose Not Return The Web API Custom Scope
The Microsoft identity platform endpoint does not allow you to get a token for ... MSAL does not return the new token after...
Read more >
MSAL Python 1.20.0 documentation
This method will combine the cache empty and refresh error into one return value, None . If your app does not care about...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found