【Tips - Clerk】実例でわかるシリーズ
<前置き、挨拶>
みなさんどうも、こんばんみ~。ぎょうざです。
Webアプリ上などでユーザー認証機能を実装する際に便利なツール
Clerk(クラーク)を利用する際に、ぎょうざが躓いたところを備忘録として記録しておきます。
同じようなケースあるいは現象で困っている方の参考になれば幸いです。
【目次】
<Useful>
▶ 本番環境にむけて設定したこと(Google認証を用いた例)
※各自自身の環境に読みかえて下さい。
- DNS管理先の設定
Clerk側でProductionインスタンスを作成済みで、かつ自分の場合はCloudflareのドメイン管理ですでに、「gyozaless.com」の独自ドメインを取得済みで、サブドメインとして「to-gyoza-er.gyozaless.com」をDNS設定した前提です。デフォルトで登録使用すると、プロキシステータスがリバーシプロキシを設定してしまい、Clerk側にDNS適用情報が伝達しないので、DNSのみになっているか確認して下さい
- Clerkダッシュボード
ConfigureのDevelopersのDomains画面で、上記のDNS設定がインターネット側で適用が終わると、Clerk側でも適用済みがダッシュボード上で確認できます。これで本番環境でClerkの認証画面が開くようになります。ただし、Google側のOAuth認証も設定してあげないと、Googleでのログイン認証がまだ未実装の状態です
- GoogleのOAuth認証情報の取得
Google Cloudコンソールから、APIとサービス▶認証情報で、APIキーを発行すると「Client ID」が「Client Secret」が発行してもらえます
- 再び、Clerkダッシュボード
ConfigureのUser & AuthenticationのSSO connections画面で、上記でGoogle Cloudで発行したAPIキー各項目を入力してUpdateします。
以上で、Google認証を用いたClerkの本番環境設定が完了です。
最後に、Clerk(Productionインスタンス)側のOverview画面でログインユーザーを確認してみて下さい。
<Error>
▶ userId (※ぎょうざ独自にPrismaのSchemaに定義) をSupabaseのDBにPOSTできない
- 現象:TypeError: (0 , clerk_nextjs__WEBPACK_IMPORTED_MODULE_2_.useAuth) is not a function
- 原因:Internal Error 500
- 解決策:import元を
import { useAuth } “@clerk/nextjs”からimport { auth } “@clerk/nextjs/server”へ変更
▶ npm run dev で 開発コンソール上でGET / 307が連続で表示される
- 現象:ブラウザ表示画面上で、
ERR_TOO_MANY_REDIRECTSがエラー表示される - 原因:Next.jsとClerkの認証設定が競合してリダイレクトが繰り返されている 💡原因を推測するに、
middleware.tsのisPublicRouteとconfig.matcherの設定が競合していた可能性があります。具体的には、
-
localhost:3000にアクセスすると、middleware.tsが実行されます。 -
/がisPublicRouteに含まれているため、userAuth.protect()は実行されません。 - しかし、
config.matcherの設定により、localhost:3000はミドルウェアの対象となります。 - その結果、Clerk のミドルウェアが何らかの処理を行い、意図しないリダイレクトが発生していた可能性があります。
/をisPublicRouteから削除することで、localhost:3000へのアクセス時にuserAuth.protect()が実行されるようになり、Clerk のサインインページへのリダイレクトが正しく行われるようになったと考えられます。Clerk のミドルウェアは、認証が必要なページにアクセスがあった際に、ユーザーをサインインページにリダイレクトする役割を担います。
/をisPublicRouteに含めていた場合、ルートページへのアクセスも認証が必要と判断され、リダイレクトが発生していた可能性があります。ただし、
/をisPublicRouteから削除することで、ルートページへのアクセスが常に認証を要求されるようになります。もしルートページを認証なしでアクセスできるようにしたい場合は、別の方法で対応する必要があります。 -
- 解決策:isPublicRouteからルートページ、自分の場合は
'/'を削除
▶ Clerk Auth & Middleware: 「検知できません!」エラーとの戦い
▶ 現象:
APIルート(ルートハンドラ)内で await auth() (from @clerk/nextjs/server) を呼び出すと、「Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware()...」というエラーが発生する。
▶ 原因:
Clerkの auth() ヘルパーが正しく機能するためには、そのリクエストが事前に clerkMiddleware を通過し、認証コンテキストがセットアップされている必要がある。このエラーは、ミドルウェアが当該APIルートに対して実行されていない、または実行されてもコンテキストを適切に設定できていないことを示す。主な原因は middleware.ts の config.matcher の設定漏れか、ミドルウェア内のロジック。
▶ 解決策:
-
middleware.tsのconfig.matcherが、エラーの発生するAPIルート(例:/(api|trpc)(.*))を確実に含んでいることを確認する。
TypeScript// middleware.ts export const config = { matcher: [ '/((?!.+\\.[\\w]+$|_next).*)', // 静的ファイル等を除外 '/', // ルート '/(api|trpc)(.*)', // APIルートを含める ], }; -
middleware.tsのclerkMiddlewareコールバック関数内で、APIルートへの未認証アクセスに対しては、authObject.protect()でページリダイレクトを試みるのではなく、NextResponse.json({ error: '認証が必要です。' }, { status: 401 });のように明示的に401エラーを返すようにする。これにより、auth.protect()がAPIルートに対して予期せずnotFound()をトリガーするのを防ぐ。
TypeScript// middleware.ts const isApiRoute = createRouteMatcher(['/api/(.*)']); export default clerkMiddleware((authObject, request) => { // ... (isPublicRouteのチェック) ... const { userId, protect } = authObject; if (!userId) { // 未認証の場合 if (isApiRoute(request)) { return NextResponse.json({ error: '認証が必要です。' }, { status: 401 }); } return protect(); // ページならリダイレクト } return NextResponse.next(); });- Tips: ミドルウェア内の
auth引数(例ではauthObject)は、Clerkの認証情報を持つオブジェクトそのものであり、APIルート内のawait auth()とは呼び出し方が異なる点に注意。
- Tips: ミドルウェア内の
<Warn>
▶
〆の一言
Clerkは、ユーザー認証機能を簡単に実装できるツールとして優秀です。
メール認証だけにとどまらず、Google認証やSNS認証なども可能です。
ここまで読んでいただき、ありがとうございました。
以上、ぎょうざでした。