Skip to content

Replace custom MSAL cache plugin with msal-node-extensions#7398

Open
waldekmastykarz wants to merge 7 commits into
pnp:mainfrom
waldekmastykarz:waldekmastykarz/msal-extensions-token-storage
Open

Replace custom MSAL cache plugin with msal-node-extensions#7398
waldekmastykarz wants to merge 7 commits into
pnp:mainfrom
waldekmastykarz:waldekmastykarz/msal-extensions-token-storage

Conversation

@waldekmastykarz

Copy link
Copy Markdown
Member

What's in this PR

Replaces the custom ICachePlugin implementation that stored MSAL tokens as plaintext JSON files (~/.cli-m365-msal.json) with @azure/msal-node-extensions PersistenceCachePlugin, which uses the OS-specific credential store:

  • macOS: Keychain
  • Windows: DPAPI
  • Linux: libsecret (with plaintext file fallback)

Changes

  • src/auth/msalCachePlugin.ts: Replaced custom beforeCacheAccess/afterCacheAccess with PersistenceCreator.createPersistence() and PersistenceCachePlugin. Exposed stubbable createPersistence/createPlugin seams for testability. Added removeLegacyCache() to clean up old plaintext cache files on upgrade.
  • src/Auth.ts: Updated to use async getCachePlugin() and clearMsalCache() from the new module. Removed getMsalCacheStorage() method.
  • src/auth/FileTokenStorage.ts: Removed msalCacheFilePath() (no longer needed for MSAL cache; connection storage unchanged).
  • package.json: Added @azure/msal-node-extensions dependency.
  • docs/docs/concepts/persisting-connection.mdx: Updated to document OS credential store usage and corrected connection file names.
  • Tests: Updated all related tests (msalCachePlugin.spec.ts, Auth.spec.ts, FileTokenStorage.spec.ts).

Migration

On first run after upgrade, removeLegacyCache() detects and deletes any old plaintext ~/.cli-m365-msal.json file. Users will need to re-authenticate, as the new credential store starts empty.

Multi-account support

Unchanged — MSAL internally manages multiple accounts within a single cache. Connection metadata (~/.cli-m365-connection.json, ~/.cli-m365-all-connections.json) remains in FileTokenStorage.

waldekmastykarz and others added 3 commits June 9, 2026 15:51
Replace the custom ICachePlugin implementation that stored tokens as
plain JSON files (~/.cli-m365-msal.json) with @azure/msal-node-extensions
PersistenceCachePlugin, which stores tokens in the OS-specific credential
store (macOS Keychain, Windows DPAPI, Linux libsecret with plaintext
fallback).

Key changes:
- msalCachePlugin.ts: replaced custom beforeCacheAccess/afterCacheAccess
  with PersistenceCreator.createPersistence() and PersistenceCachePlugin
- Auth.ts: use async getCachePlugin() and clearMsalCache() from the
  new module
- FileTokenStorage.ts: removed msalCacheFilePath() (no longer needed for
  MSAL cache; still used for connection info storage)
- Updated all tests to use the new stubbable object pattern

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On upgrade, the old ~/.cli-m365-msal.json may contain plaintext tokens
(especially on macOS where the new persistence uses Keychain). Delete
the file during initialization if it contains valid JSON, indicating
it's a legacy plaintext cache rather than new persistence-managed content.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update persisting-connection.mdx to document that the MSAL token cache
is now stored in the OS-specific credential store (macOS Keychain,
Windows DPAPI, Linux libsecret) instead of a plaintext JSON file. Also
fix the connection file names to match current code (.cli-m365-connection
and .cli-m365-all-connections).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@waldekmastykarz waldekmastykarz marked this pull request as draft June 10, 2026 07:38
waldekmastykarz and others added 4 commits June 10, 2026 09:41
On Linux without libsecret installed, PersistenceCreator.createPersistence()
fails because the keytar native module cannot load libsecret-1.so.0. The
usePlaintextFileOnLinux fallback in PersistenceCreator only handles
verification failures, not module load failures.

Catch the error in createPersistence() and fall back to FilePersistence
directly, ensuring the CLI works on Linux environments without libsecret
(CI, containers, headless servers).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace createPersistence/createPlugin seams with a single
createNativePersistence method that wraps the entire dynamic import of
@azure/msal-node-extensions in a try/catch. When the barrel export
fails to load (e.g. Linux without libsecret installed), fall back to
a built-in FileCachePlugin that reads/writes tokens to a plain JSON
file, matching the original custom cache plugin behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract importMsalExtensions and createFileFallback as separate
stubbable seams so createNativePersistence body can be tested via
mocks. Add test that calls real importMsalExtensions (covers line 52
even when the import throws on Linux without libsecret).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@waldekmastykarz waldekmastykarz marked this pull request as ready for review June 10, 2026 10:10
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.

1 participant