question: What is remote url in jwt, and is it gone? #1867

Closed
opened 2026-03-13 09:09:30 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @rahinkore on GitHub (Sep 6, 2025).

I am using version 1.3.7(latest)

And it seems remoteUrl Is gone

I needed it for my jwks and OIDC provider

But to be clear. It means it will save all jwks to that file of choice right(example: https://example.com/.well-known/jwks.json)

If so how do I do that now?

My auth config:

jwt({
            jwks: {
                remoteUrl: "https://rhsstudio.rahinstestprisma.workers.dev/api/auth/oauth2/jwks",
                keyPairConfig: {
                  alg: 'ES256',
                },
              }
        }),
        // Make sure to add the JWT plugin
        oidcProvider({
            useJWTPlugin: true,
            loginPage: "/log-in",
            allowDynamicClientRegistration: false, // Disable for better performance
            // Configure trusted client for your game
            trustedClients: [
                {
                    clientId: getEnvVar('OPEN_ID_CLIENT_ID')!,
                    clientSecret: getEnvVar('OPEN_ID_CLIENT_SECRET') || "your-secure-secret-here", // Use environment variable
                    redirectURLs: ["http://127.0.0.1:27056/callback"], 
                    name: "RHS Game Client",
                    type: "web",
                    skipConsent: true, // Skip consent for your own game
                    metadata: {},
                    disabled: false,
                }
            ],
            // Customize OIDC metadata
            metadata: {
                issuer: getEnvVar('BETTER_AUTH_URL') || "https://your-domain.com", // Use environment variable
                authorization_endpoint: "/api/auth/oauth2/authorize",
                token_endpoint: "/api/auth/oauth2/token",
                userinfo_endpoint: "/api/auth/oauth2/userinfo",
                // jwks_uri: "/api/auth/oauth2/jwks",
                
            },
            // Performance optimizations
            accessTokenExpiresIn: 3600, // 1 hour
            refreshTokenExpiresIn: 2592000, // 30 days
        }),
        // emailOTP({
        //     overrideDefaultEmailVerification: true, 
        //     async sendVerificationOTP({ email, otp, type }) {
        //         if (type === "sign-in") { 
        //             // Send the OTP for sign in
        //         } else if (type === "email-verification") { 
        //             // Send the OTP for email verification
        //         } else if (type === "password-reset") { 
        //             // Send the OTP for password reset
        //         } 
        //     },
        // }),
    ],
Originally created by @rahinkore on GitHub (Sep 6, 2025). I am using version 1.3.7(latest) And it seems remoteUrl Is gone I needed it for my jwks and OIDC provider But to be clear. It means it will save all jwks to that file of choice right(example: https://example.com/.well-known/jwks.json) If so how do I do that now? My auth config: ``` jwt({ jwks: { remoteUrl: "https://rhsstudio.rahinstestprisma.workers.dev/api/auth/oauth2/jwks", keyPairConfig: { alg: 'ES256', }, } }), // Make sure to add the JWT plugin oidcProvider({ useJWTPlugin: true, loginPage: "/log-in", allowDynamicClientRegistration: false, // Disable for better performance // Configure trusted client for your game trustedClients: [ { clientId: getEnvVar('OPEN_ID_CLIENT_ID')!, clientSecret: getEnvVar('OPEN_ID_CLIENT_SECRET') || "your-secure-secret-here", // Use environment variable redirectURLs: ["http://127.0.0.1:27056/callback"], name: "RHS Game Client", type: "web", skipConsent: true, // Skip consent for your own game metadata: {}, disabled: false, } ], // Customize OIDC metadata metadata: { issuer: getEnvVar('BETTER_AUTH_URL') || "https://your-domain.com", // Use environment variable authorization_endpoint: "/api/auth/oauth2/authorize", token_endpoint: "/api/auth/oauth2/token", userinfo_endpoint: "/api/auth/oauth2/userinfo", // jwks_uri: "/api/auth/oauth2/jwks", }, // Performance optimizations accessTokenExpiresIn: 3600, // 1 hour refreshTokenExpiresIn: 2592000, // 30 days }), // emailOTP({ // overrideDefaultEmailVerification: true, // async sendVerificationOTP({ email, otp, type }) { // if (type === "sign-in") { // // Send the OTP for sign in // } else if (type === "email-verification") { // // Send the OTP for email verification // } else if (type === "password-reset") { // // Send the OTP for password reset // } // }, // }), ], ```
GiteaMirror added the question label 2026-03-13 09:09:30 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

The remoteUrl option in the JWT plugin was used to point to an external JWKS endpoint (like https://example.com/.well-known/jwks.json). It did not save JWKS to a file; instead, it told the system to fetch keys from that remote endpoint and disabled the local /jwks route. This was useful if your JWKS were managed elsewhere or served from a CDN, and OIDC discovery would reference the remote URL instead of the local endpoint [source].

In version 1.3.7, the remoteUrl option has been removed from the standard JWT plugin configuration [source]. Now, JWKS keys are managed internally and served dynamically via endpoints (like /.well-known/jwks.json) through the OIDC provider plugin. There is no built-in way to save JWKS to a file or push them to a remote URL; if you need a remote JWKS endpoint, you must host it yourself and update your OIDC metadata accordingly [source].

If you need to use a remote JWKS for custom JWT signing (using a custom jwt.sign function), you must set jwks.remoteUrl—but this is now only for remote signing, not for saving JWKS to a file [source].

For most use cases, the recommended approach is to use the OIDC provider plugin, which exposes JWKS dynamically at an endpoint. If you want to serve a static JWKS file, you'll need to manage that outside of better-auth.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> The `remoteUrl` option in the JWT plugin was used to point to an external JWKS endpoint (like `https://example.com/.well-known/jwks.json`). It did not save JWKS to a file; instead, it told the system to fetch keys from that remote endpoint and disabled the local `/jwks` route. This was useful if your JWKS were managed elsewhere or served from a CDN, and OIDC discovery would reference the remote URL instead of the local endpoint [[source]](https://github.com/better-auth/better-auth/pull/4071). In version 1.3.7, the `remoteUrl` option has been removed from the standard JWT plugin configuration [[source]](https://github.com/better-auth/better-auth/pull/4161). Now, JWKS keys are managed internally and served dynamically via endpoints (like `/.well-known/jwks.json`) through the OIDC provider plugin. There is no built-in way to save JWKS to a file or push them to a remote URL; if you need a remote JWKS endpoint, you must host it yourself and update your OIDC metadata accordingly [[source]](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/docs/content/docs/plugins/oidc-provider.mdx). If you need to use a remote JWKS for custom JWT signing (using a custom `jwt.sign` function), you must set `jwks.remoteUrl`—but this is now only for remote signing, not for saving JWKS to a file [[source]](https://github.com/better-auth/better-auth/pull/4074). For most use cases, the recommended approach is to use the OIDC provider plugin, which exposes JWKS dynamically at an endpoint. If you want to serve a static JWKS file, you'll need to manage that outside of better-auth. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/f9b29746-972f-45a2-859d-5ffcda45babe?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

Dynamic isn't an issue,

Can't i just make an endpoint like /api/get-jwks

where it will return a full json?

@rahinkore commented on GitHub (Sep 6, 2025): @dosu Dynamic isn't an issue, Can't i just make an endpoint like /api/get-jwks where it will return a full json?
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

Yes, you can expose JWKS as JSON at a custom endpoint like /api/get-jwks—but better-auth already serves JWKS dynamically at the standard /jwks endpoint via the JWT and OIDC provider plugins, which is OIDC-compliant and integrated with your setup [source].

If you specifically want a different route, you can replicate the logic from the JWT plugin's /jwks handler: use the JWKS adapter to fetch all keys and return them as JSON. The plugin architecture supports defining custom endpoints using createAuthEndpoint, so you could do something like:

createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => {
  const adapter = getJwksAdapter(ctx.context.adapter);
  const keySets = await adapter.getAllKeys();
  if (keySets.length === 0) {
    const key = await createJwk(ctx, options);
    keySets.push(key);
  }
  return ctx.json({
    keys: keySets.map((keySet) => ({
      alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA",
      crv: keySet.crv,
      ...JSON.parse(keySet.publicKey),
      kid: keySet.id,
    })),
  });
});

There isn't a public API for fetching JWKS outside the built-in endpoints, but you can access the JWKS data using internal utilities and plugin state if needed [source]. Unless you have a specific reason for a custom route, using the standard /jwks endpoint is recommended.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> Yes, you can expose JWKS as JSON at a custom endpoint like `/api/get-jwks`—but better-auth already serves JWKS dynamically at the standard `/jwks` endpoint via the JWT and OIDC provider plugins, which is OIDC-compliant and integrated with your setup [[source]](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts). If you specifically want a different route, you can replicate the logic from the JWT plugin's `/jwks` handler: use the JWKS adapter to fetch all keys and return them as JSON. The plugin architecture supports defining custom endpoints using `createAuthEndpoint`, so you could do something like: ```ts createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => { const adapter = getJwksAdapter(ctx.context.adapter); const keySets = await adapter.getAllKeys(); if (keySets.length === 0) { const key = await createJwk(ctx, options); keySets.push(key); } return ctx.json({ keys: keySets.map((keySet) => ({ alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA", crv: keySet.crv, ...JSON.parse(keySet.publicKey), kid: keySet.id, })), }); }); ``` There isn't a public API for fetching JWKS outside the built-in endpoints, but you can access the JWKS data using internal utilities and plugin state if needed [[source]](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts). Unless you have a specific reason for a custom route, using the standard `/jwks` endpoint is recommended. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/41b8dd7f-4fc5-458b-b3e8-c43993b3bbb8?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

If you could, please provide me a full code for tanstack start api route of this

including imports

@rahinkore commented on GitHub (Sep 6, 2025): @dosu If you could, please provide me a full code for tanstack start api route of this including imports
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

Here’s a full example of a TanStack Start API route that serves JWKS as JSON at /api/get-jwks, using better-auth v1.3.7. This assumes you have access to your better-auth adapter and config in the route context. If you’re using a custom adapter or config, adjust accordingly.

// src/routes/api/get-jwks.ts
import { getJwksAdapter } from "better-auth/plugins/jwt/adapter";
import { createJwk } from "better-auth/plugins/jwt/utils";
import type { JSONWebKeySet } from "jose";
import type { APIContext } from "@tanstack/start"; // Adjust if your context type is different

// Import your better-auth config and adapter as used in your app
import { authOptions, adapter } from "~/auth"; // Adjust the import path as needed

export async function GET(ctx: APIContext) {
  // Use your adapter and config here
  const jwksAdapter = getJwksAdapter(adapter);

  let keySets = await jwksAdapter.getAllKeys();

  if (keySets.length === 0) {
    const key = await createJwk(
      { context: { adapter } }, // minimal context shape for createJwk
      authOptions
    );
    keySets.push(key);
  }

  const keyPairConfig = authOptions?.jwks?.keyPairConfig;
  const defaultCrv =
    keyPairConfig && "crv" in keyPairConfig
      ? (keyPairConfig as { crv: string }).crv
      : undefined;

  const keys = keySets.map((keySet) => ({
    alg: keySet.alg ?? authOptions?.jwks?.keyPairConfig?.alg ?? "EdDSA",
    crv: keySet.crv ?? defaultCrv,
    ...JSON.parse(keySet.publicKey),
    kid: keySet.id,
  }));

  return new Response(JSON.stringify({ keys } satisfies JSONWebKeySet), {
    headers: { "Content-Type": "application/json" },
  });
}

You may need to tweak the imports for your project structure, but this matches the logic from the built-in /jwks endpoint in better-auth v1.3.7 and works with TanStack Start’s API route conventions. If your adapter or config is initialized differently, just update the relevant lines.
Reference: better-auth JWT plugin source

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> Here’s a full example of a TanStack Start API route that serves JWKS as JSON at `/api/get-jwks`, using better-auth v1.3.7. This assumes you have access to your better-auth adapter and config in the route context. If you’re using a custom adapter or config, adjust accordingly. ```ts // src/routes/api/get-jwks.ts import { getJwksAdapter } from "better-auth/plugins/jwt/adapter"; import { createJwk } from "better-auth/plugins/jwt/utils"; import type { JSONWebKeySet } from "jose"; import type { APIContext } from "@tanstack/start"; // Adjust if your context type is different // Import your better-auth config and adapter as used in your app import { authOptions, adapter } from "~/auth"; // Adjust the import path as needed export async function GET(ctx: APIContext) { // Use your adapter and config here const jwksAdapter = getJwksAdapter(adapter); let keySets = await jwksAdapter.getAllKeys(); if (keySets.length === 0) { const key = await createJwk( { context: { adapter } }, // minimal context shape for createJwk authOptions ); keySets.push(key); } const keyPairConfig = authOptions?.jwks?.keyPairConfig; const defaultCrv = keyPairConfig && "crv" in keyPairConfig ? (keyPairConfig as { crv: string }).crv : undefined; const keys = keySets.map((keySet) => ({ alg: keySet.alg ?? authOptions?.jwks?.keyPairConfig?.alg ?? "EdDSA", crv: keySet.crv ?? defaultCrv, ...JSON.parse(keySet.publicKey), kid: keySet.id, })); return new Response(JSON.stringify({ keys } satisfies JSONWebKeySet), { headers: { "Content-Type": "application/json" }, }); } ``` You may need to tweak the imports for your project structure, but this matches the logic from the built-in `/jwks` endpoint in better-auth v1.3.7 and works with TanStack Start’s API route conventions. If your adapter or config is initialized differently, just update the relevant lines. Reference: [better-auth JWT plugin source](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/54f59663-589a-4900-a505-6d931bff624e?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

[{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'getJwksAdapter'.",
"source": "ts",
"startLineNumber": 10,
"startColumn": 21,
"endLineNumber": 10,
"endColumn": 35,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'createJwk'.",
"source": "ts",
"startLineNumber": 13,
"startColumn": 25,
"endLineNumber": 13,
"endColumn": 34,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'options'.",
"source": "ts",
"startLineNumber": 13,
"startColumn": 40,
"endLineNumber": 13,
"endColumn": 47,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "7006",
"severity": 8,
"message": "Parameter 'keySet' implicitly has an 'any' type.",
"source": "ts",
"startLineNumber": 17,
"startColumn": 26,
"endLineNumber": 17,
"endColumn": 32,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'options'.",
"source": "ts",
"startLineNumber": 18,
"startColumn": 28,
"endLineNumber": 18,
"endColumn": 35,
"modelVersionId": 211
}]

I got this error on:
code

// src/routes/api/get-jwks.ts
import { createAuthEndpoint } from "better-auth/plugins";
import { auth } from "@/lib/auth"; // wherever your auth.ts lives
import { createServerFileRoute } from "@tanstack/react-start/server";

// Create an endpoint using better-auth's helper


export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => {
    const adapter = getJwksAdapter(ctx.context.adapter);
    const keySets = await adapter.getAllKeys();
    if (keySets.length === 0) {
      const key = await createJwk(ctx, options);
      keySets.push(key);
    }
    return ctx.json({
      keys: keySets.map((keySet) => ({
        alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA",
        crv: keySet.crv,
        ...JSON.parse(keySet.publicKey),
        kid: keySet.id,
      })),
    });
  });
@rahinkore commented on GitHub (Sep 6, 2025): @dosu [{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'getJwksAdapter'.", "source": "ts", "startLineNumber": 10, "startColumn": 21, "endLineNumber": 10, "endColumn": 35, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'createJwk'.", "source": "ts", "startLineNumber": 13, "startColumn": 25, "endLineNumber": 13, "endColumn": 34, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'options'.", "source": "ts", "startLineNumber": 13, "startColumn": 40, "endLineNumber": 13, "endColumn": 47, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "7006", "severity": 8, "message": "Parameter 'keySet' implicitly has an 'any' type.", "source": "ts", "startLineNumber": 17, "startColumn": 26, "endLineNumber": 17, "endColumn": 32, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'options'.", "source": "ts", "startLineNumber": 18, "startColumn": 28, "endLineNumber": 18, "endColumn": 35, "modelVersionId": 211 }] I got this error on: code ``` // src/routes/api/get-jwks.ts import { createAuthEndpoint } from "better-auth/plugins"; import { auth } from "@/lib/auth"; // wherever your auth.ts lives import { createServerFileRoute } from "@tanstack/react-start/server"; // Create an endpoint using better-auth's helper export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => { const adapter = getJwksAdapter(ctx.context.adapter); const keySets = await adapter.getAllKeys(); if (keySets.length === 0) { const key = await createJwk(ctx, options); keySets.push(key); } return ctx.json({ keys: keySets.map((keySet) => ({ alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA", crv: keySet.crv, ...JSON.parse(keySet.publicKey), kid: keySet.id, })), }); }); ```
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

[{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'getJwksAdapter'.",
"source": "ts",
"startLineNumber": 10,
"startColumn": 21,
"endLineNumber": 10,
"endColumn": 35,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'createJwk'.",
"source": "ts",
"startLineNumber": 13,
"startColumn": 25,
"endLineNumber": 13,
"endColumn": 34,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'options'.",
"source": "ts",
"startLineNumber": 13,
"startColumn": 40,
"endLineNumber": 13,
"endColumn": 47,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "7006",
"severity": 8,
"message": "Parameter 'keySet' implicitly has an 'any' type.",
"source": "ts",
"startLineNumber": 17,
"startColumn": 26,
"endLineNumber": 17,
"endColumn": 32,
"modelVersionId": 211
},{
"resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts",
"owner": "typescript",
"code": "2304",
"severity": 8,
"message": "Cannot find name 'options'.",
"source": "ts",
"startLineNumber": 18,
"startColumn": 28,
"endLineNumber": 18,
"endColumn": 35,
"modelVersionId": 211
}]

I got this error on:
code

// src/routes/api/get-jwks.ts
import { createAuthEndpoint } from "better-auth/plugins";
import { auth } from "@/lib/auth"; // wherever your auth.ts lives
import { createServerFileRoute } from "@tanstack/react-start/server";

// Create an endpoint using better-auth's helper


export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => {
    const adapter = getJwksAdapter(ctx.context.adapter);
    const keySets = await adapter.getAllKeys();
    if (keySets.length === 0) {
      const key = await createJwk(ctx, options);
      keySets.push(key);
    }
    return ctx.json({
      keys: keySets.map((keySet) => ({
        alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA",
        crv: keySet.crv,
        ...JSON.parse(keySet.publicKey),
        kid: keySet.id,
      })),
    });
  });
@rahinkore commented on GitHub (Sep 6, 2025): @dosu [{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'getJwksAdapter'.", "source": "ts", "startLineNumber": 10, "startColumn": 21, "endLineNumber": 10, "endColumn": 35, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'createJwk'.", "source": "ts", "startLineNumber": 13, "startColumn": 25, "endLineNumber": 13, "endColumn": 34, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'options'.", "source": "ts", "startLineNumber": 13, "startColumn": 40, "endLineNumber": 13, "endColumn": 47, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "7006", "severity": 8, "message": "Parameter 'keySet' implicitly has an 'any' type.", "source": "ts", "startLineNumber": 17, "startColumn": 26, "endLineNumber": 17, "endColumn": 32, "modelVersionId": 211 },{ "resource": "/d:/RHS Studio/rhs-studio-website/src/routes/api/get-jwks.ts", "owner": "typescript", "code": "2304", "severity": 8, "message": "Cannot find name 'options'.", "source": "ts", "startLineNumber": 18, "startColumn": 28, "endLineNumber": 18, "endColumn": 35, "modelVersionId": 211 }] I got this error on: code ``` // src/routes/api/get-jwks.ts import { createAuthEndpoint } from "better-auth/plugins"; import { auth } from "@/lib/auth"; // wherever your auth.ts lives import { createServerFileRoute } from "@tanstack/react-start/server"; // Create an endpoint using better-auth's helper export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => { const adapter = getJwksAdapter(ctx.context.adapter); const keySets = await adapter.getAllKeys(); if (keySets.length === 0) { const key = await createJwk(ctx, options); keySets.push(key); } return ctx.json({ keys: keySets.map((keySet) => ({ alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA", crv: keySet.crv, ...JSON.parse(keySet.publicKey), kid: keySet.id, })), }); }); ```
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

You’re getting those TypeScript errors because getJwksAdapter and createJwk aren’t imported, and options isn’t defined in your file. Here’s how to fix it:

  • Import getJwksAdapter from better-auth/plugins/jwt/adapter and createJwk from better-auth/plugins/jwt/utils.
  • options should be your JWT plugin config—import it (often called authOptions or similar) from your auth setup.
  • Make sure you’re passing the correct adapter and options to these helpers.

Here’s a corrected example:

import { createAuthEndpoint } from "better-auth/plugins";
import { getJwksAdapter } from "better-auth/plugins/jwt/adapter";
import { createJwk } from "better-auth/plugins/jwt/utils";
import type { JSONWebKeySet } from "jose";
// Import your adapter and JWT options/config
import { adapter, authOptions } from "@/lib/auth"; // adjust path as needed

export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => {
  const jwksAdapter = getJwksAdapter(adapter);

  let keySets = await jwksAdapter.getAllKeys();

  if (keySets.length === 0) {
    const key = await createJwk({ context: { adapter, secret: ctx.context.secret } }, authOptions);
    keySets.push(key);
  }

  const keyPairConfig = authOptions?.jwks?.keyPairConfig;
  const defaultCrv = keyPairConfig && "crv" in keyPairConfig ? keyPairConfig.crv : undefined;

  return ctx.json({
    keys: keySets.map((keySet: any) => ({
      alg: keySet.alg ?? authOptions?.jwks?.keyPairConfig?.alg ?? "EdDSA",
      crv: keySet.crv ?? defaultCrv,
      ...JSON.parse(keySet.publicKey),
      kid: keySet.id,
    })),
  } satisfies JSONWebKeySet);
});

If your adapter or config are named differently, adjust the imports and usage accordingly. This matches the internal logic used by better-auth’s built-in /jwks endpoint and should resolve your errors.
Reference: better-auth JWT plugin source

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> You’re getting those TypeScript errors because getJwksAdapter and createJwk aren’t imported, and options isn’t defined in your file. Here’s how to fix it: - Import getJwksAdapter from better-auth/plugins/jwt/adapter and createJwk from better-auth/plugins/jwt/utils. - options should be your JWT plugin config—import it (often called authOptions or similar) from your auth setup. - Make sure you’re passing the correct adapter and options to these helpers. Here’s a corrected example: ```ts import { createAuthEndpoint } from "better-auth/plugins"; import { getJwksAdapter } from "better-auth/plugins/jwt/adapter"; import { createJwk } from "better-auth/plugins/jwt/utils"; import type { JSONWebKeySet } from "jose"; // Import your adapter and JWT options/config import { adapter, authOptions } from "@/lib/auth"; // adjust path as needed export const ServerRoute = createAuthEndpoint("/api/get-jwks", { method: "GET" }, async (ctx) => { const jwksAdapter = getJwksAdapter(adapter); let keySets = await jwksAdapter.getAllKeys(); if (keySets.length === 0) { const key = await createJwk({ context: { adapter, secret: ctx.context.secret } }, authOptions); keySets.push(key); } const keyPairConfig = authOptions?.jwks?.keyPairConfig; const defaultCrv = keyPairConfig && "crv" in keyPairConfig ? keyPairConfig.crv : undefined; return ctx.json({ keys: keySets.map((keySet: any) => ({ alg: keySet.alg ?? authOptions?.jwks?.keyPairConfig?.alg ?? "EdDSA", crv: keySet.crv ?? defaultCrv, ...JSON.parse(keySet.publicKey), kid: keySet.id, })), } satisfies JSONWebKeySet); }); ``` If your adapter or config are named differently, adjust the imports and usage accordingly. This matches the internal logic used by better-auth’s built-in /jwks endpoint and should resolve your errors. [Reference: better-auth JWT plugin source](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/93ba7525-944a-4b16-a593-d8cca83fd0be?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

better-auth/plugins/jwt/adapter Doesn;t exist

@rahinkore commented on GitHub (Sep 6, 2025): @dosu better-auth/plugins/jwt/adapter Doesn;t exist
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

You can't import better-auth/plugins/jwt/adapter or getJwksAdapter because those are internal to the better-auth source and not part of the published package's public API. The same goes for createJwk and other internal helpers—these are only used inside the library itself and aren't available for userland imports, which is why you get those TypeScript errors. This is a known limitation of the current version [source].

The only officially supported way to serve JWKS in better-auth v1.3.7 is through the built-in /jwks endpoint provided by the JWT plugin. There is no public API for accessing JWKS keys or replicating the /jwks endpoint logic in a custom route [source].

If you need your JWKS at a different route (like /api/get-jwks), the safest approach is to proxy the built-in /jwks endpoint. For example, in TanStack Start, you could do:

// src/routes/api/get-jwks.ts
export const GET = async () => {
  const res = await fetch("http://localhost:YOUR_PORT/jwks"); // adjust base URL/port as needed
  const body = await res.text();
  return new Response(body, {
    status: res.status,
    headers: { "Content-Type": "application/json" },
  });
};

This way, you don't rely on any internal APIs that could change or break in future versions. If you need more control, you'd have to copy the internal logic from the better-auth source, but that's not recommended or supported.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> You can't import better-auth/plugins/jwt/adapter or getJwksAdapter because those are internal to the better-auth source and not part of the published package's public API. The same goes for createJwk and other internal helpers—these are only used inside the library itself and aren't available for userland imports, which is why you get those TypeScript errors. This is a known limitation of the current version [[source]](https://github.com/better-auth/better-auth/pull/4161). The only officially supported way to serve JWKS in better-auth v1.3.7 is through the built-in /jwks endpoint provided by the JWT plugin. There is no public API for accessing JWKS keys or replicating the /jwks endpoint logic in a custom route [[source]](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts). If you need your JWKS at a different route (like /api/get-jwks), the safest approach is to proxy the built-in /jwks endpoint. For example, in TanStack Start, you could do: ```ts // src/routes/api/get-jwks.ts export const GET = async () => { const res = await fetch("http://localhost:YOUR_PORT/jwks"); // adjust base URL/port as needed const body = await res.text(); return new Response(body, { status: res.status, headers: { "Content-Type": "application/json" }, }); }; ``` This way, you don't rely on any internal APIs that could change or break in future versions. If you need more control, you'd have to copy the internal logic from the better-auth source, but that's not recommended or supported. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/3138e616-3706-48da-8379-fe0d569060ba?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@rahinkore commented on GitHub (Sep 6, 2025):

@dosu

/jwks is giving error 404

it doesn't exist

@rahinkore commented on GitHub (Sep 6, 2025): @dosu /jwks is giving error 404 it doesn't exist
Author
Owner

@dosubot[bot] commented on GitHub (Sep 6, 2025):

The /jwks endpoint will return a 404 if you have jwks.remoteUrl set in your JWT plugin config—this is by design. When remoteUrl is set, better-auth disables the local /jwks endpoint and expects you to use the remote JWKS URL instead. If you want the built-in JWKS endpoint to work, remove the remoteUrl option from your JWT plugin config and restart your server. Also, make sure you are accessing the correct path: in most setups, the JWKS endpoint is at /api/auth/jwks, not just /jwks.

If you need to serve JWKS from your own server, keep remoteUrl unset and use the default JWT plugin. If you want to use a remote JWKS, keep remoteUrl set and fetch keys from that remote URL—just know that the local /jwks endpoint will not exist in this mode.
Reference

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 6, 2025): <!-- Answer --> The /jwks endpoint will return a 404 if you have jwks.remoteUrl set in your JWT plugin config—this is by design. When remoteUrl is set, better-auth disables the local /jwks endpoint and expects you to use the remote JWKS URL instead. If you want the built-in JWKS endpoint to work, remove the remoteUrl option from your JWT plugin config and restart your server. Also, make sure you are accessing the correct path: in most setups, the JWKS endpoint is at /api/auth/jwks, not just /jwks. If you need to serve JWKS from your own server, keep remoteUrl unset and use the default JWT plugin. If you want to use a remote JWKS, keep remoteUrl set and fetch keys from that remote URL—just know that the local /jwks endpoint will not exist in this mode. [Reference](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/plugins/jwt/index.ts) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/809a44d7-fc06-44d3-ac83-f2f382a96351?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4488)
Author
Owner

@dvanmali commented on GitHub (Sep 7, 2025):

@rahinkore remove remoteUrl from your config. This is only used when your public jwks are located on a cdn as a file. Instead, the /jwks endpoint fetches it from the database which I believe is your case.

Additionally, remove the metadata section from the config to use its defaults.

@dvanmali commented on GitHub (Sep 7, 2025): @rahinkore remove `remoteUrl` from your config. This is only used when your public jwks are located on a cdn as a file. Instead, the `/jwks` endpoint fetches it from the database which I believe is your case. Additionally, remove the `metadata` section from the config to use its defaults.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1867