Standalone GitHub App authentication for a Rigel editor, with one shared auth core and thin Cloudflare Workers, Netlify Functions and Vercel entry points. No website framework, database, telemetry or sibling repository is required.
This template uses Apache-2.0. The npm package stays private: true to prevent accidental publication.
Platform deployments have not yet been verified. Run the live checklist below on each host before relying on it.
| File | Role |
|---|---|
src/auth.ts |
Shared authentication and security checks |
src/gateway.ts |
Common Hono app and gateway routes |
cloudflare/index.ts |
Worker default export |
src/index.ts |
Native Vercel Hono default export |
src/node.ts |
Shared Node Request handler with explicit runtime environment mapping |
netlify/functions/auth.ts |
Default Request-to-Response handler; netlify.toml forces root routes to this function |
wrangler.jsonc, netlify.toml, vercel.json |
Provider configuration; Vercel explicitly selects framework hono |
.env.example |
Variable names with all values blank |
Use Node 24 and pnpm 11.7.0.
pnpm install --frozen-lockfile
pnpm check
pnpm test
pnpm test:browserpnpm check runs TypeScript checking. pnpm test runs node --experimental-strip-types --test tests/*.test.ts. The browser suite uses native Chrome with mocked GitHub transport, not real credentials. Set CHROME_PATH if Chrome is installed elsewhere. pnpm build only typechecks; each provider bundles its entry point.
| Command | Local gateway | Underlying command |
|---|---|---|
pnpm dev:cloudflare |
http://localhost:8789 |
wrangler dev --port 8789 |
pnpm dev:netlify |
http://localhost:8888 |
pnpm --package=netlify-cli@27.14.2 dlx netlify dev --offline --port 8888 |
pnpm dev:vercel |
http://localhost:3000 |
pnpm --package=vercel@65.2.0 dlx vercel dev --listen 3000 |
Synthetic tests do not prove GitHub registration, deployed runtime behavior or editor integration. Vercel's CLI may require account/project setup even for local development.
Set these on the gateway's hosting runtime, never in the editor bundle or build output.
| Variable | Value |
|---|---|
GH_CLIENT_ID |
Required GitHub App client ID, not the numeric App ID |
GH_CLIENT_SECRET |
Required GitHub App client secret |
AUTH_SECRET |
Required independent cryptographically random secret, at least 32 characters; not a GitHub credential |
SITE_ORIGIN |
Required exact gateway origin, for example https://auth.example.com |
ALLOWED_ORIGINS |
Optional. Blank means public destination confirmation. An exact comma-separated list restricts destinations and skips confirmation. |
Generate AUTH_SECRET independently for each deployment, using a password manager or a local cryptographic generator, and save it directly to the host's secret store. Rotating it invalidates existing gateway cookies. Never put secrets, authorization codes or tokens in URLs, source control, logs or chat.
Origins must be canonical: scheme and hostname with an optional port, without a trailing slash, path, query, fragment, credentials or wildcard. For example, restricted mode can use https://editor.example.com,https://preview.example.com. An allowlist is not a repository access control list.
HTTPS is required except for explicit http://localhost:<port> development origins. HTTP 127.0.0.1 and IPv6 origins are rejected. The request host must match SITE_ORIGIN; a random provider preview URL is not interchangeable with it. Give a preview its own explicit origin, callback and runtime configuration if you need to test it.
For local development, manually copy the blank .env.example to ignored .dev.vars for Wrangler, or an ignored .env for the Node provider CLI. Fill it locally; do not commit it. Set SITE_ORIGIN to the chosen localhost gateway above and register that exact origin plus /callback with a separate development GitHub App. Restart the dev server after changing variables.
Follow GitHub's registration guide. Under your account or organization's Settings, open Developer settings, GitHub Apps, then New GitHub App.
- Choose your own unique app name and operator homepage, for example
https://example.com. - Set Callback URL to exactly
SITE_ORIGIN/callback, for examplehttps://auth.example.com/callback. Disable wildcard matching if offered. A gateway mounted under an/authpath prefix is not this template's contract. - Keep Expire user authorization tokens enabled. This gateway requires expiring GitHub App user tokens.
- Leave Request user authorization (OAuth) during installation off. Login must start from the editor so the gateway can establish state and the popup destination.
- Leave Device Flow off, Setup URL blank and Redirect on update off. Turn webhook Active off; leave webhook URL/secret and event subscriptions empty.
- Give repository Contents and Pull requests read/write access for editing and publishing reviews. Metadata is read-only. Use read-only Checks, Commit statuses and Deployments where the selected backend needs publish/review status. Leave unrelated repository, organization, account and enterprise permissions at no access.
- Choose Any account if other people or organizations must install your app; otherwise restrict it to your account. This setting does not control destination confirmation.
- Create the app, copy its client ID, then generate a client secret and save it only to the gateway runtime. This flow does not require an App private key.
- Install the app on the needed account and select only the repositories the editor should access. Organization approval may be required. Installation and repository grants are separate from user authorization; successful sign-in alone does not prove repository access.
The returned token is a GitHub App user access token, not an installation bot token. Its permissions are the intersection of the user's access and the app's installation permissions. It can cover every repository in that intersection, not just the repository selected in an editor. A public confirmation page cannot prove a destination runs Rigel. Confirm only editor origins you trust with those privileges.
Protocol references: user access tokens and PKCE, token expiry and refresh.
Choose a provider to create your own deployment. Register your GitHub App, set the runtime variables, and match its callback URL to your gateway's final domain. The buttons set up hosting; they do not register the GitHub App for you.
Workers does not support fetch with redirect: "error". The shared token exchange uses redirect: "manual" and rejects redirects and other unsuccessful responses; pnpm test verifies this in workerd.
- Connect your repository to Workers Builds, or use the Wrangler CLI from this directory. Keep
cloudflare/index.tsas the entry point inwrangler.jsonc. - Store all four required variables as Worker secrets, not build-only variables. Use
pnpm exec wrangler secret put NAMEfor each ofGH_CLIENT_ID,GH_CLIENT_SECRET,AUTH_SECRETandSITE_ORIGIN; each command prompts locally. Set optionalALLOWED_ORIGINSinwrangler.jsonc. Keeping the required values as secrets prevents later Wrangler deployments from overwriting dashboard-only variables. - Run
pnpm checkas the build check andpnpm exec wrangler deployas the deployment command, or deploy with that command yourself. Configure your chosen custom domain and make its origin matchSITE_ORIGINexactly. - Keep
observability.enabledfalse inwrangler.jsonc. Review upstream proxy and account logging separately.
- Import your repository into Netlify with its root as the base directory. Use Node 24, pnpm 11.7.0 and build command
pnpm build; retain the functions settings innetlify.toml. This is a function-only gateway, not a static website build. - Set all required variables in the site's environment settings with Functions/runtime availability. Build-only variables do not configure the running function. Use the correct production or preview context and redeploy after changes.
- Deploy through Netlify's Git integration or
pnpm --package=netlify-cli@27.14.2 dlx netlify deploy --build --prodafter linking your own site. Configure the domain and exactSITE_ORIGIN/GitHub callback pair. - Keep
netlify/functions/auth.tsas a modern Node Function returning a webResponsefrom a webRequest. Retain the forced catch-all rewrite innetlify.toml, which preserves the original request URL and auth error status codes. Do not add functionconfig.pathrouting or an SPA rewrite over the gateway routes.
See Netlify's function setup. Provider request/function logs need a separate review; application silence does not mean Netlify collects no request metadata.
- Import your repository into Vercel with its root as the project directory. Use Node 24 and pnpm 11.7.0. Keep
framework: "hono"invercel.jsonand the default Hono export insrc/index.ts. - Set the required environment variables in Project Settings for the intended Production or Preview environment. Keep secrets server-side and redeploy after changes. Match the deployment's stable origin to
SITE_ORIGINand the GitHub callback. - Deploy through the Git integration or
pnpm --package=vercel@65.2.0 dlx vercel --prodafter linking your own project.pnpm buildis a typecheck, not a generated static output directory. - Use Vercel's native Hono support. Do not add framework scaffolding, an
api/wrapper or a second auth implementation.
Review Vercel's request logs, observability and any log drains independently. This template cannot promise that provider-level callback URL logging is universally disabled.
Set your editor's GitHub appSlug to the actual slug shown on your own GitHub App page, and authOrigin to your gateway's exact SITE_ORIGIN. Do not derive the slug from a suggested display name. Keep the configured content repository separate from these auth settings.
The popup opens GET /authorize?origin=<encoded editor origin>&nonce=<random caller nonce>. Use crypto.randomUUID() or another cryptographically random nonce of 32 to 128 URL-safe characters. Blank ALLOWED_ORIGINS shows an exact-destination confirmation before GitHub; restricted mode rejects unlisted origins and skips that page. The confirmation submits to POST /authorize; GitHub returns to GET /callback.
The callback posts { type: 'rigel:auth', provider: 'github', nonce, access_token, expires_in } to the recorded opener origin, or an error instead of token fields. The editor must check the exact gateway message origin, popup window source and caller nonce, then validate the payload. Clean up listeners on success, cancellation and timeout. Preserve the opener: do not use noopener for the sign-in popup, and do not isolate the editor with COOP same-origin. Use the default policy or same-origin-allow-popups; auth responses retain unsafe-none.
The gateway stores the refresh token only in an encrypted HttpOnly cookie. POST /refresh is bodyless, same-origin only and requires X-Rigel-CSRF: 1; it is not an editor cross-origin API. There is no automatic refresh across origins. Reopen the login popup when the editor's access token expires. Avoid concurrent refresh calls.
Rigel CMS 1.0.0 stores its access-token session in localStorage, separate from the gateway's HttpOnly refresh cookie. Same-origin editor scripts can read that session.
Repeat for Cloudflare, Netlify and Vercel. Use a development app and disposable repository first; tests that publish or save should have deliberate, reversible changes.
Run pnpm test:platform https://auth.example.com https://editor.example.com with your exact gateway and an allowed editor origin. This checks routing, method guards, hardened failures, destination confirmation or allowlist mode, PKCE and separate cookies. It never follows the GitHub redirect or exchanges real tokens; complete the real popup checks below yourself.
-
GET /healthreturns success. Check it separately from authentication: liveness alone proves neither configuration nor GitHub access. - Confirm the final HTTPS host,
SITE_ORIGIN, exact/callbackregistration and editorauthOrigin/appSlugagree. Review provider/proxy callback logging and remove analytics or URL logging from the auth path. - In public mode, start from the actual editor and verify the exact destination warning, cancellation and confirmation. In restricted mode, verify an allowed origin works and an unlisted origin fails.
- Complete real GitHub authorization in the popup. Confirm return to the opener, popup closure and successful editor identity. Deny a second attempt and verify a safe error without losing drafts.
- Inspect production cookies without copying values: host-only, Secure, HttpOnly and expected SameSite behavior. Verify state clearing and that a callback opened without valid state fails. Do not record callback URLs, codes, tokens or cookie values in screenshots or reports.
- Confirm installation/repository selection and any organization approval. Verify access to the intended repository, then save a reversible change and read it back. Verify the backend's publish/review operation separately from sign-in.
- Verify popup blocking and editor COOP behavior, rejected forged messages, and expiration/reauthentication. Cross-origin
/refreshmust fail; failed saves should preserve drafts. - Record the provider, tested routes and outcomes without secrets. Leave any untested deployment or flow marked unverified.
The application has no telemetry and must not log callback URLs or provider bodies. Hosting providers may still collect request metadata. Supply your own operator identity, privacy notice and terms, and review cookie/token handling and provider retention before public use. Deploying this template does not make Rigel's hosted-service policies apply to your service.
Copyright 2026 Alex Zappa (FreshJuice). Licensed under the Apache License, Version 2.0.
The software is provided "AS IS", without warranties or conditions of any kind. See LICENSE for the full terms and limitations of liability.