mirror of
https://github.com/domfelipe/mika-agent-assist.git
synced 2026-08-07 15:56:50 +00:00
chore: add safe oauth provider diagnostics
This commit is contained in:
parent
c97da5f22a
commit
2506daddf6
1 changed files with 61 additions and 9 deletions
|
|
@ -23,6 +23,58 @@ export interface ProviderEnv {
|
||||||
redirectUri: string;
|
redirectUri: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeProviderLogValue(value: unknown): string | null {
|
||||||
|
if (typeof value !== "string") return null;
|
||||||
|
const normalized = value.replace(/\s+/g, " ").trim();
|
||||||
|
if (!normalized) return null;
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
.replace(/[A-Za-z0-9_-]{24,}/g, "[redacted]")
|
||||||
|
.slice(0, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logOAuthHttpFailure(
|
||||||
|
label: string,
|
||||||
|
res: Response,
|
||||||
|
tokenUrl: string,
|
||||||
|
redirectUri?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
let errorCode: string | null = null;
|
||||||
|
let errorDescription: string | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const contentType = res.headers.get("content-type") ?? "";
|
||||||
|
if (contentType.includes("application/json")) {
|
||||||
|
const payload = await res.json() as {
|
||||||
|
error?: unknown;
|
||||||
|
error_description?: unknown;
|
||||||
|
error_uri?: unknown;
|
||||||
|
};
|
||||||
|
errorCode = sanitizeProviderLogValue(payload.error);
|
||||||
|
errorDescription = sanitizeProviderLogValue(payload.error_description ?? payload.error_uri);
|
||||||
|
} else {
|
||||||
|
errorDescription = sanitizeProviderLogValue(await res.text());
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
errorDescription = "unreadable_error_payload";
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokenHost = "invalid_token_url";
|
||||||
|
try {
|
||||||
|
tokenHost = new URL(tokenUrl).host;
|
||||||
|
} catch (_) { /* keep fallback */ }
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
[
|
||||||
|
`${label} status=${res.status}`,
|
||||||
|
`error=${errorCode ?? "unknown"}`,
|
||||||
|
`description=${errorDescription ?? "unavailable"}`,
|
||||||
|
`token_host=${tokenHost}`,
|
||||||
|
redirectUri ? `redirect_uri=${redirectUri}` : null,
|
||||||
|
].filter(Boolean).join(" "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function readFirstEnv(keys: string[]): string {
|
function readFirstEnv(keys: string[]): string {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const value = Deno.env.get(key);
|
const value = Deno.env.get(key);
|
||||||
|
|
@ -116,7 +168,7 @@ export function buildAuthorizeUrl(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Troca o `code` por tokens. NUNCA loga response body.
|
* Troca o `code` por tokens. NUNCA loga response body bruto nem tokens.
|
||||||
*/
|
*/
|
||||||
export async function exchangeCodeForTokens(
|
export async function exchangeCodeForTokens(
|
||||||
slug: ProviderSlug,
|
slug: ProviderSlug,
|
||||||
|
|
@ -139,7 +191,7 @@ export async function exchangeCodeForTokens(
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`google token exchange status=${res.status}`);
|
await logOAuthHttpFailure("google token exchange", res, tokenUrl, env.redirectUri);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const tokens = await res.json() as {
|
const tokens = await res.json() as {
|
||||||
|
|
@ -186,7 +238,7 @@ export async function exchangeCodeForTokens(
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`notion token exchange status=${res.status}`);
|
await logOAuthHttpFailure("notion token exchange", res, tokenUrl, env.redirectUri);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const t = await res.json() as {
|
const t = await res.json() as {
|
||||||
|
|
@ -218,7 +270,7 @@ export async function exchangeCodeForTokens(
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`microsoft token exchange status=${res.status}`);
|
await logOAuthHttpFailure("microsoft token exchange", res, tokenUrl, env.redirectUri);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const t = await res.json() as {
|
const t = await res.json() as {
|
||||||
|
|
@ -263,7 +315,7 @@ export async function exchangeCodeForTokens(
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`calcom token exchange status=${res.status}`);
|
await logOAuthHttpFailure("calcom token exchange", res, tokenUrl, env.redirectUri);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const t = await res.json() as {
|
const t = await res.json() as {
|
||||||
|
|
@ -304,7 +356,7 @@ export async function exchangeCodeForTokens(
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`todoist token exchange status=${res.status}`);
|
await logOAuthHttpFailure("todoist token exchange", res, tokenUrl);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const t = await res.json() as { access_token: string };
|
const t = await res.json() as { access_token: string };
|
||||||
|
|
@ -332,7 +384,7 @@ export async function exchangeCodeForTokens(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh access_token usando refresh_token. NUNCA loga body.
|
* Refresh access_token usando refresh_token. NUNCA loga body bruto nem tokens.
|
||||||
*/
|
*/
|
||||||
export async function refreshAccessToken(
|
export async function refreshAccessToken(
|
||||||
slug: ProviderSlug,
|
slug: ProviderSlug,
|
||||||
|
|
@ -355,11 +407,11 @@ export async function refreshAccessToken(
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
if (res.status === 400 || res.status === 401) {
|
if (res.status === 400 || res.status === 401) {
|
||||||
console.error(`refresh ${slug} invalid_grant status=${res.status}`);
|
await logOAuthHttpFailure(`refresh ${slug} invalid_grant`, res, tokenUrl);
|
||||||
throw new Error("invalid_grant");
|
throw new Error("invalid_grant");
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`refresh ${slug} status=${res.status}`);
|
await logOAuthHttpFailure(`refresh ${slug}`, res, tokenUrl);
|
||||||
throw new Error("provider_error");
|
throw new Error("provider_error");
|
||||||
}
|
}
|
||||||
const t = await res.json() as {
|
const t = await res.json() as {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue