從零開始:用 Hono + Cloudflare Workers 打造 Mock OAuth Server
從零開始:用 Hono + Cloudflare Workers 打造 Mock OAuth Server
本文記錄如何建立一個輕量級的 Mock OAuth 2.0 Server,用於測試 MCP Client 或其他需要 OAuth 認證的應用程式。
為什麼需要 Mock OAuth Server?
當你在開發需要 OAuth 認證的應用程式時,常常會遇到這些問題:
- 測試困難:每次測試都要登入 Google/GitHub 很麻煩
- 無法測試異常情況:真實 OAuth Provider 不會故意回傳錯誤
- 本地開發受限:
localhost難以被外部服務呼叫 - 速度慢:真實 OAuth 流程涉及多次網路請求
Mock OAuth Server 可以解決這些問題,讓你完全掌控 OAuth 流程。
技術選擇:Hono + Cloudflare Workers
我們選擇這個組合的原因:
| 優點 | 說明 |
|---|---|
| 公開 HTTPS | 獲得 https://xxx.workers.dev 網址,外部服務可呼叫 |
| 零冷啟動 | Workers 啟動速度極快,測試不會卡頓 |
| 免費 | Cloudflare 免費額度對測試綽綽有餘 |
| Hono 輕量 | 類似 Express 的語法,學習成本低 |
實作步驟
Step 1:建立專案
mkdir my-oauth-mock
cd my-oauth-mock
bun init -y
Step 2:安裝依賴
bun add hono
bun add -d wrangler @cloudflare/workers-types typescript
Step 3:設定 Wrangler (wrangler.toml)
name = "my-oauth-mock"
main = "src/index.ts"
compatibility_date = "2024-12-30"
[observability]
enabled = true
Step 4:實作 OAuth Server (src/index.ts)
核心程式碼包含四個端點:
import { Hono } from 'hono'
import { cors } from 'hono/cors'
const app = new Hono()
// 開啟 CORS
app.use('/*', cors())
// 首頁
app.get('/', (c) => c.text('OAuth 2.0 Mock Server is Running! 🚀'))
// 1. Authorization Endpoint - 顯示同意頁面
app.get('/authorize', async (c) => {
const redirectUri = c.req.query('redirect_uri')
const state = c.req.query('state') || ''
const clientId = c.req.query('client_id') || 'Unknown App'
// 返回 HTML 同意頁面
const html = `
<h1>授權請求</h1>
<p>${clientId} 想要存取您的帳戶</p>
<form method="POST" action="/authorize">
<input type="hidden" name="redirect_uri" value="${redirectUri}">
<input type="hidden" name="state" value="${state}">
<button name="action" value="allow">授權</button>
<button name="action" value="deny">拒絕</button>
</form>
`
return c.html(html)
})
// 2. 處理同意/拒絕
app.post('/authorize', async (c) => {
const body = await c.req.parseBody()
const redirectUri = body['redirect_uri'] as string
const state = body['state'] as string || ''
const action = body['action'] as string
const callbackUrl = new URL(redirectUri)
if (action === 'allow') {
const code = 'mock_code_' + crypto.randomUUID().split('-')[0]
callbackUrl.searchParams.set('code', code)
} else {
callbackUrl.searchParams.set('error', 'access_denied')
}
if (state) callbackUrl.searchParams.set('state', state)
return c.redirect(callbackUrl.toString())
})
// 3. Token Endpoint
app.post('/token', async (c) => {
return c.json({
access_token: 'mock_access_token_' + Date.now(),
token_type: "Bearer",
expires_in: 3600,
refresh_token: "mock_refresh_token_xyz",
scope: "user:email"
})
})
// 4. UserInfo Endpoint
app.get('/userinfo', (c) => {
const authHeader = c.req.header('Authorization')
if (authHeader?.startsWith('Bearer ')) {
return c.json({
sub: "1234567890",
name: "Mock User",
email: "[email protected]",
email_verified: true
})
}
return c.json({ error: "Unauthorized" }, 401)
})
// 5. OIDC Discovery
app.get('/.well-known/openid-configuration', (c) => {
const baseUrl = new URL(c.req.url).origin
return c.json({
issuer: baseUrl,
authorization_endpoint: baseUrl + '/authorize',
token_endpoint: baseUrl + '/token',
userinfo_endpoint: baseUrl + '/userinfo'
})
})
export default app
部署到 Cloudflare
方法一:手動部署
npx wrangler login
npx wrangler deploy
方法二:Git 整合自動部署
- 將專案 push 到 GitHub
- 在 Cloudflare Dashboard 選擇 Workers & Pages → Create
- 連線到 GitHub repository
- 設定:
- 組建命令:
bun install - 部署命令:
npx wrangler deploy
- 組建命令:
每次 push 都會自動部署!
設定自訂域名
如果你有自己的域名(例如 oauth.example.com):
- 去 Workers & Pages → 選擇你的 Worker
- Settings → Triggers → Custom Domains
- Add Custom Domain → 輸入子域名
Cloudflare 會自動設定 DNS 並啟用 SSL。
如何使用
環境變數設定
OAUTH_CLIENT_ID="test_client_id"
OAUTH_CLIENT_SECRET="test_client_secret"
OAUTH_AUTH_URL="https://your-worker.workers.dev/authorize"
OAUTH_TOKEN_URL="https://your-worker.workers.dev/token"
OAUTH_USERINFO_URL="https://your-worker.workers.dev/userinfo"
測試流程
-
Authorization:瀏覽器訪問
https://your-worker.workers.dev/authorize?client_id=MyApp&redirect_uri=http://localhost:3000/callback -
點擊「授權」,會跳轉到:
http://localhost:3000/callback?code=mock_code_abc123 -
Exchange Token:
curl -X POST https://your-worker.workers.dev/token -
Get UserInfo:
curl -H "Authorization: Bearer token" https://your-worker.workers.dev/userinfo
進階:模擬異常情況
這是 Mock Server 的真正價值 - 測試 Negative Cases:
1. Token 快速過期
expires_in: 5, // 5 秒後過期
2. 模擬錯誤回應
app.post('/token', async (c) => {
return c.json({ error: "invalid_grant" }, 400)
})
3. 模擬網路延遲
app.post('/token', async (c) => {
await new Promise(r => setTimeout(r, 5000)) // 5 秒延遲
// ...
})
結語
透過 Hono + Cloudflare Workers,我們可以在幾分鐘內建立一個功能完整的 Mock OAuth Server。這對於:
- ✅ 開發 MCP Client
- ✅ 測試 OAuth 整合
- ✅ 模擬異常情況
- ✅ CI/CD 自動化測試
都非常有幫助。
完整程式碼:GitHub Repository
- ← Previous
2026 年混合雲部署策略:Cloudflare + Vercel + Railway + Fly.io + Neon 完整架構指南 - Next →
HTTP Authorization Header 深入解析:為什麼一定要加 Bearer?