microsoft / microsoft/teams.ts
TokenManager: expose skipCache option for token refresh after 401
- Dominant language
- TypeScript
- Stars
- 107
- Forks
- 39
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 74
Description
## 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 })`:
```typescript
// token-manager.ts
async getBotToken(skipCache?: boolean): Promise { ... }
async getGraphToken(tenantId?: string, skipCache?: boolean): Promise { ... }
// 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.
Contributor guide
Research direction
Start in token-manager.ts with getBotToken() and getGraphToken(), then trace their wrappers in app.ts and the existing acquireTokenByClientCredential calls. Thread the optional skipCache value through these entry points while preserving current behavior when it is omitted; done means callers can request a fresh token without breaking existing calls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, authentication
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100