test(oauth): cover exchange-throw logging and guard against raw token leaks

This commit is contained in:
Bekacru
2026-09-30 15:15:15 -07:00
parent 892bdd6bbc
commit 16a8bfa1fa
@@ -17,7 +17,11 @@ describe("callback", () => {
validateAuthorizationCode: async () =>
// Mirrors a provider (e.g. Slack) that answers with HTTP 200 but an
// error in the response body instead of failing the HTTP request.
getOAuth2Tokens({ ok: false, error: "invalid_code" }),
getOAuth2Tokens({
ok: false,
error: "invalid_code",
refresh_token: "sensitive-refresh-token",
}),
getUserInfo: async () => null,
} satisfies OAuthProvider<Record<string, never>>;
@@ -70,6 +74,66 @@ describe("callback", () => {
});
// The raw token response must never be logged as-is.
expect(JSON.stringify(noTokenCalls[0])).not.toContain('"raw"');
expect(JSON.stringify(noTokenCalls[0])).not.toContain(
"sensitive-refresh-token",
);
errorSpy.mockRestore();
});
it("logs the provider id when the authorization code exchange throws", async () => {
const exchangeError = new Error("token endpoint unreachable");
const provider = {
id: "throwing-provider",
name: "Throwing Provider",
accountSubject: () => "subject",
createAuthorizationURL: ({ state }) =>
new URL(`https://idp.example.com/authorize?state=${state}`),
validateAuthorizationCode: async () => {
throw exchangeError;
},
getUserInfo: async () => null,
} satisfies OAuthProvider<Record<string, never>>;
const { client, auth, cookieSetter } = await getTestInstance({
plugins: [
{
id: "throwing-provider-plugin",
init: (ctx) => ({
context: {
socialProviders: [provider, ...ctx.socialProviders],
},
}),
},
],
});
const ctx = await auth.$context;
const errorSpy = vi.spyOn(ctx.logger, "error").mockImplementation(() => {});
const oAuthHeaders = new Headers();
const signIn = await client.signIn.social(
{
provider: provider.id,
callbackURL: "/dashboard",
},
{
throw: true,
onSuccess: cookieSetter(oAuthHeaders),
},
);
const state = new URL(signIn.url!).searchParams.get("state");
await client.$fetch(`/callback/${provider.id}?code=test&state=${state}`, {
method: "GET",
headers: oAuthHeaders,
onError() {},
});
expect(errorSpy).toHaveBeenCalledWith(
"Failed to exchange OAuth authorization code",
{ providerId: provider.id, error: exchangeError },
);
errorSpy.mockRestore();
});