📢关注「麦序子」公众号,回复关键词 报价模板 免费领《装修报价 Excel 模板》 立即领取 →
Skip to content

Cloudflare Worker 给 Decap CMS 做 GitHub OAuth 网关

背景

Decap CMS(之前的 Netlify CMS)改文件需要 GitHub Token,自己写 OAuth 太麻烦,写一个 Worker 当网关就行。

实现原理

text
Decap CMS → /auth → Cloudflare Worker → github.com/login/oauth/authorize

                            github.com/login/oauth/access_token ← /callback

                                  Worker → Decap CMS(postMessage token)

完整代码

javascript
// cloudflare/decap-oauth-worker.js
// Decap CMS GitHub OAuth gateway for Cloudflare Workers

const GITHUB_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize'
const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token'

function html(body) {
  return new Response(body, {
    headers: {
      'content-type': 'text/html; charset=utf-8',
      'cache-control': 'no-store'
    }
  })
}

function popupScript(payload) {
  const safePayload = JSON.stringify(payload).replace(/</g, '\\u003c')
  return `<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>GitHub 登录完成</title></head>
<body>
<script>
(function() {
  var payload = ${safePayload};
  function send() {
    if (window.opener) {
      window.opener.postMessage('authorization:github:success:' + JSON.stringify(payload), '*');
    }
    window.close();
  }
  send();
  setTimeout(send, 500);
})();
</script>
<p>GitHub 登录完成,可以关闭此窗口。</p>
</body>
</html>`
}

function errorScript(message) {
  const safeMessage = JSON.stringify({ error: message }).replace(/</g, '\\u003c')
  return `<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>登录失败</title></head>
<body>
<script>
(function() {
  var payload = ${safeMessage};
  if (window.opener) {
    window.opener.postMessage('authorization:github:error:' + JSON.stringify(payload), '*');
  }
  window.close();
})();
</script>
<p>登录失败:${message}</p>
</body>
</html>`
}

export default {
  async fetch(request, env) {
    const url = new URL(request.url)

    if (!env.GITHUB_CLIENT_ID || !env.GITHUB_CLIENT_SECRET) {
      return new Response('Missing credentials', { status: 500 })
    }

    if (url.pathname === '/auth') {
      const scope = url.searchParams.get('scope') || 'repo,user'
      const siteId = url.searchParams.get('site_id') || 'maixuzi'
      const state = crypto.randomUUID() + ':' + siteId

      const redirect = new URL(GITHUB_AUTHORIZE_URL)
      redirect.searchParams.set('client_id', env.GITHUB_CLIENT_ID)
      redirect.searchParams.set('redirect_uri', `${url.origin}/callback`)
      redirect.searchParams.set('scope', scope)
      redirect.searchParams.set('state', state)

      return Response.redirect(redirect.toString(), 302)
    }

    if (url.pathname === '/callback') {
      const code = url.searchParams.get('code')
      if (!code) {
        return html(errorScript('Missing code'))
      }

      const tokenResponse = await fetch(GITHUB_TOKEN_URL, {
        method: 'POST',
        headers: {
          'accept': 'application/json',
          'content-type': 'application/json',
          'user-agent': 'maixuzi-decap-oauth'
        },
        body: JSON.stringify({
          client_id: env.GITHUB_CLIENT_ID,
          client_secret: env.GITHUB_CLIENT_SECRET,
          code,
          redirect_uri: `${url.origin}/callback`
        })
      })

      const tokenData = await tokenResponse.json()
      if (!tokenResponse.ok || tokenData.error || !tokenData.access_token) {
        return html(errorScript(tokenData.error_description || tokenData.error || 'Token exchange failed'))
      }

      return html(popupScript({ token: tokenData.access_token, provider: 'github' }))
    }

    return new Response('Not found', { status: 404 })
  }
}

配置

wrangler.toml

toml
name = "maixuzi-cms-oauth"
main = "decap-oauth-worker.js"
compatibility_date = "2026-06-20"

[vars]
GITHUB_CLIENT_ID = "xxx"

Secrets

bash
wrangler secret put GITHUB_CLIENT_SECRET
# 输入 GitHub OAuth App 的 client secret

GitHub OAuth App

GitHub → Settings → Developer settings → OAuth Apps → New OAuth App

配置
Homepage URLhttps://maixuzi.cn
Authorization callback URLhttps://maixuzi-cms-oauth.xxx.workers.dev/callback

Decap CMS 配置

yaml
backend:
  name: github
  repo: lingyun15/maixuzi-site
  branch: main
  base_url: https://maixuzi.cn
  auth_endpoint: auth  # Cloudflare Worker 路径

部署

bash
cd cloudflare
wrangler deploy

效果

可以在 https://maixuzi.cn/admin/ 用 GitHub 登录直接编辑内容,所有变更会 commit 到 main 分支,Cloudflare Pages 自动重新部署。

完整项目


更多内容:https://maixuzi.cn

Last updated:

💬加企微
领试用