Search code examples
oauthjwttokenmsalentra

MSAL AcquireSilent gets always new token


I got a svelte application which is using @azure/msal-browser: ^3.22.0

Actually calling acquireTokenSilent should retreive my valid accessToken from the cache and only if it is not valid or expired get a new one.

But in my case the method call always gets a new token and updates the cache. So for each api call a new token is created.

What is the problem here?

Here is the method. getToken is called for every api call but should not always create a new token:


export async function getToken(clientId, mail) {
    let tokenRes;
    let loginReq = getLoginRequest(clientId);
    tokenRes = await acquireToken(loginReq, mail);

    if (!tokenRes) {
        return null;
    }

    return tokenRes.accessToken;
}

function acquireToken(req, mail) {
    req.account = msalInstance.getAccount({ username: mail });

    return msalInstance.acquireTokenSilent(req).catch((error) => {
        if (error instanceof msal.InteractionRequiredAuthError) {
            return msalInstance.acquireTokenRedirect(req);
        }
    });
}
    if (!tokenResponse) {
        return null;
    }

    return tokenResponse.accessToken;
}

function acquireToken(request, mailAddress) {
    request.account = msalInstance.getAccount({ username: mailAddress });

    return msalInstance.acquireTokenSilent(request).catch((error) => {
        console.error(error);

        if (error instanceof msal.InteractionRequiredAuthError) {
            console.warn('Silent token acquisition fails. Use fallback acquiring token using redirect.');
            return msalInstance.acquireTokenRedirect(request);
        }
    });
}

Solution

  • I found the solution in the official Microsoft Microsoft documentation:

    It explains that "MSAL uses a cache to store tokens based on specific parameters including scopes, resource and authority, and will retrieve the token from the cache when needed. It also can perform silent renewal of those tokens when they have expired. MSAL exposes this functionality through the acquireTokenSilent method."

    The scopes are crucial because they determine whether a cached token can be reused.

    In my getLoginRequest method, the scopes are defined as:

    scopes: [
                "api://${id}/myApi.Read",
                "api://${id}/myApi.Write",
                "openid",
                "profile",
                "offline_access",
                "email",
                "User.Read"
            ]
    

    This is where the issue arose. I was requesting the scopes openid, profile, offline_access, email, and User.Read. However, it appears these scopes weren’t being set in the retrieved token. As a result, every time I called acquireTokenSilent, it recognized the token in the cache, but since the token was missing the requested scopes, it discarded the old token and attempted to fetch a new one. Unfortunately, the new token still didn’t include the necessary scopes, causing the cycle to repeat.

    When the not existent scopes are removed, the tokenCache works correctly.