7.2 KiB
Auth provider spike: Cloudflare Workers OAuth
Decision summary
Choose Better Auth as the preferred auth route implementation for Heygo, using its Cloudflare Workers/D1-compatible runtime path and configuring only Google and GitHub social providers for MVP.
For this task, the code change is intentionally small: worker/auth.ts records provider/session constants and cookie policy helpers for the next session-guard task. We are not installing Better Auth or implementing the OAuth callback flow yet, because Task 7 is the session guard and there are no production OAuth secrets or remote Cloudflare resources in this workspace.
Apple is disabled by default. Add Sign in with Apple only after confirming an active paid Apple Developer Program membership and the required web credentials exist.
Chosen auth approach for this project
- Primary path: Better Auth on Cloudflare Workers with D1.
- Better Auth currently publishes as
better-auth@1.6.19. - Its docs describe D1/SQLite support through the built-in Kysely/D1 path, programmatic migrations for Cloudflare/serverless environments, social providers, and custom table/column names through
modelName/fields. - It supports Google and GitHub social provider configuration in code.
- Better Auth currently publishes as
- Project integration style: mount Better Auth under an API prefix such as
/api/auth/*in the Worker, backed by the existingDBD1 binding. - MVP session guard: Task 7 should read and validate the existing
sessionstable directly using an HttpOnly cookie token, so privatemy.heygo.ccroutes can be protected before the full OAuth login/callback route is wired. - Fallback if Better Auth schema/runtime integration blocks implementation: manual OAuth authorization-code flow using Workers-native
fetch, Web Crypto, and D1. Auth.js with@auth/d1-adapterremains a secondary fallback but is less attractive because its D1 adapter creates fixed core table names (accounts,sessions,users,verification_tokens) and does not fit the existingoauth_accountstable as cleanly.
Why this fits Cloudflare Workers
- Uses standard Web APIs available in Workers (
Request,Response,fetch,URL,Headers, Web Crypto) rather than Node-onlyfs, rawnet, or NodecryptoAPIs. - Can use the existing Cloudflare D1 binding (
env.DB) as the system of record for users, OAuth accounts, and sessions. - Better Auth docs explicitly cover database-backed auth, D1/SQLite migrations, and programmatic migration support for Cloudflare/serverless contexts.
- No new package was installed for this spike, avoiding lockfile churn before OAuth routes are implemented.
Mapping to existing D1 tables
Existing migration tables are already close to the MVP auth model:
users
Use as the canonical application user table.
| Existing column | Auth meaning |
|---|---|
id |
app user id |
email |
provider email, nullable for providers that do not return/verify email |
name |
display name |
image_url |
avatar/profile image URL |
role |
app authorization role (user/admin) |
created_at, updated_at |
audit timestamps |
oauth_accounts
Use as the provider account-link table.
| Existing column | Auth meaning |
|---|---|
id |
local account-link id |
user_id |
foreign key to users.id |
provider |
google, github, or future apple |
provider_account_id |
stable subject/id from the OAuth provider |
created_at |
account-link creation timestamp |
If Better Auth requires additional account token columns for refresh/access/id tokens, add nullable columns in a future migration rather than storing tokens in code or secrets files.
sessions
Use as the session table for the Worker session guard.
| Existing column | Auth meaning |
|---|---|
id |
session row id |
user_id |
foreign key to users.id |
session_token_hash |
hash of the opaque cookie token; never store raw cookie token |
expires_at |
absolute session expiry |
created_at |
session creation timestamp |
Task 7 should query this table by a hash of the cookie token, reject expired sessions, and join/load the associated users row.
Cookie/session policy
Session cookie name: heygo_session.
Production cookie attributes:
HttpOnly; Secure; SameSite=Lax; Path=/; Domain=.heygo.cc
Rationale:
HttpOnlyprevents client-side JavaScript from reading the session token.Securerestricts transmission to HTTPS.SameSite=Laxsupports ordinary top-level OAuth redirects while reducing CSRF exposure.Path=/makes the cookie available to the whole app.Domain=.heygo.ccallows the session set onheygo.ccto be visible tomy.heygo.cc.
Local development fallback: keep the same attributes but omit Domain for localhost/non-heygo hosts so browsers accept the host-only cookie.
Provider support
Enabled for MVP:
- Google OAuth
- GitHub OAuth
Disabled by default:
- Apple / Sign in with Apple
worker/auth.ts now encodes this as:
AUTH_ENABLED_PROVIDERS = ['google', 'github']
AUTH_DISABLED_PROVIDERS = ['apple']
Apple checkpoint
Do not block MVP on Apple.
Sign in with Apple for web requires Apple developer credentials such as:
- Service ID
- Team ID
- Key ID
- private key (
.p8)
Current research indicates an active paid Apple Developer Program membership is likely required for the resources needed to configure Sign in with Apple for web. That program is commonly priced at $99/year. Apple should remain disabled until the account and credentials are confirmed.
Secrets needed later
Set these as Wrangler/Cloudflare secrets or local development variables when OAuth routes are implemented. Do not commit them.
Required for Google/GitHub MVP:
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGITHUB_CLIENT_IDGITHUB_CLIENT_SECRETSESSION_SECRET
Optional only if Apple is enabled later:
APPLE_CLIENT_ID/ Service IDAPPLE_TEAM_IDAPPLE_KEY_IDAPPLE_PRIVATE_KEY
Next implementation steps for Task 7 session guard
- Add
SESSION_SECRETto the WorkerEnvtype only when code needs it. - Parse
Cookieheaders and extractheygo_session. - Hash the opaque token with Workers-compatible Web Crypto; do not use Node
crypto. - Query D1
sessionsbysession_token_hashand requireexpires_atto be in the future. - Load the matching
usersrow and expose a typed authenticated user/session context to protectedmy.heygo.ccroutes. - Return 401/redirect for missing, invalid, or expired sessions.
- Keep
/api/auth/*reserved for the later OAuth route implementation.
Verification notes
Research checked during this spike:
npm view better-authreports1.6.19and a dependency stack centered on Web-compatible TypeScript packages such as Kysely/Jose/Noble.- Better Auth docs/search results describe social providers, D1 support, programmatic migrations for Cloudflare/serverless, and custom table/column names.
- Auth.js D1 adapter docs confirm it is available for D1 but creates fixed core tables including
accounts,sessions,users, andverification_tokens. - Apple/third-party provider docs confirm the web credential shape for Sign in with Apple and the likely paid Apple Developer Program requirement.