1. はじめに
初めまして!
株式会社サードスコープでWebエンジニアをやっている田岡と申します。
Qiitaはおろか技術記事の執筆すら初めてなので、拙い点もあるかと思いますがあしからず...
さて、今回はとある社内プロジェクトでクロスドメインでのセッション共有を実現するにあたり、ClerkのSatellite Domains機能というものを触ってみましたのでその紹介とNext.jsでの具体的な実装例をお見せできればと思います!
2. ClerkのSatellite Domainsとは?
ClerkはAuth0とSupabase Authの中間くらいの機能性を持ちながら、圧倒的に手軽な開発体験を提供するIDaaSです。特にNext.jsとの相性が良く、Clerkが提供する専用UIを使えば5分程度で基本的な認証機能をセットアップすることができるみたいです。
そんなClerkが提供するSatellite Domainsは、1つのセッションを複数のドメインで共有できるようにする機能です。厳密には違うみたいですがSSOのようなイメージですね。
本来であればDNS設定やトークンの受け渡しなど複雑な設定・実装を行わないといけないところを、Clerk側にお任せして簡単にセットアップすることができます。さすが手軽さを売りにしているだけありますね。
基本的な仕組み
ClerkのSatellite Domains機能では、実際にサインインを行い認証情報が保存されるドメインをプライマリドメイン、プライマリドメインから認証状態を読み取ってセッションの検証を行うドメインをサテライトドメインと呼びます。
例えば、primary.devをプライマリドメイン、satellite.devをサテライトドメインとして設定した場合のフローは以下の通りです。
- ユーザーが
primary.devでサインイン - サインインに成功したら、
satellite.devにアクセス -
satellite.devはprimary.devから認証状態を透過的に取得 - ユーザーは
satellite.devでも認証済みの状態でアクセス可能 -
primary.devのセッションが切れた状態でsatellite.devにアクセスすると、primary.devのサインインページにリダイレクト
月間コスト
結論: $125/月
ClerkのSatellite Domains機能を使うには、ClerkのProプラン(50ドル/月)に加えて、Enhanced authentication add-on(100ドル/月)を購読する必要があります。
2025/10/15時点での料金になります。最新の料金情報は公式サイトを参照してください。
3. Next.jsでの実装例
ここでは、Next.js App Routerを使用した実装例を紹介します。
前提条件
- Clerkアカウントとアプリケーションが作成済み
- Next.jsプロジェクトを2つ(プライマリとサテライト)準備済み
-
@clerk/nextjsパッケージがインストール済み
ステップ1: Clerkダッシュボードでサテライトドメインを追加
- Clerkダッシュボードにアクセス
- Configureタブを選択
- サイドバーからDomainsを選択
- Satellitesを選択
- 「Add satellite domain」ボタンからサテライトドメインを追加する(以下の例を参照)
開発環境の例:
- プライマリドメイン:
localhost:3000 - サテライトドメイン:
localhost:3001
本番環境の例:
- プライマリドメイン:
primary.dev - サテライトドメイン:
satellite.dev
ステップ2: DNS設定(本番環境のみ)
本番環境でサテライトドメインを使用する場合、DNSプロバイダーでclerkサブドメインのCNAMEレコードを追加する必要があります。
- ClerkダッシュボードのDomainsページで追加したサテライトドメインを選択
- DNS Configurationセクションの指示に従ってCNAMEレコードを追加
- 設定が正しく完了すると、ドメインの横にVerifiedラベルが表示される
ステップ3: プライマリドメイン側の実装
環境変数(.env):
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key
CLERK_SECRET_KEY=your_secret_key
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
app/layout.tsx:
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ja">
<body>
<ClerkProvider
allowedRedirectOrigins={[
'http://localhost:3001', // 開発環境
'https://satellite.dev', // 本番環境
]}
>
{children}
</ClerkProvider>
</body>
</html>
)
}
app/sign-in/[[...sign-in]]/page.tsx:
import { SignIn } from '@clerk/nextjs'
export default function SignInPage() {
return (
<div className="flex items-center justify-center min-h-screen">
<SignIn />
</div>
)
}
ステップ4: サテライトドメインの設定
環境変数(.env.local):
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key
CLERK_SECRET_KEY=your_secret_key
NEXT_PUBLIC_CLERK_IS_SATELLITE=true
# 開発環境の例
NEXT_PUBLIC_CLERK_DOMAIN=localhost:3001
NEXT_PUBLIC_CLERK_SIGN_IN_URL=http://localhost:3000/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=http://localhost:3000/sign-up
# 本番環境の例
# NEXT_PUBLIC_CLERK_DOMAIN=satellite.dev
# NEXT_PUBLIC_CLERK_SIGN_IN_URL=https://primary.dev/sign-in
# NEXT_PUBLIC_CLERK_SIGN_UP_URL=https://primary.dev/sign-up
app/layout.tsx:
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ja">
<body>
<ClerkProvider>
{children}
</ClerkProvider>
</body>
</html>
)
}
middleware.ts:
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
// 公開ルートの設定(認証不要なページ)
const isPublicRoute = createRouteMatcher(['/'])
// サテライトアプリケーション用のオプション
const options = {
isSatellite: true,
signInUrl: 'http://localhost:3000/sign-in', // 開発環境
signUpUrl: 'http://localhost:3000/sign-up', // 開発環境
// 本番環境の例:
// signInUrl: 'https://primary.dev/sign-in',
// signUpUrl: 'https://primary.dev/sign-up',
domain: 'http://localhost:3001', // 開発環境
// 本番環境の例:
// domain: 'https://satellite.dev',
}
export default clerkMiddleware(async (auth, req) => {
if (isPublicRoute(req)) return // 公開ルートはスキップ
await auth.protect() // その他のルートは認証を要求
}, options)
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}
ステップ5: 動作確認
- プライマリドメイン(
localhost:3000)にアクセスし、サインイン - サテライトドメイン(
localhost:3001)にアクセス - 自動的に認証済みの状態でアクセスできることを確認
サテライトドメインで保護されたページにアクセスした場合、プライマリドメインのサインインページにリダイレクトされ、認証後に元のサテライトドメインのページに戻ります。
より正確な実装方法については公式ドキュメントをご参照ください
4. まとめ
クロスドメインでのセッション共有って聞くと「なんか大変そうだなぁ」って感じがしますが(実際それなりに大変ではある)、ここまで簡単に実装できてしまうClerkはさすがです。
MVP開発などサクッとアプリケーションを構築したい場合においては、IDaaSとして迷わず採用してもいいのではと思いました!