本記事は「Adding Auth0 to Hono on Cloudflare Workers: A Practical Guide」を翻訳した記事です。
最近Cloudflare Workersで何かをリリースしたなら、おそらくHonoを利用したことでしょう。Honoは小規模で高速であり、Workers、Node、Bun、Denoで同じコードを実行できます。唯一欠けていたのは、Auth0ログインを処理するファーストパーティの方法でした。そのため、独自のミドルウェアを記述し、gistからOpenID Connectフローをコピーして、PKCEと状態チェックが正しく機能することを祈るしかありませんでした。
今日、状況が変わります。@auth0/auth0-honoがベータ版になりました。1つのミドルウェア呼び出しで、ログイン、ログアウト、セッション、トークンリフレッシュをネイティブなHonoミドルウェアとして組み込めます。追加の設定なしで、エッジですぐに実行できます。
本記事では、追加方法、提供する機能、そして本番環境に導入する前に知っておくべき「ベータ版」の意味について説明します。
要約:
npm install @auth0/auth0-honoを実行し、app.use('*', auth0())を追加します。これでログイン、ログアウト、コールバック、セッション管理が完了します。requiresAuth()でルートを保護します。ユーザー情報はc.var.auth0.userから読み取ります。- 1つのコードベースからCloudflare Workers、Node、Bun、Deno、Vercel Edgeで実行でき、プラットフォーム固有の回避策は不要です。
- セッションはデフォルトで暗号化されたCookieであり、より大きなペイロード用にプラガブルなストアを備えています。
- ベータ版です。機能セットはNodeとWorkers向けに本番環境レベルですが、一般提供前にAPIが変更される可能性があります。フィードバックを歓迎します。
HonoはGitHubで3万スターを超え、週に4000万回以上npmからインストールされています。Cloudflare Workersで構築する人にとってデフォルトのWebフレームワークとなっており、スタックの他の部分を容易にするTypeScriptファーストな設計です。
現在、Hono向けの公式Auth0 SDKがあります。トークン交換、状態検証、セッション暗号化など、完全なOpenID Connectフローを処理し、Node.js、Cloudflare Workers、その他のエッジランタイムでネイティブに実行されます。
@auth0/auth0-honoを使用したアプリの保護
始めましょう。パッケージをインストールします。
npm install @auth0/auth0-hono
次に、1つのミドルウェアを追加します。
import { Hono } from 'hono' import { auth0, requiresAuth } from '@auth0/auth0-hono' const app = new Hono() // Add auth to every route app.use('*', auth0()) // Public route app.get('/', (c) => c.text('Home')) // Protected route app.get('/profile', requiresAuth(), (c) => { const user = c.var.auth0.user return c.json({ name: user?.name, sub: user?.sub }) }) export default app
単一のauth0()呼び出しにより、ログイン、コールバック、ログアウトのルート(/auth/login、/auth/callback、/auth/logout)がマウントされます。また、バックチャネルログアウトを処理し、セッションCookieを暗号化し、すべてのリクエストでユーザーをc.var.auth0に読み込みます。バックチャネルログアウトにより、Auth0はサーバー間でセッションを終了するようアプリに通知できるため、1か所でのログアウトですべての場所からユーザーをサインアウトさせます。OpenID Connect(OIDC)ハンドラーを記述する必要はありません。1行追加するだけです。
設定は環境変数から取得され、すべてのランタイムで同じように機能します。
AUTH0_DOMAIN=tenant.auth0.com AUTH0_CLIENT_ID=abc123 AUTH0_CLIENT_SECRET=secret123 AUTH0_SESSION_ENCRYPTION_KEY=very_long_string_with_at_least_32_characters APP_BASE_URL=https://myapp.com
これらの値は、Auth0 Dashboardのアプリケーション設定にあります。
requiresAuth()を使用したルートの保護
requiresAuth()は通常のHonoミドルウェアであるため、期待通りに構成できます。任意のルートまたはルートグループの前に配置します。
app.get('/dashboard', requiresAuth(), (c) => { // c.var.auth0.user is guaranteed to exist here return c.json(c.var.auth0.user) })
認証されていないリクエストは、ハンドラーが実行される前に、コンテンツタイプに応じて401を返すか、ログインにリダイレクトします。ハンドラー内にはすでにユーザーが存在します。非同期呼び出し、nullチェック、推測は不要です。
より細かい制御が必要な場合、認可ミドルウェアも同じように構成できます。
app.get('/admin', requiresAuth(), claimEquals('role', 'admin'), handler ) app.get('/reports', requiresAuth(), claimIncludes('permissions', 'read:reports', 'admin:reports'), handler )
パターンに注目してください。各チェックは独自のミドルウェアであるため、ルートのアクセスルールを定義から直接読み取れます。チェックは、Auth0がトークンに配置するユーザーに関するKey-Valueの事実であるクレームを読み取ります。
暗号化されたCookieを使用したセッション管理
デフォルトでは、セッションは暗号化されたCookieに保存されます。ステートレスであるため、実行するセッションデータベースはなく、Cloudflare Workersのリージョン間で調整するものもありません。SDKはリクエストごとに1回、約1〜2ミリ秒でCookieを復号するため、ハンドラーが実行される前にユーザーの準備が整います。
暗号化キーはローテーションをサポートしています。配列を渡すと、最初のキーが暗号化を行い、すべてのキーが復号できるため、ダウンタイムなしでキーをローテーションできます。
app.use('*', auth0({ session: { secret: [process.env.NEW_KEY, process.env.OLD_KEY], }, }))
より大きなペイロードでセッションを充実させる場合、Cookieにはサイズ制限があります。その場合、ステートフルなストアを組み込み、独自のデータベースにデータを保持できます。
import { SessionStore } from '@auth0/auth0-hono' const customStore: SessionStore = { async set(name, data, isTransaction, ctx) { await db.sessions.set(data.internal.sid, data) }, async get(name, ctx) { return await db.sessions.get(sessionId) }, // ... delete, clear } app.use('*', auth0({ session: { secret: '...', store: customStore }, }))
リフレッシュの煩わしさなしでトークンを取得する
独自のAPIを呼び出しますか。getAccessToken()は有効なトークンを返し、期限切れの場合はリフレッシュします。
app.get('/api/data', requiresAuth(), async (c) => { const { accessToken } = await getAccessToken(c) const res = await fetch('https://api.example.com/data', { headers: { Authorization: `Bearer ${accessToken}` }, }) return c.json(await res.json()) })
ここで本当に頭痛の種を減らせる部分を紹介します。トークンリフレッシュは重複排除されます。5つの並行リクエストがすべてgetAccessToken()に到達し、トークンのリフレッシュが必要な場合、SDKは1回のリフレッシュ呼び出しを行い、残りは同じPromiseを待機します。トークンエンドポイントに対するリフレッシュリクエストの殺到は発生せず、それを防ぐためのロックを記述する必要もありません。
Cloudflare Workersおよびその他の環境向けに構築
認証コードを1回記述すれば、Honoが実行される場所ならどこでも実行できます。同じアプリを、プラットフォーム固有の変更を一切加えずに、Cloudflare Workers、Node、Bun、Deno、Vercel Edgeにデプロイできます。
その移植性が重要です。1つのランタイムに縛られることはなく、後で従来のサーバーからエッジに移行する場合でも、認証レイヤーを書き直す必要はありません。
| Runtime | Level | Status |
|---|---|---|
| Node.js 18+ | Primary | Full support, full test coverage |
| Cloudflare Workers | Primary | Full support |
| Bun 1.x+ | Secondary | Works, best-effort testing |
| Deno 1.x / 2.x | Secondary | Works, best-effort testing |
| Vercel Edge | Secondary | Works, best-effort testing |
安全なパスがデフォルトのパスです。ログインフローは一般的な攻撃から保護され、セッションCookieは暗号化されます。シークレットはサーバー上に保持され、クライアントバンドルには決して含まれません。内部的には、SDKは他のサーバーSDKを強化するのと同じAuth0認証エンジン上に構築されているため、Auth0がプラットフォーム全体で維持しているセキュリティ基準を得られます。
動作を確認する
リポジトリには完全なデモであるAcme Corpが含まれているため、断片ではなく機能するコードを読めます。匿名の訪問者がホームページにアクセスし、Auth0を通じてログインし、保護されたダッシュボードに到達し、ログアウトするという全体のフローを示しています。examples/demo/の下にあります。
Cloudflare Workersへのデプロイ
デプロイは標準のWranglerパスです。ローカル開発用には.dev.varsに、本番環境用には暗号化されたシークレットとしてAuth0シークレットを保持し、次を実行します。
wrangler deploy
SDKはHonoのアダプターを通じてバインディングを読み取るため、Cloudflare Workersで設定を機能させるための追加の配線は不要です。
ベータ版SDKのインストール
インストールについてはすでに確認しました。実際のアプリを実行するための最小限の設定は次のとおりです。
npm install @auth0/auth0-hono
app.use('*', auth0({ domain: 'tenant.auth0.com', clientID: 'abc123', clientSecret: 'secret123', baseURL: 'https://myapp.com', }))
本記事におけるベータ版の意味
ベータ版とは、完全な認証フロー、型付きエラー、認可ミドルウェア、セッション管理を備え、主要なランタイム(NodeおよびCloudflare Workers)で機能セットが本番環境レベルであることを意味します。まだ流動的なのはパブリックAPIの形状です。一般提供前に確定する予定であり、Issueの作成やリポジトリでのディスカッションの開始によるフィードバックを歓迎します。
トークン取り消し、リソースサーバー向けのJWT APIミドルウェア、多要素認証チャレンジフローなど、さらに多くの機能がまもなくリリースされる予定です。
次のステップ
今後のロードマップには、Multiple Custom DomainsやConnected Accountsなどの認証機能の追加、organizationサポートの強化、ファーストクラスのカスタムセッションストアが含まれます。
現時点で、スタックに適合するかどうかを確認する最も速い方法は、インストールしてテストテナントに向け、既存のアプリにauth0()を追加することです。
詳細情報
始めましょう: npm install @auth0/auth0-honoを実行し、デモを確認して、ドキュメントを読みます。
About the author

Tushar Pandey
Software Development Engineer II
