Hono

Full integration guide for Clowk with Hono

Install

pnpm add @clowk/hono @clowk/core

Configure

src/index.ts
import { Hono } from 'hono'
import { clowkMiddleware, requireAuth } from '@clowk/hono'

const app = new Hono()

Protect all routes

Apply clowkMiddleware globally. The decoded JWT payload is available via c.get('auth'):

app.use(clowkMiddleware({
  secretKey: process.env.CLOWK_SECRET_KEY,
}))

app.get('/me', (c) => {
  const auth = c.get('auth')

  return c.json({ user: auth })
})

export default app

Middleware options

OptionTypeDescription
secretKeystringSecret key for JWT verification
tokenParamstringQuery param name (default: "token")
cookieKeystringCookie name (default: "clowk_token")

Protect specific routes

Use requireAuth() to gate routes. It returns 401 if no valid token is found:

app.get('/public', (c) => {
  return c.json({ message: 'This is public' })
})

app.get('/dashboard', requireAuth(), (c) => {
  const auth = c.get('auth')

  return c.json({ user: auth })
})

c.get('auth') type

interface JwtPayload {
  iss?: string       // "clowk"
  sub?: string       // User UUID
  email?: string     // "jane@example.com"
  name?: string      // "Jane Doe"
  avatar_url?: string
  provider?: string  // "google" | "github" | "twitter" | "email"
  instance_id?: string
  app_id?: string
  iat?: number
  exp?: number
}

With clowkMiddleware, c.get('auth') is null if no token is found (soft auth). With requireAuth, the request is rejected before reaching your handler.

Runtimes

Hono + Clowk works on every runtime. @clowk/core uses native fetch and Web Crypto APIs — no Node.js-specific dependencies.

RuntimeDeploy target
Cloudflare Workerswrangler deploy
Bunbun run src/index.ts
Denodeno run src/index.ts
Node.jsnode src/index.ts

Full example

src/index.ts
import { Hono } from 'hono'
import { clowkMiddleware, requireAuth } from '@clowk/hono'

const app = new Hono()

app.use(clowkMiddleware({
  secretKey: process.env.CLOWK_SECRET_KEY,
}))

app.get('/', (c) => {
  const auth = c.get('auth')

  return c.json({ authenticated: !!auth })
})

app.get('/profile', requireAuth(), (c) => {
  const auth = c.get('auth')

  return c.json({
    id: auth?.sub,
    email: auth?.email,
    name: auth?.name,
  })
})

export default app

On this page