Warn before the client secret expires (SP_SECRET_EXPIRES)
Azure client secrets lapse silently into opaque 401s (AADSTS7000222). Add a self-reported expiry date (SP_SECRET_EXPIRES=YYYY-MM-DD) and a shared secretExpiryStatus() helper in graph.mjs; surface warnings <30 days out via the CLI (stderr on every command + test output), MCP server startup log, /health, and the sharepoint_test tool. Documented in both .env.examples, setup.md, and READMEs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -327,6 +327,49 @@ export class SharePointListsClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report how close the app's client secret is to expiring, so consumers can
|
||||
* warn BEFORE auth starts failing with an opaque 401 (AADSTS7000222).
|
||||
*
|
||||
* Azure can't be queried for its own secret's expiry with app-only creds, so
|
||||
* the date is self-reported: set SP_SECRET_EXPIRES=YYYY-MM-DD in .env when you
|
||||
* create/rotate the secret (the Entra portal shows it in Certificates & secrets).
|
||||
*
|
||||
* @param {string} [expiresStr] The configured date; falsy → null (feature unused).
|
||||
* @returns {null | { expiresAt: string, daysLeft: number, level: "ok"|"warning"|"expired", message: string|null }}
|
||||
*/
|
||||
export function secretExpiryStatus(expiresStr, { warnDays = 30 } = {}) {
|
||||
if (!expiresStr) return null;
|
||||
const exp = new Date(expiresStr);
|
||||
if (Number.isNaN(exp.getTime())) {
|
||||
return {
|
||||
expiresAt: expiresStr,
|
||||
daysLeft: NaN,
|
||||
level: "warning",
|
||||
message: `SP_SECRET_EXPIRES is set but unparseable ("${expiresStr}") — use YYYY-MM-DD.`,
|
||||
};
|
||||
}
|
||||
const daysLeft = Math.floor((exp.getTime() - Date.now()) / 86_400_000);
|
||||
const expiresAt = exp.toISOString().slice(0, 10);
|
||||
if (daysLeft < 0) {
|
||||
return {
|
||||
expiresAt,
|
||||
daysLeft,
|
||||
level: "expired",
|
||||
message: `Client secret EXPIRED ${-daysLeft} day(s) ago (${expiresAt}). Auth will fail with 401 — create a new secret in Entra (Certificates & secrets), update SP_CLIENT_SECRET and SP_SECRET_EXPIRES.`,
|
||||
};
|
||||
}
|
||||
if (daysLeft <= warnDays) {
|
||||
return {
|
||||
expiresAt,
|
||||
daysLeft,
|
||||
level: "warning",
|
||||
message: `Client secret expires in ${daysLeft} day(s) (${expiresAt}) — rotate it soon: new secret in Entra, then update SP_CLIENT_SECRET and SP_SECRET_EXPIRES.`,
|
||||
};
|
||||
}
|
||||
return { expiresAt, daysLeft, level: "ok", message: null };
|
||||
}
|
||||
|
||||
/** Flatten the Graph item shape into { id, fields, webUrl, ...meta }. */
|
||||
function simplifyItem(item) {
|
||||
if (!item) return item;
|
||||
|
||||
Reference in New Issue
Block a user