fix(msteams): bound delegated token probe expiry

This commit is contained in:
Peter Steinberger
2026-05-30 13:54:56 -04:00
parent 5fde637ba8
commit c5aa3ff02f
2 changed files with 54 additions and 1 deletions

View File

@@ -3,6 +3,15 @@ import type { MSTeamsConfig } from "../runtime-api.js";
const hostMockState = vi.hoisted(() => ({
tokenError: null as Error | null,
delegatedTokens: undefined as
| {
accessToken: string;
refreshToken: string;
expiresAt: number;
scopes: string[];
userPrincipalName?: string;
}
| undefined,
}));
vi.mock("@microsoft/teams.apps", () => ({
@@ -33,11 +42,20 @@ vi.mock("@microsoft/teams.api", () => ({
}),
}));
vi.mock("./token.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./token.js")>();
return {
...actual,
loadDelegatedTokens: () => hostMockState.delegatedTokens,
};
});
import { probeMSTeams } from "./probe.js";
describe("msteams probe", () => {
beforeEach(() => {
hostMockState.tokenError = null;
hostMockState.delegatedTokens = undefined;
vi.stubEnv("MSTEAMS_APP_ID", "");
vi.stubEnv("MSTEAMS_APP_PASSWORD", "");
vi.stubEnv("MSTEAMS_TENANT_ID", "");
@@ -83,4 +101,38 @@ describe("msteams probe", () => {
error: "bad creds",
});
});
it("reports delegated tokens expired when the process clock is invalid", async () => {
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(Number.NaN);
hostMockState.delegatedTokens = {
accessToken: "delegated-token",
refreshToken: "refresh-token",
expiresAt: Date.parse("2030-01-01T00:00:00.000Z"),
scopes: ["ChatMessage.Send"],
userPrincipalName: "user@example.com",
};
const cfg = {
enabled: true,
appId: "app",
appPassword: "pw",
tenantId: "tenant",
delegatedAuth: { enabled: true },
} as unknown as MSTeamsConfig;
try {
await expect(probeMSTeams(cfg)).resolves.toEqual({
ok: true,
appId: "app",
graph: { ok: true, roles: undefined, scopes: undefined },
delegatedAuth: {
ok: false,
scopes: ["ChatMessage.Send"],
userPrincipalName: "user@example.com",
error: "token expired (will auto-refresh on next use)",
},
});
} finally {
nowSpy.mockRestore();
}
});
});

View File

@@ -1,3 +1,4 @@
import { isFutureDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
import {
normalizeStringEntries,
type BaseProbeResult,
@@ -100,7 +101,7 @@ export async function probeMSTeams(cfg?: MSTeamsConfig): Promise<ProbeMSTeamsRes
try {
const tokens = loadDelegatedTokens();
if (tokens) {
const isExpired = tokens.expiresAt <= Date.now();
const isExpired = !isFutureDateTimestampMs(tokens.expiresAt);
delegatedAuth = {
ok: !isExpired,
scopes: tokens.scopes,