Skip to content

Fix MSAL token expiry and silent refresh behavior [Experimental]#22103

Open
aasimkhan30 wants to merge 2 commits into
mainfrom
aasim/fix/msalProp
Open

Fix MSAL token expiry and silent refresh behavior [Experimental]#22103
aasimkhan30 wants to merge 2 commits into
mainfrom
aasim/fix/msalProp

Conversation

@aasimkhan30
Copy link
Copy Markdown
Contributor

@aasimkhan30 aasimkhan30 commented May 12, 2026

Description

Fixes MSAL token acquisition behavior that could lead to repeated interactive auth prompts and incorrect token lifetime decisions.

Changes:

  • Store SQL access token expiration from AuthenticationResult.expiresOn instead of ID token exp claims.
  • Stop setting forceRefresh: true on every silent request; force refresh is now only used as a fallback when reusing an account from another tenant for first-time guest-tenant acquisition.
  • Keep silent token acquisition silent: missing cache entries return null, and interaction-required errors are surfaced instead of immediately launching interactive login from getToken.
  • Recover from interaction-required/no-account silent refresh failures one level up in refreshAccessToken with a single interactive reauth attempt using tenant/account/login hints.
  • Resolve tenant-specific AccountInfo by matching both account id and tenant id.
  • Keep prompt: select_account for initial sign-in only, while allowing account/login hints for recovery flows.
  • Update direct MSAL dependencies to @azure/msal-node@^5.2.1 and @azure/msal-common@^16.6.1.
  • Add unit coverage for tenant account selection, force-refresh fallback, silent failure behavior, interaction-required recovery, hinted reauth, and expiresOn mapping.

Related: #22031, #22046, #22053, #22078, #22097.

Validation performed:

  • npm run build:extension:typecheck
  • npm run build:extension:emit
  • npx eslint --quiet --cache src/azure/msal/msalAzureAuth.ts src/azure/msal/msalAzureController.ts test/unit/msalAzureController.test.ts
  • npx vscode-test --coverage --grep "MsalAzure" (12 passing)

Note: full npm run test was not run; this PR was validated with the focused MSAL test suite plus typecheck/lint/build.

Code Changes Checklist

  • New or updated unit tests added
  • All existing tests pass (npm run test)
  • Code follows contributing guidelines
  • Telemetry/logging updated if relevant
  • No regressions or UX breakage

Reviewers: Please read our reviewer guidelines

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the MSSQL extension’s MSAL authentication behavior to reduce repeated interactive prompts and make token lifetime decisions more accurate, addressing reported Entra/MFA login loops and token-expiry-related failures.

Changes:

  • Use AuthenticationResult.expiresOn (access token lifetime) for expiresOn instead of ID token exp claims.
  • Refine silent token acquisition: avoid unconditional forceRefresh, add tenant-aware account selection, and avoid automatically triggering interactive auth from silent flows.
  • Update MSAL dependencies and add unit tests around tenant selection, force-refresh fallback, silent failures, and expiry mapping.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
extensions/mssql/src/azure/msal/msalAzureAuth.ts Adjusts silent token acquisition behavior, tenant-specific account lookup, and maps token expiry from expiresOn.
extensions/mssql/src/azure/msal/msalAzureController.ts Uses the new expiry mapping helper and tightens cache-check behavior.
extensions/mssql/src/azure/msal/msalAzureCodeGrant.ts Extends login flow to accept optional prompt/login hints/account for recovery scenarios.
extensions/mssql/src/azure/msal/msalAzureDeviceCode.ts Updates login signature to match base class options interface.
extensions/mssql/test/unit/msalAzureController.test.ts Adds unit coverage for tenant-aware MSAL account selection, force-refresh fallback, silent failure behavior, and expiry mapping.
extensions/mssql/package.json Bumps direct MSAL dependencies to newer major versions.
extensions/mssql/package-lock.json Updates lockfile to reflect MSAL dependency changes and resolved dependency graph.
Files not reviewed (1)
  • extensions/mssql/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

extensions/mssql/test/unit/msalAzureController.test.ts:354

  • This test uses calledOnce and firstCall to validate forceRefresh behavior. To reduce brittleness, assert the silent request via calledWithMatch (including forceRefresh: true) without depending on call count or firstCall indexing.
        expect(acquireTokenSilent).to.have.been.calledOnce;
        const request = acquireTokenSilent.firstCall.args[0] as msalNode.SilentFlowRequest;
        expect(request.account).to.equal(homeAccount);
        expect(request.forceRefresh).to.equal(true);
    });

Comment on lines +47 to +48
export function getTokenExpirationInSeconds(result: AuthenticationResult): number | undefined {
return result.expiresOn ? Math.floor(result.expiresOn.getTime() / 1000) : undefined;
Comment on lines 176 to 183
try {
accountInfo = await this.getAccountFromMsalCache(account.key.id);
accountInfo = await this.getAccountFromMsalCache(account.key.id, tenantId);
} catch (ex) {
this.logger.error(
`Error: Could not fetch account from MSAL cache, re-authentication needed: ${getErrorMessage(ex)}`,
`Error: Could not fetch account from MSAL cache: ${getErrorMessage(ex)}`,
);
// build refresh token request
const tenant: ITenant = {
id: tenantId,
displayName: "",
};
return this.handleInteractionRequired(tenant, settings, false);
return null;
}
Comment on lines +319 to +320
const account = matchingAccounts[0] ?? null;

Comment on lines +327 to +335
expect(acquireTokenSilent).to.have.been.calledOnce;
const request = acquireTokenSilent.firstCall.args[0] as msalNode.SilentFlowRequest;
expect(request.account).to.equal(targetAccount);
expect(request.forceRefresh).to.be.undefined;
expect(request.authority).to.equal(
`${providerSettings.publicAzureProviderSettings.loginEndpoint}${targetTenantId}`,
);
expect(request.scopes).to.deep.equal(["https://database.windows.net/.default"]);
});
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

PR Changes

Category Target Branch PR Branch Difference
vscode-mssql VSIX 78068 KB 78021 KB ⚪ -47 KB ( 0% )
sql-database-projects VSIX 6310 KB 6310 KB ⚪ 0 KB ( 0% )
data-workspace VSIX 535 KB 535 KB ⚪ 0 KB ( 0% )
keymap VSIX 7 KB 7 KB ⚪ 0 KB ( 0% )

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 12, 2026

Codecov Report

❌ Patch coverage is 75.66138% with 46 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.98%. Comparing base (218a589) to head (e4060b2).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
extensions/mssql/src/azure/msal/msalAzureAuth.ts 80.16% 24 Missing ⚠️
...ensions/mssql/src/azure/msal/msalAzureCodeGrant.ts 14.28% 12 Missing ⚠️
...nsions/mssql/src/azure/msal/msalAzureController.ts 85.71% 7 Missing ⚠️
...nsions/mssql/src/azure/msal/msalAzureDeviceCode.ts 40.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #22103      +/-   ##
==========================================
+ Coverage   74.83%   74.98%   +0.15%     
==========================================
  Files         394      394              
  Lines      120403   120506     +103     
  Branches     7197     7242      +45     
==========================================
+ Hits        90102    90360     +258     
+ Misses      30301    30146     -155     
Flag Coverage Δ
data-workspace 77.10% <ø> (ø)
mssql 74.68% <75.66%> (+0.16%) ⬆️
sqlproj 77.33% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...nsions/mssql/src/azure/msal/msalAzureDeviceCode.ts 40.00% <40.00%> (-1.31%) ⬇️
...nsions/mssql/src/azure/msal/msalAzureController.ts 75.00% <85.71%> (+20.87%) ⬆️
...ensions/mssql/src/azure/msal/msalAzureCodeGrant.ts 24.72% <14.28%> (-1.05%) ⬇️
extensions/mssql/src/azure/msal/msalAzureAuth.ts 58.66% <80.16%> (+12.08%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aasimkhan30 aasimkhan30 changed the title Fix MSAL token expiry and silent refresh behavior Fix MSAL token expiry and silent refresh behavior [Experimental] May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants