mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-19 23:53:17 -05:00
docs: rewrite Hono integration guide (#10763)
This commit is contained in:
@@ -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.
|
||||
|
||||
<Callout type="info">
|
||||
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));
|
||||
```
|
||||
</Callout>
|
||||
|
||||
### 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<Env>(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<AppType>("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.
|
||||
<Tabs items={["Client", "Server"]}>
|
||||
<Tab value="Client">
|
||||
```ts title="src/client.ts"
|
||||
import { hc } from "hono/client";
|
||||
import type { AppType } from "./server";
|
||||
|
||||
const client = hc<AppType>("http://localhost:8787", {
|
||||
init: { // [!code highlight]
|
||||
credentials: "include", // [!code highlight]
|
||||
}, // [!code highlight]
|
||||
});
|
||||
|
||||
const response = await client.hello.$get();
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab value="Server">
|
||||
```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;
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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).
|
||||
|
||||
Reference in New Issue
Block a user