Skip to content

[copilot-finds] Bug: AccessTokenCache has race condition allowing concurrent duplicate token fetches #173

@github-actions

Description

@github-actions

Problem

AccessTokenCache.getToken() in packages/durabletask-js-azuremanaged/src/access-token-cache.ts has a race condition when multiple concurrent callers request a token while the cache is empty or expired.

When multiple gRPC calls occur simultaneously (e.g., during high-concurrency orchestration processing), each call invokes the metadata generator which calls cache.getToken(). If the token needs refreshing, every concurrent caller independently calls credential.getToken() because the await suspends each caller before the cache is updated:

Caller A: checks cache → expired → await credential.getToken() [suspends]
Caller B: checks cache → still expired → await credential.getToken() [suspends]
Caller C: checks cache → still expired → await credential.getToken() [suspends]
...

This results in N redundant Azure AD / Entra ID network calls instead of 1.

Root Cause

The getToken() method checks the cache and fetches a new token in a single async flow without any coordination between concurrent callers. The await before credential.getToken() yields execution, allowing other callers to observe the same stale cache state and trigger their own token fetches.

Proposed Fix

Use promise deduplication: store the in-flight token fetch promise so concurrent callers share the same fetch. Only the first caller initiates the credential request; subsequent callers await the same promise. The pending promise is cleared in a finally block, ensuring cleanup on both success and failure.

Impact

  • Severity: Medium. In high-concurrency scenarios, this causes redundant Azure AD API calls which can lead to rate limiting and unnecessary latency.
  • Affected scenarios: Any application using Azure Managed Durable Task with concurrent orchestration/activity/entity operations, particularly during token refresh windows.
  • Additional concern: The class has zero unit test coverage, so this issue has never been tested.

Metadata

Metadata

Assignees

No one assigned

    Labels

    copilot-findsFindings from daily automated code review agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions