mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-22 22:32:17 +10:00
fix(auth): allow unlinking providers after the session ages past a day (#3364)
Better Auth guards `/unlink-account` with `freshSessionMiddleware`, which
rejects any session whose `createdAt` is older than `freshAge` (one day by
default). Sessions here last a week and there is no re-authentication flow to
refresh that timestamp, so disconnecting a provider failed with
`SESSION_NOT_FRESH` for every user who signed in more than a day ago.
Disable the freshness gate, and teach `getReadableErrorMessage` to read plain
error objects: Better Auth client errors are `{ code, message, status }`
objects rather than `Error` instances, so every auth toast was collapsing to
its generic fallback instead of showing the real reason.
This commit is contained in:
@@ -11,8 +11,16 @@ describe("getReadableErrorMessage", () => {
|
||||
expect(getReadableErrorMessage(new Error("boom"), "fallback")).toBe("boom");
|
||||
});
|
||||
|
||||
it("returns the message of a plain error object (Better Auth client errors)", () => {
|
||||
expect(getReadableErrorMessage({ code: "SESSION_NOT_FRESH", message: "Session is not fresh" }, "fallback")).toBe(
|
||||
"Session is not fresh",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns fallback for unknown shapes", () => {
|
||||
expect(getReadableErrorMessage({ random: "object" }, "fallback")).toBe("fallback");
|
||||
expect(getReadableErrorMessage({ message: "" }, "fallback")).toBe("fallback");
|
||||
expect(getReadableErrorMessage({ message: 42 }, "fallback")).toBe("fallback");
|
||||
expect(getReadableErrorMessage(null, "fallback")).toBe("fallback");
|
||||
expect(getReadableErrorMessage(undefined, "fallback")).toBe("fallback");
|
||||
expect(getReadableErrorMessage(42, "fallback")).toBe("fallback");
|
||||
|
||||
@@ -3,6 +3,11 @@ import { ORPCError } from "@orpc/client";
|
||||
export function getReadableErrorMessage(error: unknown, fallback: string): string {
|
||||
if (typeof error === "string" && error) return error;
|
||||
if (error instanceof Error && error.message) return error.message;
|
||||
// Better Auth client errors are plain objects ({ code, message, status }), not Error instances.
|
||||
if (typeof error === "object" && error !== null && "message" in error) {
|
||||
const { message } = error as { message?: unknown };
|
||||
if (typeof message === "string" && message) return message;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,3 +13,9 @@ describe("social provider signup policy", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("session freshness", () => {
|
||||
it("disables the freshness gate so provider unlinking works for week-old sessions", () => {
|
||||
expect(auth.options.session?.freshAge).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -203,6 +203,12 @@ const getAuthConfig = () => {
|
||||
},
|
||||
},
|
||||
|
||||
// Better Auth gates `/unlink-account` (and `/list-sessions`) behind a "fresh"
|
||||
// session, which defaults to one day old. Sessions here live for a week and
|
||||
// there is no re-authentication flow to refresh that timestamp, so disconnecting
|
||||
// a provider failed with `SESSION_NOT_FRESH` for anyone who signed in yesterday.
|
||||
session: { freshAge: 0 },
|
||||
|
||||
account: {
|
||||
accountLinking: {
|
||||
enabled: true,
|
||||
|
||||
Reference in New Issue
Block a user