本記事では、Vercel の Preview デプロイメントと Production デプロイメント間で Cookie を共有する方法を解説します。特に、Better Auth の oAuthProxy プラグインを使用して Preview 環境でも OAuth 認証を動作させるために必要な設定について詳しく説明します。
背景: なぜ Cookie 共有が必要なのか
oAuthProxy の仕組み
Better Auth の oAuthProxy プラグインは、開発環境や Preview 環境で OAuth 認証を動作させるためのプラグインです。
OAuth プロバイダー(Google など)は、セキュリティ上の理由からリダイレクト URI として登録できる URL に制限があります。動的に変わる Preview URL(例: https://my-app-git-feature-xxx.vercel.app)をすべて登録することは現実的ではありません。
oAuthProxy は以下のフローで動作します:
1. Preview で「ログイン」クリック
↓
2. Google OAuth 開始(state を Cookie に保存)
↓
3. Google が Production の callback URL にリダイレクト
(Production URL は Google に登録済み)
↓
4. Production が認証を処理し、Preview にリダイレクト
↓
5. Preview でセッション Cookie を確認してログイン状態を維持問題: Production は Preview の Cookie を読めない
このフローには致命的な問題があります。ステップ 4 で Production サーバーが Preview の Cookie(state)を読み取る必要がありますが、vercel.app のサブドメイン間では Cookie を共有できません。
Public Suffix List(PSL)とは
PSL の概要
**Public Suffix List(PSL)**は、ブラウザが Cookie のスコープを決定するために参照するリストです。
通常、Cookie は親ドメインに対して設定でき、サブドメイン間で共有できます:
example.com に設定した Cookie
├─ app.example.com ✅ 共有可能
├─ api.example.com ✅ 共有可能
└─ staging.example.com ✅ 共有可能しかし、PSL に登録されているドメインは「公開サフィックス」として扱われ、そのサブドメイン間での Cookie 共有が禁止されます。
なぜ PSL が必要なのか
PSL がなければ、悪意のあるサイトが他のユーザーのサイトの Cookie にアクセスできてしまいます:
悪意のあるサイト: evil.vercel.app
ターゲット: victim.vercel.app
もし PSL がなければ:
evil.vercel.app が vercel.app に Cookie を設定
→ victim.vercel.app でもその Cookie が読み取れてしまうこれを防ぐため、vercel.app、github.io、netlify.app などの共有ホスティングドメインは PSL に登録されています。
vercel.app は PSL に登録されている
publicsuffix.org で確認すると、vercel.app が登録されていることがわかります:
// Vercel, Inc : https://vercel.com/
// Submitted by Connor Davis <connor@vercel.com>
vercel.app
vercel.dev
now.shこれにより、以下のような Cookie 共有は不可能です:
linto-dev.vercel.app に設定した Cookie
├─ linto-dev-git-feature.vercel.app ❌ 共有不可
└─ linto-dev-xxx.vercel.app ❌ 共有不可解決策: カスタムドメインの使用
カスタムドメインなら Cookie 共有が可能
カスタムドメイン(例: example.com)は PSL に登録されていないため、サブドメイン間で Cookie を共有できます:
example.com に設定した Cookie
├─ preview-feature.example.com ✅ 共有可能
├─ staging.example.com ✅ 共有可能
└─ app.example.com ✅ 共有可能Vercel Preview Deployment Suffix
Vercel では Preview Deployment Suffix という機能を使用して、Preview デプロイメントにカスタムドメインを割り当てることができます。
この機能を使用すると、Preview URL が以下のように変わります:
変更前(デフォルト):
https://my-app-git-feature-username.vercel.app
変更後(カスタムドメイン):
https://my-app-git-feature.preview.example.com注意: Preview Deployment Suffix は Vercel の Pro プランまたは Enterprise プランで利用可能です。
設定手順
1. Vercel でカスタムドメインを追加
- Vercel ダッシュボードでプロジェクトを選択
- Settings → Domains に移動
- カスタムドメイン(例:
example.com)を追加 - DNS レコードを設定
2. Preview Deployment Suffix を設定
- Settings → Domains に移動
- Preview Deployment Suffix セクションを探す
- サブドメインを入力(例:
preview.example.com) - DNS で以下のレコードを追加:
*.preview.example.com CNAME cname.vercel-dns.com3. Better Auth で crossSubDomainCookies を設定
src/lib/auth.ts を更新して、Cookie がサブドメイン間で共有されるように設定します:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { oAuthProxy } from "better-auth/plugins";
import { db } from "@/db";
import { env } from "@/env";
// 本番ドメイン
const PRODUCTION_DOMAIN = "example.com";
const PRODUCTION_URL = `https://app.${PRODUCTION_DOMAIN}`;
export const auth = betterAuth({
baseURL: env.BETTER_AUTH_URL,
trustedOrigins: [
`https://*.${PRODUCTION_DOMAIN}`, // すべてのサブドメインを信頼
],
database: drizzleAdapter(db, {
provider: "sqlite",
}),
advanced: {
// サブドメイン間で Cookie を共有
crossSubDomainCookies: {
enabled: true,
domain: `.${PRODUCTION_DOMAIN}`, // 先頭のドットが重要
},
},
plugins: [
oAuthProxy({
currentURL: env.BETTER_AUTH_URL,
productionURL: PRODUCTION_URL,
}),
],
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
redirectURI: `${PRODUCTION_URL}/api/auth/callback/google`,
},
},
});crossSubDomainCookies の設定詳細
| オプション | 説明 | 例 |
|---|---|---|
enabled | クロスサブドメイン Cookie を有効化 | true |
domain | Cookie のドメイン属性。先頭にドットを付ける | .example.com |
重要:
domainの先頭にドット(.)を付けることで、そのドメインのすべてのサブドメインで Cookie が共有されます。
完全な設定例
環境変数
# .env.local(ローカル開発)
BETTER_AUTH_URL=http://localhost:3000
# .env.production(本番)
BETTER_AUTH_URL=https://app.example.com
# Preview 環境では Vercel が自動設定
# BETTER_AUTH_URL=https://my-app-git-feature.preview.example.comGitHub Actions での設定
.github/workflows/preview-deploy.yml:
- name: Deploy to Vercel
run: |
BRANCH_SLUG=$(echo "${{ github.head_ref }}" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]-')
PREVIEW_URL="https://my-app-git-${BRANCH_SLUG}.preview.example.com"
vercel deploy --prebuilt \
--env BETTER_AUTH_URL=$PREVIEW_URL \
--token=${{ secrets.VERCEL_TOKEN }}セキュリティ上の考慮事項
trustedOrigins の設定
crossSubDomainCookies を有効にする場合、trustedOrigins でサブドメインのワイルドカードを許可する必要があります:
trustedOrigins: [
`https://*.${PRODUCTION_DOMAIN}`,
],これにより、以下のオリジンからのリクエストが許可されます:
https://app.example.comhttps://preview-feature.example.comhttps://staging.example.com
Cookie のセキュリティ属性
Better Auth は自動的に以下のセキュリティ属性を設定します:
| 属性 | 値 | 説明 |
|---|---|---|
Secure | true | HTTPS でのみ送信 |
HttpOnly | true | JavaScript からアクセス不可 |
SameSite | Lax | CSRF 対策 |
Domain | .example.com | サブドメイン間で共有 |
サブドメインの管理
カスタムドメインを使用する場合、そのドメイン配下のすべてのサブドメインを管理する責任が生じます。第三者がサブドメインを悪用できないよう、DNS とサーバー設定を適切に管理してください。
代替案: oAuthProxy を使用しない方法
カスタムドメインを使用できない場合、以下の代替案を検討してください:
1. Preview 環境で OAuth を無効化
最もシンプルな解決策です。Preview 環境ではテストアカウントでのログインのみを許可します:
// src/lib/auth.ts
const isPreview = env.VERCEL_ENV === "preview";
export const auth = betterAuth({
// Preview では OAuth を無効化
socialProviders: isPreview ? {} : {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
},
});2. 共有ストレージ(Redis)を使用
Redis などの外部ストレージを使用して、Preview と Production 間で OAuth state を共有します:
import { redis } from "@/lib/redis";
export const auth = betterAuth({
account: {
stateStore: {
async get(state) {
return await redis.get(`oauth:state:${state}`);
},
async set(state, value, expiresIn) {
await redis.set(`oauth:state:${state}`, value, "EX", expiresIn);
},
async delete(state) {
await redis.del(`oauth:state:${state}`);
},
},
},
});3. 単一データベースの使用
Preview と Production で同じデータベースを使用する場合、oAuthProxy は問題なく動作します。ただし、データの分離ができなくなるため、本番データへの影響に注意が必要です。
まとめ
Vercel の Preview デプロイメントと Production デプロイメント間で Cookie を共有するには、以下の設定が必要です:
- カスタムドメインの取得: PSL に登録されていないドメインを使用
- Preview Deployment Suffix の設定: Preview URL にカスタムドメインを適用
- crossSubDomainCookies の有効化: Better Auth でサブドメイン間の Cookie 共有を設定
この設定により、oAuthProxy を使用した OAuth 認証が Preview 環境でも正常に動作します。
| 方法 | メリット | デメリット |
|---|---|---|
| カスタムドメイン + crossSubDomainCookies | 完全な OAuth 対応 | Pro プラン以上が必要 |
| Preview で OAuth 無効化 | シンプル、追加コストなし | Preview で OAuth テスト不可 |
| Redis 共有ストレージ | 既存ドメインで動作 | 追加インフラが必要 |
| 単一データベース | 追加設定不要 | データ分離ができない |
プロジェクトの要件と予算に応じて、適切な方法を選択してください。