← back to George Gmail
feat(auth): info@ → Internal client (staggered stage 2) + token-age validates Workspace accts with Internal client
e6b8c5530c9c7a4c93f0a121768529d3d81b3d06 · 2026-09-01 14:31:57 -0700 · Steve Abrams
Files touched
M server.jsM token-age-warn.mjs
Diff
commit e6b8c5530c9c7a4c93f0a121768529d3d81b3d06
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 1 14:31:57 2026 -0700
feat(auth): info@ → Internal client (staggered stage 2) + token-age validates Workspace accts with Internal client
---
server.js | 9 ++++++++-
token-age-warn.mjs | 28 +++++++++++++++++++++-------
2 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/server.js b/server.js
index b1a5637..9a11f25 100644
--- a/server.js
+++ b/server.js
@@ -144,6 +144,13 @@ try {
}
// Info@ account (secondary — now full Workspace)
+// Staggered stage 2 (2026-09-01): info@ moves to the new Internal OAuth client
+// (project 964770033153, george-workspace-internal) so its next re-consent mints
+// a durable token. Falls back to the existing client when Internal creds absent.
+// Both /auth/info (mint) + /oauth2callback info branch (exchange) reference this
+// same infoOauth2Client, so the mint/exchange-must-match invariant holds.
+const INFO_ID = process.env.GMAIL_INTERNAL_CLIENT_ID || creds.GMAIL_INTERNAL_CLIENT_ID || GMAIL_CLIENT_ID;
+const INFO_SECRET = process.env.GMAIL_INTERNAL_CLIENT_SECRET || creds.GMAIL_INTERNAL_CLIENT_SECRET || GMAIL_CLIENT_SECRET;
let infoOauth2Client;
let infoGmail;
let infoTasksApi;
@@ -154,7 +161,7 @@ let infoSlides;
let infoCalendar;
let infoForms;
try {
- infoOauth2Client = new google.auth.OAuth2(GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, INFO_REDIRECT_URI);
+ infoOauth2Client = new google.auth.OAuth2(INFO_ID, INFO_SECRET, INFO_REDIRECT_URI);
if (INFO_REFRESH_TOKEN) {
infoOauth2Client.setCredentials({ refresh_token: INFO_REFRESH_TOKEN });
infoGmail = google.gmail({ version: 'v1', auth: infoOauth2Client });
diff --git a/token-age-warn.mjs b/token-age-warn.mjs
index 1902425..64fb394 100644
--- a/token-age-warn.mjs
+++ b/token-age-warn.mjs
@@ -26,12 +26,18 @@ const DATA = `${HERE}data`; fs.mkdirSync(DATA, { recursive: true });
const STATE_FILE = `${DATA}/token-age-state.json`;
const WARN_DAYS = 5, DEAD_DAYS = 7; // Testing-mode refresh tokens die at 7d; nudge at day 5 for 2d runway
+// `internal: true` accounts were re-minted under the Internal OAuth client
+// (GMAIL_INTERNAL_CLIENT_ID/SECRET, staggered cutover: steve-office stage 1,
+// info@ stage 2 2026-09-01). They MUST be validated against the Internal client
+// or the probe returns invalid_grant and false-CRITs a healthy token. The rest
+// (personal/agentabrams/calendar) stay on GMAIL_CLIENT_ID. Falls back to the
+// GMAIL client when the Internal creds aren't present in env.
const ACCOUNTS = [
- { label: 'Steve Office (steve@dw)', key: 'GMAIL_REFRESH_TOKEN', workspace: true },
- { label: 'Info@ catch-all', key: 'INFO_REFRESH_TOKEN', workspace: true },
- { label: 'Steve Personal (@gmail)', key: 'PERSONAL_REFRESH_TOKEN', workspace: false },
- { label: 'Agent Abrams (@gmail)', key: 'AGENTABRAMS_REFRESH_TOKEN', workspace: false },
- { label: 'Calendar / Appointments', key: 'GOOGLE_CALENDAR_REFRESH_TOKEN', workspace: true },
+ { label: 'Steve Office (steve@dw)', key: 'GMAIL_REFRESH_TOKEN', workspace: true, internal: true },
+ { label: 'Info@ catch-all', key: 'INFO_REFRESH_TOKEN', workspace: true, internal: true },
+ { label: 'Steve Personal (@gmail)', key: 'PERSONAL_REFRESH_TOKEN', workspace: false, internal: false },
+ { label: 'Agent Abrams (@gmail)', key: 'AGENTABRAMS_REFRESH_TOKEN', workspace: false, internal: false },
+ { label: 'Calendar / Appointments', key: 'GOOGLE_CALENDAR_REFRESH_TOKEN', workspace: true, internal: false },
];
const RANK = { OK: 0, UNKNOWN: 0, WARN: 1, CRIT: 2 };
@@ -65,6 +71,7 @@ async function probeValid(clientId, clientSecret, refreshToken) {
const creds = loadEnv(ENV_PATH);
const clientId = creds.GMAIL_CLIENT_ID, clientSecret = creds.GMAIL_CLIENT_SECRET;
+const internalId = creds.GMAIL_INTERNAL_CLIENT_ID, internalSecret = creds.GMAIL_INTERNAL_CLIENT_SECRET;
const state = fs.existsSync(STATE_FILE) ? JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')) : {};
const now = Date.now();
const accounts = [];
@@ -79,12 +86,19 @@ for (const a of ACCOUNTS) {
const issued_ts = rotated ? now : (prev.issued_ts || now);
const ageDays = (now - issued_ts) / 86400000;
+ // Validate against the client the token was actually minted under: internal
+ // accounts use the Internal client (fall back to GMAIL if it's not in env yet),
+ // everyone else uses GMAIL_CLIENT_ID.
+ const useInternal = a.internal && internalId && internalSecret;
+ const cId = useInternal ? internalId : clientId;
+ const cSecret = useInternal ? internalSecret : clientSecret;
+
let valid = null, err;
- if (clientId && clientSecret) ({ valid, err } = await probeValid(clientId, clientSecret, tok));
+ if (cId && cSecret) ({ valid, err } = await probeValid(cId, cSecret, tok));
let level, detail;
if (valid === false) { level = 'CRIT'; detail = `token DEAD (invalid_grant) — re-consent needed`; }
- else if (valid === null && !clientId) { level = 'UNKNOWN'; detail = 'no client id/secret to probe'; }
+ else if (valid === null && !cId) { level = 'UNKNOWN'; detail = 'no client id/secret to probe'; }
else if (valid === null) { level = 'UNKNOWN'; detail = `probe inconclusive (${err})`; }
else if (ageDays >= DEAD_DAYS) { level = 'OK'; detail = `valid at ${ageDays.toFixed(1)}d — survived past the 7d Testing horizon, so this token is long-lived (Production/Internal/Workspace), not on the weekly clock`; }
else if (ageDays >= WARN_DAYS) { level = 'WARN'; detail = `valid, ${ageDays.toFixed(1)}d old — expires ~${(DEAD_DAYS - ageDays).toFixed(1)}d if app still in Testing`; }
← a3c366a Reapply "feat(auth): steve-office → Internal OAuth client (s
·
back to George Gmail
·
auto-data-snapshot: 2026-09-02T04:39:22 (1 data files) — dat 5378a85 →