Skip to content

[Bug] Invalid unlocking in the Persistence extension #893

Open

Description

Library version used

1.18.0 and 1.3.0

Java version

Temurin JDK 17

Scenario

PublicClient (AcquireTokenInteractive, AcquireTokenByUsernamePassword)

Is this a new or an existing app?

This is a new app or experiment

Issue description and reproduction steps

When using the msal4j-persistence extension on MacOS Keychain, the library fails to perform an unlock operation during the afterCacheAccess step, even during a basic read operation. This results in an unnecessary error being logged without any explanation.

The code example is below. The issue arises when I create a public client application with a persistent token cache enabled for Keychain. I only read the account cache, but it logs a "null" error during the second unlock attempt. The log reads:

[ForkJoinPool.commonPool-worker-1] DEBUG com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - pid:78864 thread:15 acquiring file lock
[ForkJoinPool.commonPool-worker-1] DEBUG com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - pid:78864 thread:15 acquired OK file lock
[ForkJoinPool.commonPool-worker-1] DEBUG com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - pid:78864 thread:15 releasing lock
[ForkJoinPool.commonPool-worker-1] DEBUG com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - pid:78864 thread:15 releasing lock
[ForkJoinPool.commonPool-worker-1] ERROR com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - null

When I debug the code, I observe that it acquires a lock as a normal behavior. But it releases the lock during the beforeCacheAccess step. Then, it reads the cache and executes afterCacheAccess, where it does not have a proper lock but still attempts to unlock.

I was able to reproduce this issue with MSAL4j 1.17.3 and 1.18.0, using the persistence extension 1.2.0 and 1.3.0. Unfortunately, I don’t have access to a Windows test environment, so I couldn’t test it there.

Relevant code snippets

public class MsalPersistenceTest {

    public static final String CLIENTID = "<irrelevant>";
    public static final String AUTHURL = "https://login.microsoftonline.com/irrelevant";
    private static final Set<String> SCOPES = Set.of("User.Read");

    public static void main(String[] args) {

        try {
            var ps = PersistenceSettings.builder("temp.cache", Path.of(System.getProperty("user.home"), ".deleteme"))
                                        .setMacKeychain("DeleteMe", "me")
                                        .setLockRetry(1000, 50)
                                        .build();

            var pca = PublicClientApplication.builder(CLIENTID)
                                             .authority(AUTHURL)
                                             .setTokenCacheAccessAspect(new PersistenceTokenCacheAccessAspect(ps))
                                             .build();

            // Force cache to be loaded
            pca.getAccounts().join();

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

}

Expected behavior

I guess it doesn't need to unlock on the beforeCacheAccess, or it doesn't need to unlock later.

Identity provider

Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)

Regression

No response

Solution and workarounds

No response

Activity

  1. added
    needs attentionAutomatically used when an issue is created through an issue template
    untriagedAutomatically used when an issue is created through an issue template
    on Dec 30, 2024
  2. added
    BugSomething isn't working, needs an investigation and a fix
    P2Normal priority items, should be done after P1
    persistence-packageFor issues related to the msal4j-persistence-extension package
    and removed
    needs attentionAutomatically used when an issue is created through an issue template
    untriagedAutomatically used when an issue is created through an issue template
    on Dec 31, 2024
  3. bowen-facere commented on Jul 26, 2025

    @bowen-facere

    Hey, were you able to find a solution to this?

  4. sbasegmez commented on Jul 28, 2025

    @sbasegmez
    Author

    Hey, were you able to find a solution to this?

    I just added a band-aid if to the line in the error message just to prevent the null logs.

    PersistenceTokenCacheAccessAspect > afterCacheAccess:

    @Override
        public void afterCacheAccess(ITokenCacheAccessContext iTokenCacheAccessContext) {
            try {
                if (isWriteAccess(iTokenCacheAccessContext)) {
                    cacheAccessor.write(iTokenCacheAccessContext.tokenCache().serialize().getBytes(StandardCharset.UTF_8));
                    updateLastSeenCacheFileModifiedTimestamp();
                }
            } finally {
                // Added by SB to check the lock before unlocking.
                if(lock != null && lock.isLocked()) {
                    try {
                        lock.unlock();
                    } catch (IOException e) {
                        LOG.error("Unable to unlock the lock", e);
                    }
                }
            }
        }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working, needs an investigation and a fixP2Normal priority items, should be done after P1persistence-packageFor issues related to the msal4j-persistence-extension packagepublic-clientFor questions/issues related to public client apps

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions