fix(jwt): preserve client plugin inference with jwtClient (#10513)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Andreas Osberghaus
2026-07-27 16:25:27 +00:00
committed by GitHub
co-authored by Cursor
parent 2ebd766d60
commit e2c73fbec8
3 changed files with 64 additions and 4 deletions
@@ -0,0 +1,5 @@
---
"better-auth": patch
---
Fix `jwtClient()` collapsing `createAuthClient` type inference when combined with other client plugins such as `inferAdditionalFields`. Additional user fields (for example on `updateUser`) are preserved again.
+13 -3
View File
@@ -1,4 +1,10 @@
import type { BetterAuthClientPlugin } from "@better-auth/core";
import type {
BetterAuthClientOptions,
BetterAuthClientPlugin,
ClientFetchOption,
ClientStore,
} from "@better-auth/core";
import type { BetterFetch } from "@better-fetch/fetch";
import type { JSONWebKeySet } from "jose";
import { PACKAGE_VERSION } from "../../version";
import type { jwt } from "./index";
@@ -25,8 +31,12 @@ export const jwtClient = (options?: JwtClientOptions) => {
pathMethods: {
[jwksPath]: "GET",
},
getActions: ($fetch) => ({
jwks: async (fetchOptions?: any) => {
getActions: (
$fetch: BetterFetch,
_$store: ClientStore,
_options: BetterAuthClientOptions | undefined,
) => ({
jwks: async (fetchOptions?: ClientFetchOption) => {
return await $fetch<JSONWebKeySet>(jwksPath, {
method: "GET",
...fetchOptions,
@@ -1,8 +1,10 @@
import type { BetterAuthClientPlugin } from "@better-auth/core";
import type { JSONWebKeySet } from "jose";
import { createLocalJWKSet, jwtVerify } from "jose";
import { describe, expect, it } from "vitest";
import { describe, expect, expectTypeOf, it } from "vitest";
import { createAuthClient } from "../../client";
import { getTestInstance } from "../../test-utils/test-instance";
import { inferAdditionalFields } from "../additional-fields/client";
import { jwt } from ".";
import { jwtClient } from "./client";
import type { JWKOptions, Jwk, JwtOptions } from "./types";
@@ -833,3 +835,46 @@ describe("toExpJWT", () => {
});
});
});
/**
* Declaration emit previously narrowed `$fetch` from `$fetch<JSONWebKeySet>(...)`,
* making `jwtClient()` unassignable to `BetterAuthClientPlugin` and collapsing
* `createAuthClient` option inference when combined with other plugins.
*
* @see https://github.com/masumi-network/sokosumi/pull/3403
*/
describe("jwtClient types", () => {
it("should be assignable to BetterAuthClientPlugin", () => {
const plugin: BetterAuthClientPlugin = jwtClient();
const plugins: BetterAuthClientPlugin[] = [jwtClient()];
expect(plugin.id).toBe("better-auth-client");
expect(plugins).toHaveLength(1);
});
it("should preserve additional user fields when combined with inferAdditionalFields", () => {
const client = createAuthClient({
plugins: [
inferAdditionalFields({
user: {
logo: {
type: "string",
required: false,
},
},
}),
jwtClient(),
],
});
type UpdateUserBody = NonNullable<Parameters<typeof client.updateUser>[0]>;
const body = {
logo: "https://example.com/logo.png",
} satisfies UpdateUserBody;
expect(body.logo).toBe("https://example.com/logo.png");
expectTypeOf<UpdateUserBody>().toMatchTypeOf<{
logo?: string | null | undefined;
}>();
expectTypeOf(client.jwks).toBeFunction();
});
});