From c35c41ef2addf16c826babc7e83a358838d7de4e Mon Sep 17 00:00:00 2001 From: Taesu <166604494+bytaesu@users.noreply.github.com> Date: Wed, 12 Aug 2026 18:25:44 +0000 Subject: [PATCH] docs: rewrite Hono integration guide (#10763) --- docs/content/docs/integrations/hono.mdx | 279 ++++++++++++------------ 1 file changed, 140 insertions(+), 139 deletions(-) diff --git a/docs/content/docs/integrations/hono.mdx b/docs/content/docs/integrations/hono.mdx index 9679c06085..e2e8c68017 100644 --- a/docs/content/docs/integrations/hono.mdx +++ b/docs/content/docs/integrations/hono.mdx @@ -3,185 +3,186 @@ title: Hono Integration description: Integrate Better Auth with Hono. --- -Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation). +Both Hono and Better Auth use the Web Standard `Request` and `Response` APIs, so you can mount Better Auth directly in a Hono application without an adapter. -### Mount the handler +Before you begin, make sure you have a Better Auth instance configured. If not, follow the [installation guide](/docs/installation). -We need to mount the handler to Hono endpoint. +## Setup -```ts +### Create a Hono App + +If you are starting a new project, create a Hono application and select the template for your runtime or platform: + +```package-install +npx create-hono@latest +``` + +If you already have a Hono application, you can skip this step. See the [create-hono documentation](https://hono.dev/docs/guides/create-hono) for available templates and options, or Hono's [Getting Started guides](https://hono.dev/docs/getting-started/basic) for runtime-specific setup and deployment. + +### Mount the Auth Handler + +Add the highlighted route to your existing Hono application: + +```ts title="src/index.ts" import { Hono } from "hono"; import { auth } from "./auth"; -import { serve } from "@hono/node-server"; const app = new Hono(); -app.on(["POST", "GET"], "/api/auth/*", (c) => { - return auth.handler(c.req.raw); -}); +app.all("/api/auth/*", (c) => auth.handler(c.req.raw)); // [!code highlight] -serve(app); +export default app; ``` -### Cors +That's all you need to connect Better Auth to Hono! -To configure cors, you need to use the `cors` plugin from `hono/cors`. +If you're using another runtime, keep your existing entry point and add the same `app.all()` route. -```ts +`app.all()` forwards every HTTP method to Better Auth using the raw Web Standard `Request` from `c.req.raw`. Better Auth validates the method and returns a `Response` that Hono sends directly. Register the auth route before any catch-all route that could handle the request first. + + + The resulting Hono route must match your Better Auth `basePath`, which + defaults to `/api/auth`. If your app already uses `new + Hono().basePath("/api")`, mount Better Auth at `/auth/*`: + + ```ts + const app = new Hono().basePath("/api"); + + app.all("/auth/*", (c) => auth.handler(c.req.raw)); + ``` + + +### Additional Configuration + +#### Cloudflare Workers + +Better Auth uses `AsyncLocalStorage`. Add the [`nodejs_compat` compatibility flag](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag) to your Wrangler configuration: + +```jsonc title="wrangler.jsonc" +{ + // Use "nodejs_als" instead if you only need AsyncLocalStorage. + "compatibility_flags": ["nodejs_compat"], +} +``` + +## CORS + +To allow cross-origin authentication requests, register Hono's [CORS middleware](https://hono.dev/docs/middleware/builtin/cors) before the Better Auth route: + +```ts title="src/index.ts" import { Hono } from "hono"; +import { cors } from "hono/cors"; // [!code highlight] import { auth } from "./auth"; -import { serve } from "@hono/node-server"; -import { cors } from "hono/cors"; - + const app = new Hono(); -app.use( - "/api/auth/*", // or replace with "*" to enable cors for all routes - cors({ - origin: "http://localhost:3001", // replace with your origin - allowHeaders: ["Content-Type", "Authorization"], - allowMethods: ["POST", "GET", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, - }), -); +app.use( // [!code highlight] + "/api/auth/*", // [!code highlight] + cors({ // [!code highlight] + origin: "http://localhost:3001", // [!code highlight] + credentials: true, // [!code highlight] + }), // [!code highlight] +); // [!code highlight] -app.on(["POST", "GET"], "/api/auth/*", (c) => { - return auth.handler(c.req.raw); -}); +app.all("/api/auth/*", (c) => auth.handler(c.req.raw)); -serve(app); +export default app; ``` -> **Important:** CORS middleware must be registered before your routes. This ensures that cross-origin requests are properly handled before they reach your authentication endpoints. +Add the same origin to Better Auth's `trustedOrigins` configuration: -### Middleware +```ts title="auth.ts" +import { betterAuth } from "better-auth"; -You can add a middleware to save the `session` and `user` in a `context` and also add validations for every route. +export const auth = betterAuth({ + trustedOrigins: ["http://localhost:3001"], // [!code highlight] +}); +``` -```ts -import { Hono } from "hono"; +When `credentials` is enabled, configure an explicit CORS origin instead of `*` and add the same origin to Better Auth's `trustedOrigins`. See [Cookies](/docs/concepts/cookies) for cross-domain and cross-subdomain cookie behavior. + +## Middleware + +You can also use custom middleware to make the current session available through Hono's context: + +```ts title="src/session-middleware.ts" +import { createMiddleware } from "hono/factory"; import { auth } from "./auth"; -import { serve } from "@hono/node-server"; -import { cors } from "hono/cors"; - -const app = new Hono<{ + +type Env = { Variables: { - user: typeof auth.$Infer.Session.user | null; - session: typeof auth.$Infer.Session.session | null - } -}>(); + session: typeof auth.$Infer.Session | null; + }; +}; -app.use("*", async (c, next) => { - const session = await auth.api.getSession({ headers: c.req.raw.headers }); - - if (!session) { - c.set("user", null); - c.set("session", null); - await next(); - return; - } - - c.set("user", session.user); - c.set("session", session.session); - await next(); -}); - -app.on(["POST", "GET"], "/api/auth/*", (c) => { - return auth.handler(c.req.raw); -}); - - -serve(app); -``` - -This will allow you to access the `user` and `session` object in all of your routes. - -```ts -app.get("/session", (c) => { - const session = c.get("session") - const user = c.get("user") - - if(!user) return c.body(null, 401); - - return c.json({ - session, - user +export const sessionMiddleware = createMiddleware(async (c, next) => { + const session = await auth.api.getSession({ + headers: c.req.raw.headers, }); + + c.set("session", session); + + await next(); }); ``` -### Cross-Domain Cookies +Add the middleware to any route that needs access to the current session: -By default, all Better Auth cookies are set with `SameSite=Lax`. If you need to use cookies across different domains, you’ll need to set `SameSite=None` and `Secure=true`. However, we recommend using subdomains whenever possible, as this allows you to keep `SameSite=Lax`. To enable cross-subdomain cookies, simply turn on `crossSubDomainCookies` in your auth config. +```ts title="src/index.ts" +import { Hono } from "hono"; +import { HTTPException } from "hono/http-exception"; +import { sessionMiddleware } from "./session-middleware"; -```ts title="auth.ts" -export const auth = createAuth({ - advanced: { - crossSubDomainCookies: { - enabled: true - } - } -}) -``` +const app = new Hono(); -If you still need to set `SameSite=None` and `Secure=true`, you can adjust these attributes globally through `cookieOptions` in the `createAuth` configuration. +app.get("/hello", sessionMiddleware, (c) => { + const session = c.get("session"); -```ts title="auth.ts" -export const auth = createAuth({ - advanced: { - defaultCookieAttributes: { - sameSite: "none", - secure: true, - partitioned: true // New browser standards will mandate this for foreign cookies - } - } -}) -``` + if (!session) { + throw new HTTPException(401); + } -You can also customize cookie attributes individually by setting them within `cookies` in your auth config. - -```ts title="auth.ts" -export const auth = createAuth({ - advanced: { - cookies: { - sessionToken: { - attributes: { - sameSite: "none", - secure: true, - partitioned: true // New browser standards will mandate this for foreign cookies - } - } - } - } -}) -``` - -### Client-Side Configuration - -When using the Hono client (`@hono/client`) to make requests to your Better Auth-protected endpoints, you need to configure it to send credentials (cookies) with cross-origin requests. - -```ts title="api.ts" -import { hc } from "hono/client"; -import type { AppType } from "./server"; // Your Hono app type - -const client = hc("http://localhost:8787/", { - init: { - credentials: "include", // Required for sending cookies cross-origin - }, + return c.json({ message: `Hello, ${session.user.name}.` }); }); -// Now your client requests will include credentials -const response = await client.someProtectedEndpoint.$get(); +export default app; ``` -This configuration is necessary when: +This example applies the middleware to a single route. For other patterns, see Hono's [Middleware guide](https://hono.dev/docs/guides/middleware). -* Your client and server are on different domains/ports during development -* You're making cross-origin requests in production -* You need to send authentication cookies with your requests +## Hono RPC -The `credentials: "include"` option tells the fetch client to send cookies even for cross-origin requests. This works in conjunction with the CORS configuration on your server that has `credentials: true`. +The Better Auth client uses `credentials: "include"` by default. If you use Hono RPC to call authenticated Hono routes from another origin, configure the RPC client to include credentials as well: -> **Note:** Make sure your CORS configuration on the server matches your client's domain, and that `credentials: true` is set in both the server's CORS config and the client's fetch config. + + + ```ts title="src/client.ts" + import { hc } from "hono/client"; + import type { AppType } from "./server"; + + const client = hc("http://localhost:8787", { + init: { // [!code highlight] + credentials: "include", // [!code highlight] + }, // [!code highlight] + }); + + const response = await client.hello.$get(); + ``` + + + + ```ts title="src/server.ts" + import { Hono } from "hono"; + + // Assume this route requires an authenticated session. // [!code highlight] + const app = new Hono().get("/hello", (c) => { + return c.json({ message: "Hello!" }); + }); + + export type AppType = typeof app; + export default app; + ``` + + + +This configuration is only required for cross-origin requests. For more information, see Hono's [RPC cookies guide](https://hono.dev/docs/guides/rpc#cookies).