---
title: Hono — first integration
project: Authaz
updated: 2026-05-07T23:22:33.427Z
---

# Hono — first integration

> [← All recipes](./index.md) · First time? [Set up your app](../quickstart/setup.md) (60 seconds — keys + redirect URI)

A Hono backend that signs a user in via Authaz Sign-In, exposes a protected JSON endpoint, and lets them log out. Single-tenant, password provider, no roles.

## 1. What you'll build

A Hono API where `GET /` is public, `GET /me` returns the signed-in user, and `GET /protected` requires a valid Authaz session. Runs on Node or Bun.

## 2. Application setup

In the Authaz Dashboard:

1. **New application** → name it, choose **Single-tenant**.
2. Note the **Client ID** and **Client Secret**.
3. Under **Auth Flow Configuration**, add `http://localhost:3000/auth/callback` as an allowed callback URL.

…or via the Management API:

```http
POST https://your-app.authaz.io/api/v1/applications
X-API-Key: mgmt_01h...
Content-Type: application/json

{ "name": "my-hono-api", "tenancy_type": "single_tenant" }
```

## 3. Install

```bash
pnpm add hono @authaz/hono
pnpm add -D @hono/node-server tsx
```

## 4. Configure

`.env`:

```
AUTHAZ_CLIENT_ID=app_01h...
AUTHAZ_CLIENT_SECRET=secret_...
AUTHAZ_ORGANIZATION_ID=0199...
AUTHAZ_API_KEY=mgmt_01h...
```

## 5. Wire it up

`src/index.ts`:

```typescript
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import {
  createAuthazHandler,
  createAuthMiddleware,
  isAuthenticated,
} from "@authaz/hono";

const app = new Hono();

// Mount the Authaz handler at /api/auth/*
const authaz = createAuthazHandler({
  clientId: process.env.AUTHAZ_CLIENT_ID!,
  clientSecret: process.env.AUTHAZ_CLIENT_SECRET!,
  organizationId: process.env.AUTHAZ_ORGANIZATION_ID!,
});
app.route("/api/auth", authaz);

// Public route
app.get("/", (c) => c.text("Welcome — POST /api/auth/login to sign in."));

// Protect /protected and /me with the auth middleware
app.use(
  "*",
  createAuthMiddleware({
    publicPaths: ["/", "/api/auth/*", "/auth/callback"],
    loginPath: "/api/auth/login",
  })
);

app.get("/me", async (c) => {
  if (!isAuthenticated(c)) return c.json({ error: "unauthorized" }, 401);
  // The handler also exposes /api/auth/me — redirect for parity
  return c.redirect("/api/auth/me");
});

app.get("/protected", (c) =>
  c.json({ message: "You are signed in to Authaz." })
);

serve({ fetch: app.fetch, port: 3000 });
console.log("Listening on http://localhost:3000");
```

The handler exposes `/api/auth/login`, `/api/auth/callback` (POST), `/api/auth/logout`, `/api/auth/me`, `/api/auth/refresh`. The OAuth callback bridge page (a tiny client form that POSTs the code) is the same as the [Next.js recipe step 5](./nextjs-single-tenant.md#5-wire-it-up); host it on whatever frontend you pair Hono with.

## 6. Read the user

`getAccessToken(c)` returns the cookie token; pair it with the `/api/auth/me` call from your client, or call `createAuthazHelpers({ organizationId, apiKey }).getUser()` server-side:

```typescript
import { createAuthazHelpers } from "@authaz/hono";

const helpers = createAuthazHelpers({
  organizationId: process.env.AUTHAZ_ORGANIZATION_ID!,
  apiKey: process.env.AUTHAZ_API_KEY!,
});

app.get("/who-am-i", async (c) => {
  const user = await helpers.getUser(c);
  if (!user) return c.json({ error: "unauthorized" }, 401);
  return c.json({ id: user.id, email: user.email });
});
```

## 7. Protect a route

The `createAuthMiddleware` wraps `*` already. For per-route protection, check the cookie directly with `isAuthenticated(c)`:

```typescript
app.get("/private", (c) => {
  if (!isAuthenticated(c)) return c.json({ error: "unauthorized" }, 401);
  return c.json({ secret: 42 });
});
```

## 8. Common gotchas

- **Callback URL must match exactly.** A trailing slash, `http` vs `https`, or a different port all fail. Register every dev/staging/prod URL.
- **You still need a callback page.** Authaz redirects to a *page* (GET), which POSTs the code into `/api/auth/callback`. Hono only handles the API side — pair it with a frontend (or a tiny static HTML page) for the bridge.
- **Cookies need HTTPS in production.** The SDK sets `Secure` automatically; behind a load balancer make sure `x-forwarded-proto: https` reaches the app.
- **Logout is POST.** Use a `<form method="POST" action="/api/auth/logout">`, not a GET link.
- **Don't expose `clientSecret` to the browser.** It's a server-only env var. The Hono handler is fine; never inline the secret into a client bundle.

## 9. Next steps

- [Multi-tenant Hono recipe](./hono-multi-tenant.md) — same shape with tenant-scoped permission checks
- [React SPA recipe](./react-single-tenant.md) — pair this Hono backend with a React frontend
- [Authentication providers](../authentication/settings.md) — add Google, magic link, passkey
