Skip to content

TokenManager: expose skipCache option for token refresh after 401 #493

@SidU

Description

@SidU

Problem

TokenManager.getBotToken() and TokenManager.getGraphToken() call MSAL's acquireTokenByClientCredential({ scopes }) without passing skipCache. This means callers have no way to force a fresh token after receiving a 401 from the Bot Framework or Graph API.

MSAL caches tokens with a ~5 minute buffer before expiry. A token can expire between the moment it's fetched from cache and when the HTTP response arrives — particularly during:

  • Long-running operations (file uploads to OneDrive/SharePoint)
  • Streaming sessions that span minutes (Teams streaminfo protocol)
  • Proactive messaging that happens long after the initial token was cached

When this race condition occurs, the caller gets a 401 and wants to retry with a fresh token. But calling getBotToken() / getGraphToken() again returns the same cached token because MSAL still considers it valid.

Proposed Solution

Add an optional skipCache parameter to getBotToken and getGraphToken that threads through to MSAL's acquireTokenByClientCredential({ scopes, skipCache }):

// token-manager.ts
async getBotToken(skipCache?: boolean): Promise<IToken | null> { ... }
async getGraphToken(tenantId?: string, skipCache?: boolean): Promise<IToken | null> { ... }

// app.ts
protected async getBotToken(skipCache?: boolean) { ... }
protected async getAppGraphToken(tenantId?: string, skipCache?: boolean) { ... }

This is fully backward-compatible — skipCache defaults to false, so all existing callers are unaffected. Only 401-retry code paths would pass true.

Context

We hit this in the OpenClaw Teams plugin where Bot Framework REST calls (update/delete activity) and Graph API calls (OneDrive uploads, sharing links, chat member lookups) intermittently fail with 401 after the bot has been running for extended periods. We implemented a retry-on-401 pattern but it's ineffective without skipCache because the retry gets the same stale token from MSAL's cache.

MSAL's ClientCredentialRequest already supports skipCache?: boolean — the Teams SDK just doesn't expose it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions