Next.js + ClerkでログインしたユーザーをRailsバックエンドで認証・検証する方法
初めまして!
スペースマーケットでインターンをしている@aki_apintです!
今回は、
Next.js × Clerk のログインは簡単にできたけど、
「Rails側で、Clerkユーザーってどう取得するの?」って話。
個人開発で使っている Next.js + Clerk + Rails の構成。
フロントのClerkは一瞬で導入できたのに、バックエンドのRailsとどうつなぐかでめちゃくちゃハマりました。
同じように「Clerk、簡単そうなのにRailsとどう連携するの?」と悩んでいる人に向けて、
連携の仕組みと、実際に僕が実装した構成をまとめました!
少しでも助けになれれば嬉しいです!!
この記事でやること
- Clerkログイン済みユーザーのJWTを取得
- JWTをRailsに送信して検証
- Rails側でユーザーIDを取得
やりたいこと
フロントでClerkにログインしたユーザーを、バックエンド(Rails)側で取得して、投稿などのテーブルと紐付けたい!
背景
自身のサービスに、Googleアカウントなどでログインしたい!
ということでOAuth認証を実装することに。
調べていったら、Clerkが簡単そう!
(爆速でNext.jsに実装可能だって!?)
Clerkとは、Webアプリケーションの認証・認可を簡単に行えるプラットフォーム。
手軽にOAuth認証などを実装できるほか、
Next.js、React、Remixなどのフロントエンドフレームワーク向けのSDKを提供しており、高機能な認証処理を効率的に実装できる。
ほんとに一瞬でNext.jsに実装できた!
じゃあこのままRails側でもClerkのユーザーを使って投稿機能を実装しよう!って、
バックエンドでClerkのユーザー情報をどうやって取得するんだ??
そもそもClerkから、どうやってユーザー情報を取得するの??
Clerkでは、ユーザーがログインすると**JWT(JSON Web Token)**付きのセッションが生成されます。バックエンド(例:RailsやNode.jsなど)では、このJWTを使ってユーザーを認証し、ユーザー情報を取得できます。
つまり、Clerkが生成するJWTを検証すれば、「このユーザーは誰なのか?」がわかるという仕組み!
JWTって何?
JWTとは?
JWT(JSON Web Token)は、主に認証や認可のために利用されるトークン形式です。JSON形式で、HTTPヘッダーに格納して送受信を行えます。ユーザーの情報を含んだ署名付きのトークンで、サーバーはこのトークンを受け取り、JWKを使って検証を行うことで「正しいユーザーかどうか」判断できます。
JWKとは?
JWK(JSON Web Key)とは、JWTの検証を行うための公開鍵のことで、Clerkのダッシュボードから取得できます。
つまり、JWKがないとJWTを検証できない!
環境変数として**.env**などで宣言しておき、Rails側で取得してそれを元に署名をチェックを行います。
具体的な実装
ここまでで、ようやくバックエンド(Rails)側でClerkのユーザー情報を取得する方法がわかったので、いよいよ詳しい実装内容に入っていきます!
1. フロントでJWTを取得してバックエンドに送る
フロントでのJWTの取得
ではまず、フロント(Next.js)でログインしているClerkユーザーのJWTを取得しましよう。
ユーザーコンポーネントでのClerkのJWTの取得方法は以下のようになります。
'use client'
import { useAuth } from "@clerk/nextjs";
const { getToken } = useAuth();
const token = await getToken(); // ←ここでClerkのJWTを取得
バックエンドへJWTを送る
バックエンド(Rails)側へJWTを送り検証を行ってもらうためには、先ほど取得したJWTをAuthorizationのBearerトークンとしてヘッダーに設定し送る必要があります。
// バックエンドAPIにユーザー確認をリクエスト
const res = await fetch(`http://localhost:3001/api/user_check`, { // バックエンドAPIのエンドポイント
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`, // 先ほど取得したJWTをセット
}
})
フロント(Next.js)側のコード全体
上の処理を含めたコード全体は以下のようになります!
'use client'
import { useEffect } from "react";
import { useAuth } from "@clerk/nextjs";
export default function CheckUserPage() {
const { getToken } = useAuth();
useEffect(() => {
const checkUser = async () => {
const token = await getToken(); // ←ここでClerkのJWTを取得
if (!token) return;
const res = await fetch(`http://localhost:3001/api/user_check`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`, // ←ヘッダーにBearerトークンとして格納
'Content-Type': 'application/json'
}
});
if (res.ok) {
console.log("✅ ユーザー検証成功");
} else {
console.error("❌ ユーザー検証失敗");
}
};
checkUser();
}, []);
return (
<div className="text-center mt-10 text-lg">
ユーザー確認中です... // ユーザーの確認中に表示
</div>
);
}
2. バックエンド(Rails)側での前準備
先ほどまででフロント側の実装ができたので、今度はバックエンド側でJWTを検証するロジックを実装していきましょう!
前準備として、処理で使う環境変数を設定する必要があります!
ここで設定するのは、Clerkのダッシュボードで取得できるJWK(公開鍵)です。
ClerkのダッシュボードでJWKを取得
- まず、Clerkダッシュボードでconfigを開きます
- 次に、API Keyセクションを選択し、右側にあるJWKs public Keyをコピーします。
環境変数の設定
先ほど取得したJWKを.envファイルに設定します。
# /.env
CLERK_JWKS_URL=https://possible-lacewing-XXXXXXXXXXXXX
(↑先ほど取得したJWK)
3. バックエンド(Rails)側でJWTの検証
前準備が終わったので、検証処理の実装に移ります。
JWT検証処理
- Clerkの公開鍵(JWK)セットを取得し、JSONからRubyのハッシュ(配列)に変換。
jwks_raw = Net::HTTP.get(URI(jwks_url)) # ClerkのJWKのURLを叩き鍵セットを取得
jwks_keys = JSON.parse(jwks_raw)["keys"] # rubyのハッシュ型に変換
- JWT ライブラリが理解できる形式に変換、複数の鍵がある場合にも対応。
jwk_set = JWT::JWK::Set.new(jwks_keys.map { |jwk| JWT::JWK.import(jwk) })
- JWTをdecode(デコード)する。
ClerkのJWT署名アルゴリズムとしてRS256を指定し、先ほど取得した鍵セットを使ってJWTを検証する。decoded_tokenに結果を格納。(decoded_tokenはハッシュとなっている)
decoded_token, = JWT.decode(token, nil, true,
algorithms: ["RS256"],
jwks: jwk_set
)
JWTを検証するサービスクラスのコード全体
JWTの検証ロジック
# app/services/clerk/jwt_verifier.rb
require 'net/http'
require 'uri'
require 'json'
require 'jwt'
module Clerk
class JwtVerifier
def self.verify(token)
jwks_url = ENV.fetch("CLERK_JWKS_URL", "https://api.clerk.dev/.well-known/jwks.json")
jwks_raw = Net::HTTP.get(URI(jwks_url))
jwks_keys = JSON.parse(jwks_raw)["keys"]
jwk_set = JWT::JWK::Set.new(jwks_keys.map { |jwk| JWT::JWK.import(jwk) })
decoded_token, = JWT.decode(token, nil, true,
algorithms: ["RS256"],
jwks: jwk_set
)
decoded_token
rescue => e
Rails.logger.error "❌ Clerk認証失敗: #{e.class} - #{e.message}"
nil
end
end
end
JWT検証を行うクラスの使用例
フロントからのレスポンスからJWTを取り出す
まず、フロントからのリクエストに、AuthorizationのBearerトークンとしてヘッダーに設定されているJWTを取得します。
token = request.headers["Authorization"]&.split&.last # リクエストからJWT部分を取得
JWT検証クラスの呼び出し
先ほど作成したクラスによって、以下の形式でJWT検証処理を呼び出しる用になっています!
payload = Clerk::JwtVerifier.verify(token)
user_id = payload["sub"] # 認証OK: user_idをもとにUserを探すなど
ここまで来ればようやくJWTの検証を行う全ての準備が完了しました!!
以下は、JWTの検証を行う例の紹介になります!
ps:JWT検証の使用例
以下はJWT検証の使用例になります。
コントローラーでJWT検証を全てのアクションに対して行う
リクエストからトークンを取り出し、先ほどのJWT検証関数を呼び出してUserの認証を行うという処理を、全てのアクション(ページの読み込み)前に実行するよう指定しています。
こうすることで、ログインが確認できるユーザーのみがアクションを行えるようになります!
class Api::UsersController < ApplicationController
before_action :authenticate_clerk! # 全てのアクション前にJWT検証を行うよう指定
# POST /users
def create
end
# GET /users/:id
def show
user = User.find(params[:id])
render json: user
end
# /users/:id (※本人のみ更新可)
def update
if current_user.update(user_params)
render json: current_user
else
render json: { error: current_user.errors.full_messages }, status: :unprocessable_entity
end
end
private
# ユーザー登録/更新に使うパラメータ
def user_params
params.require(:user).permit(:username, :profile)
end
# Clerk JWT を検証して current_user をセット
def authenticate_clerk!
token = request.headers["Authorization"]&.split&.last #リクエストからJWT部分を取得
return head :unauthorized unless token
decoded_token = Clerk::JwtVerifier.verify(token) # JWT検証クラスの呼び出し、
return head :unauthorized unless decoded_token
clerk_user_id = decoded_token["sub"] # デコード結果からUserIDの部分を取得
Rails.logger.debug "Clerk User ID: #{clerk_user_id}"
return head :unauthorized unless clerk_user_id
@current_user_clerk_id = clerk_user_id
end
end
まとめ
今回は、Clerkでログインしたユーザーを、JWTを使用してRails側で検証する仕組みについて紹介しました。
- フロントでJWTを取得
- ヘッダーのAuthorizationとしてJWTを設定し、リクエスト
- バックエンドでJWKを使いJWTを検証
- コントローラーでの使用例
発展として、独自のデータベースを使用して、投稿などのテーブルとClerkのユーザーを紐付ける場合は、ClerkのUserIDをJWTを検証して取得し、自前のUserテーブルに格納する必要があります。
今回説明した部分でもClerkのUserIDを取得する部分があるので、そちらを自前のUserテーブルに格納していただければと思います!
読んでいただきありがとうございました!
この記事が、同じように「Next.js + Clerk + Rails」で悩んでいる方の助けになれば嬉しいです。

スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion