Cloudflare Workers + Hono なWebアプリに対し、Cloudflare Accessを使って特定のGoogleアカウントのみアクセス許可してみた

ここ最近のAIの動きから、自分しか使わないWebアプリケーションが気軽に作れるようになりました。

特に、Cloudflare Workersでもフロントエンドを持つアプリが作成できるようになったため、まずはCloudflare Workersの利用を考えることが多いです。
フロントエンド、バックエンド、データベースが1つのCloudflare Workerに

 
自分しか使わないとはいえ、データベース的なものがある場合はセキュリティ的な面から認証を設定したいのですが、アプリごとに認証機能を付けるのも手間です。

認証まわりを調べてみたところ、Cloudflare Accessを使うことで、GoogleなどのIdPによる認証に加え、「特定のGoogleアカウントのみアクセスを許可する」もできそうでした。
Overview · Cloudflare One docs

 
事例を調べたところ、Cloudflare PagesのアプリにAccessで制限をするのような記事がいくつかありました。

 
そこで、「Cloudflare Workersで作ったページに対して、特定のGoogleアカウントだけアクセス可能にする」を試してみたときのメモを残します。

 
目次

 

環境

  • 各種アカウントは取得済
    • Cloudflareアカウント
    • Googleアカウント
  • Cloudflare One
    • 設定を追加する際、無料プランであってもクレカ情報の登録が必要
      • PayPalやStripeのアカウントを紐づける形でも可
  • Hono 4.11.0
  • jose 6.1.3
  • Node.js 24.12.0
  • Cloudflare C3 2.59.0
  • Wrangler 4.54.0

 

Webアプリの準備

Hono製アプリの作成

今回は「Webアプリにアクセスできるかどうか」だけを確認できればよいです。そこで、Hello, world的なHono製アプリをCloudflare Workersで用意します。

HonoでCloudflare Workers向けWebアプリを作る場合、HonoのドキュメントとCloudflareのドキュメントの2つが参考になります。

 
Cloudflareのドキュメントでは「テンプレートを使ってHono + Reactなアプリを作る」方法しか書かれていません。しかし、CLIのページを読むとテンプレートなしでHonoも作れそうでした。
Get started - CLI · Cloudflare Workers docs

本当にCreate Cloudflare CLI (C3)でHonoを使えるか気になったので、今回はCloudflareのドキュメントに従って進めます。

ちなみに、C3を使った場合、途中で create-hono@0.19.3のインストール確認がありました。そのため、最終的にはCloudflareのドキュメントのやり方は、Honoのドキュメントのやり方を包含してそうです。

 
C3を実行するときのプロジェクト名でアンダースコア(_)を使うと

ERROR  Error: Project names must only contain lowercase characters, numbers, and dashes.

とエラーになるため、ハイフン区切りのプロジェクト名 hello-worker-with-accessでWebアプリを作ります。

% npm create cloudflare@latest -- hello-worker-with-access

> npx
> "create-cloudflare" hello-worker-with-access


──────────────────────────────────────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.59.0!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.

Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
──────────────────────────────────────────────────────────────────────────────────────────────────────────

╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./hello-worker-with-access
│
├ What would you like to start with?
│ category Framework Starter
│
├ Which development framework do you want to use?
│ framework Hono
│
├ Select your deployment platform
│ platform Workers with Assets
│
├ Continue with Hono via `npx create-hono@0.19.3 hello-worker-with-access --template cloudflare-workers --install --pm npm`
│

Need to install the following packages:
create-hono@0.19.3
Ok to proceed? (y) 

create-hono version 0.19.3
✔ Using target directory … hello-worker-with-access
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd hello-worker-with-access

├ Copying template files
│ files copied to project directory
│
╰ Application created 

╭ Configuring your application for Cloudflare Step 2 of 3
│
├ Installing wrangler A command line tool for building Cloudflare Workers
│ installed via `npm install wrangler --save-dev`
│
├ Retrieving current workerd compatibility date
│ compatibility date 2025-12-13
│
├ Adding Wrangler files to the .gitignore file
│ updated .gitignore file
│
├ Updating `package.json` scripts
│ updated `package.json`
│
├ Generating types for your application
│ generated to `./worker-configuration.d.ts` via `npm run cf-typegen`
│
├ Do you want to use git for version control?
│ yes git
│
├ Initializing git repo
│ initialized git
│
├ Committing new files
│ git commit
│
╰ Application configured 

╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
│
╰ Done 

────────────────────────────────────────────────────────────
🎉  SUCCESS  Application created successfully!

💻 Continue Developing
Change directories: cd hello-worker-with-access
Deploy: npm run deploy

📖 Explore Documentation
https://developers.cloudflare.com/workers

🐛 Report an Issue
https://github.com/cloudflare/workers-sdk/issues/new/choose

💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────

 
生成されたアプリのディレクトリへ移動した上で起動してみます。

# ディレクトリの移動
% cd hello-worker-with-access


# 起動
% npm run dev        

> dev
> wrangler dev


 ⛅️ wrangler 4.54.0
───────────────────
Your Worker has access to the following bindings:
Binding            Resource      Mode
env.ASSETS         Assets        local

╭──────────────────────────────────────────────────────────────────────╮
│  [b] open a browser [d] open devtools [c] clear console [x] to exit  │
╰──────────────────────────────────────────────────────────────────────╯
⎔ Starting local server...
[wrangler:info] Ready on http://localhost:8787

 
表示されたURLにアクセスすると、Hello Hono! が表示されました。

 
ログも出力されていました。

[wrangler:info] GET / 200 OK (23ms)
[wrangler:info] GET /message 200 OK (8ms)
[wrangler:info] GET /favicon.ico 404 Not Found (2ms)

 

Cloudflare Workersへのデプロイ

ローカルで動作確認ができたため、次はCloudflare Workersへデプロイして動作確認を行います。

C3でプロジェクトを作成するとWranglerがインストールされたため、Wranglerを使って、Cloudflare Workersへデプロイします。

% npx wrangler deploy

 ⛅️ wrangler 4.54.0
───────────────────
Attempting to login via OAuth...
Opening a link in your default browser: https://dash.cloudflare.com/***
Successfully logged in.
🌀 Building list of assets...
✨ Read 1 file from the assets directory /path/to/hello-worker-with-access/public
🌀 Starting asset upload...
🌀 Found 1 new or modified static asset to upload. Proceeding with upload...
+ /index.html
Uploaded 1 of 1 asset
✨ Success! Uploaded 1 file (0.88 sec)

Total Upload: 60.58 KiB / gzip: 14.79 KiB
Your Worker has access to the following bindings:
Binding            Resource      
env.ASSETS         Assets        

Uploaded hello-worker-with-access (7.28 sec)
Deployed hello-worker-with-access triggers (1.93 sec)
  https://hello-worker-with-access.***.workers.dev
Current Version ID: 7c74e6fd-7d03-424d-8047-859ce08471fe

 
デプロイで作成されたURLへアクセスすると、ローカル同様のページが表示されました。

 

Cloudflare AccessによるGoogle認証制御の追加

続いて、Cloudflare Accessを使って、特定のGoogleアカウントのみアクセスを許可します。

 

Cloudflare Oneにチームを作成

まずは、Cloudflare Oneにチームを作ります。

Settings > Team name and domain にてチームを追加します。

  • team name は任意の値
  • Add payment method でクレカ情報を設定
    • 今回はPayPalアカウントを紐づけ

 

Google Cloud Consoleでプロジェクトを作成

Google認証をWebアプリケーションへ追加するには、Google Cloud Consoleにてプロジェクトの作成が必要です。ただし、「プロジェクトを作成したGoogleアカウントのみGoogle認証を使える」わけではなく、すべてのGoogleアカウントに対するGoogle認証が使えます。

どのようなプロジェクトを作ればよいかについては、Cloudflareのドキュメントに記載があります。
Google · Cloudflare One docs

 
今回もCloudflareのドキュメントに従い、次の流れで設定を行いました。

  • Google Cloud Consoleでプロジェクトを作成
    • 名前は任意
  • APIとサービス > サイドバーの 認証情報 で移動
  • 同意画面を構成 ボタンをクリック
  • 開始 ボタンをクリック
  • アプリ情報で以下を設定
    • アプリ名
      • 任意
    • ユーザーサポートメール
      • ドロップダウンで選択
  • 対象にて、外部 を選択
  • 連絡先情報は、任意のメールアドレスを入力
  • Google APIサービス に同意して完了

 
設定が終わるとOAuthの概要ページになるため、引き続きOAuthクライアントを作成します。

  • 指標にある OAuthクライアントを作成 をクリック
  • 以下を設定し、 作成 ボタンをクリック
    • アプリケーションの種類
    • 名前
      • 任意
    • 承認済みの JavaScript 生成元
      • URIを追加 ボタンをクリックし、以下の内容を追加
        • https://<Cloudflareのチーム名>.cloudflareaccess.com
    • 承認済みのリダイレクト URI
      • URIを追加 ボタンをクリックし、以下の内容を追加
        • https://<Cloudflareのチーム名>.cloudflareaccess.com/cdn-cgi/access/callback

以上でGoogle Cloud Consoleでの作業は終わりです。

 

Cloudflare OneでIdpを登録

次に、Cloudflare AccessGoogle認証を使えるよう、GoogleのIdPを登録します。
Identity providers · Cloudflare One docs

  • 左メニューの Integrations > Identity providers
  • Add an identity provider をクリック
  • Google を選択
  • App IDClient IDClient secretClient Secret を設定
    • 合わせて、 PKCE をONにする
  • Save をクリック

 
IdPが登録できたところで動作確認をします。

  • 画面にある Test をクリックする
  • Googleアカウントでログインする
  • ログインが成功すると、画面にGoogleアカウントのnameとemailが表示される

 

Cloudflare Oneでアプリケーションを追加

続いて、Googleアカウントでの認証を要求するアプリケーションを追加します。公式ドキュメントによると、追加するアプリケーションの種類は Self-hosted になるようです。
Add web applications · Cloudflare One docs

Self-hostedの設定手順は以下のドキュメントになります。
Publish a self-hosted application to the Internet · Cloudflare One docs

 
左メニューの Access controls > Application をクリックし、Add an application をクリックします。

ウィザード形式で設定を行います。なお、記載されていないものはデフォルト設定のままとします。

  • What type of application are you adding?
    • Self-hosted
  • Configure application
    • Basic information
      • Application name
        • 任意
      • Session Duration
        • デフォルト
          • 24h
      • Public hostname
        • Domainには、先ほどデプロイしたドメインを選択
        • Subdomainには、アプリ名 hello-worker-with-access を設定
    • Login methods
      • Accept all available identity providers
        • Off
          • One-time PINは無効化する
        • 表のProviderでは Google のみ選択
      • Instant Auth
        • On
          • 1つしか選択できないので、プロバイダーの選択は表示しない
    • Nextボタン
  • Experience settings
    • Application Appearance
      • Application logo
        • Default
      • Application domain
        • Default
    • Tags
      • デフォルトのまま、何も選択しない
    • Custom pages
      • Identity failure block page
        • Cloudflare default
      • Non-identity failure block page
        • Cloudflare default
    • Nextボタン
  • Advanced settings
    • 特に設定しない
    • Save

 

Cloudflare Oneでポリシーを追加・適用

アプリケーションを追加しただけでは、まだ誰もアクセスできない状態です。

ポリシーを作成・紐づけを行うことで、特定のGoogleアカウントのみアクセスできるようになります。
Manage Access policies · Cloudflare One docs

 
まず、ポリシーを作成し、アクセスできるGoogleアカウントの設定を行います。

  • Applications をクリックし、追加されたものの3点リーダーから Edit
  • Policies タブをクリック
  • Add a policy をクリック
  • Basic Information
    • Policy name
      • 任意
    • Action
      • Allow
    • Session duration
      • Same as application session timeout
  • Add rules
    • Selector
      • Emails
    • Value
      • アクセスを許可したいメールアドレス
  • Saveボタンをクリック

 
次に、Policyタブにて、Select existing policies から先ほど作成したポリシーを選択し、アプリとの紐づけを行います。

 

アクセストークンのバリデーション

ここまででも、特定のGoogleアカウントでのみアクセスを許可できました。

しかし、Cloudflareのドキュメントでは、アクセストークン検証の必要性についても記載されています。
Publish a self-hosted application to the Internet · Cloudflare One docs

そこで今回もアクセストークンの検証を実装します。

ちなみに、アクセストークンはJWT形式です。また、検証の方法はCloudflareのドキュメントに記載されています。
Validate JWTs · Cloudflare One docs

 

AUDタグの取得

アクセストークンを検証するときには AUD タグが必要になります。

ドキュメントにある通り、以下の方法で取得できます。

  • 左メニューの Access controls > Applications をクリック
  • 3点リーダーより、 Edit をクリック
  • 下の方にある Appilcation Audience (AUD) Tag にある、AUDタグの値をコピーして取得

 

Cloudflare Workersの環境変数へ設定

続いて、Cloudflare Workersの環境変数

  • Cloudflare AccessのteamのURL
  • AUDタグ

をそれぞれ設定します。

% npx wrangler secret put CF_ACCESS_TEAM_DOMAIN

 ⛅️ wrangler 4.54.0
───────────────────
✔ Enter a secret value: … ***
🌀 Creating the secret for the Worker "hello-worker-with-access" 
✨ Success! Uploaded secret CF_ACCESS_TEAM_DOMAIN


% npx wrangler secret put CF_ACCESS_AUD

 ⛅️ wrangler 4.54.0
───────────────────
✔ Enter a secret value: … ***
🌀 Creating the secret for the Worker "hello-worker-with-access" 
✨ Success! Uploaded secret CF_ACCESS_AUD

 

Honoアプリでアクセストークンの検証を追加

Honoアプリを以下の内容で修正します。

  • Honoアプリでアクセストークンを検証するミドルウェアを追加
  • アクセス可能な場合はアクセストークンからメールアドレスを取得し、画面に表示
import { Hono } from "hono";
import { jwtVerify, createRemoteJWKSet } from 'jose'

/**
 * Hono アプリで Cloudflare Access の JWT を検証する。
 * Cf‑Access‑Jwt‑Assertion ヘッダーから取得した JWT を公開鍵で検証し、issuer と audience を照合する。
 * https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/
 */
type EnvBindings = {
  CF_ACCESS_TEAM_DOMAIN: string  // https://<team>.cloudflareaccess.com
  CF_ACCESS_AUD: string          // アプリケーションの AUD タグ
}

type CfAccessVars = {
  cfAccessJwt?: {
    payload: Record<string, unknown>
  }
}

const app = new Hono<{ Bindings: EnvBindings; Variables: CfAccessVars }>();

// アプリ全体に適用するミドルウェア
app.use(async (c, next) => {
  const teamDomain = c.env.CF_ACCESS_TEAM_DOMAIN
  const aud = c.env.CF_ACCESS_AUD

  // 必須の環境変数が設定されているかチェック
  if (!teamDomain || !aud) {
    return c.text('Missing required environment variables', 500)
  }

  // Cloudflare Access が付与する JWT を取得
  const token = c.req.header('cf-access-jwt-assertion')
  if (!token) {
    return c.text('Missing CF Access JWT', 403)
  }

  // 公開鍵のリモートセットを取得
  const jwks = createRemoteJWKSet(new URL(`${teamDomain}/cdn-cgi/access/certs`))

  try {
    // JWT の検証
    const { payload } = await jwtVerify(token, jwks, {
      issuer: teamDomain,
      audience: aud,
    })

    // 検証成功時は payload をコンテキストに保存し次の処理へ
    c.set('cfAccessJwt', { payload: payload as Record<string, unknown> })
    await next()
  } catch (err) {
    // 検証失敗時は 403 を返す
    return c.text('Invalid CF Access JWT', 403)
  }
})


app.get("/message", (c) => {
  // ミドルウェアで保存した payload からメールアドレスなどを取り出す
  const jwt = c.get('cfAccessJwt')
  const payload = jwt?.payload || {}
  const email = (payload as any).email as string | undefined

  return c.text(`Hello ${email}`);
});

export default app;

 
修正を終えたらデプロイします。

% npx wrangler deploy

 
以上でアクセストークンの検証も完了です。

 

動作確認

動作確認として、Cloudflare Accessが有効な状態の場合と無効な状態の場合を試してみます。

 

Cloudflare Accessが有効な状態の場合

アプリケーションのURLへアクセスすると、Google認証が求められます。

アクセスを許可したGoogleアカウントの場合、Cloudflare Workersの画面にメールアドレスが表示されました。

 
一方、アクセス許可を与えていないGoogleアカウントの場合、エラーになりました。

 

Cloudflare Accessが無効な状態の場合

Cloudflare Accessドメイン設定が不適切だった場合など、Cloudflare Accessが無効な状態になっている想定で動作確認します。

今回の動作確認では、Cloudflare Accessドメイン設定にてサブドメインの末尾に 2 を追加して、不適切な状態にします。

 
この状態でアプリへアクセスするとエラーになりました。画面に表示されたエラーメッセージから、Honoでのアクセストークン検証が失敗したようです。

これにより、何らかの原因でCloudflare Accessが無効になっていた場合も、アプリケーション側で防御できていると分かりました。

 

ソースコード

GitHubに上げました。
https://github.com/thinkAmi-sandbox/hello_cloudflare_worker_with_cloudflare_access-example