エーデルハーツ

Custom WordPress Support & Development

WordPressの認証をSupabaseに分離する設計|複数サービスで共通ログインを実現

WordPressで会員サイトやログイン機能を実装する場合、多くのケースではWordPress標準のユーザー管理機能を使うことになります。

もちろん、それ自体は間違いではありません。WordPressには wp_users テーブル、Cookieによるログイン維持、権限管理、パスワード再発行といった仕組みが最初から用意されています。

ただ、実務では次のような悩みが出てくることがあります。

  • WordPress側に認証情報を集中させたくない
  • 一般会員と管理者のログイン導線を分けたい
  • 他サービスと共通の認証基盤を持ちたい
  • 使っていない会員データで wp_users を肥大化させたくない

この考え方を実装したのが、私が公開しているWordPressプラグイン Edel Auth for Supabase です。

プラグインの設計思想については、以下の記事でも詳しく紹介しています。

【公式リリース】Supabase認証WordPressプラグイン「Edel Auth for Supabase」

この記事では、WordPress標準の認証の仕組みを整理したうえで、なぜSupabaseのような外部認証を組み合わせる設計が有効なのか、そして実際にどのような点に注意して設計したのかを技術的な視点で解説します。

WordPressのユーザー認証はどう動いているのか

まず前提として、WordPress標準のログイン機能は決して単純な仕組みではありません。

ユーザー情報は主に wp_users テーブルと wp_usermeta テーブルに保存され、ログイン成功後はCookieによってセッション状態が維持されます。WordPressはログイン状態をもとにユーザーを識別し、さらにロールと権限を使って操作可能な範囲を制御しています。

wp_usersに保存されるもの

wp_users には、ユーザー名、メールアドレス、表示名、パスワードハッシュなどが保存されます。WordPressのパスワードは平文ではなくハッシュ化されて保存されるため、その点だけ見れば基本的な安全性は確保されています。WordPress 6.8ではパスワードハッシュの仕組みも強化され、bcrypt が標準で使われるようになりました。これは従来よりも現代的なパスワード保護に寄せた改善です。

ただし、ハッシュ化されているからといって、認証情報をWordPress側に集中させる設計が常に最適とは限りません。

たとえば、会員機能を持つサイトで外部サービス連携や独自アプリ連携まで考え始めると、認証基盤をWordPressに寄せすぎること自体が設計上の制約になる場合があります。

Cookieとログイン状態の仕組み

WordPressでは、ログイン後にブラウザへ認証用Cookieが発行され、そのCookieをもとに「このユーザーはログイン中か」を判定します。

つまり、普段私たちが「セッション」と呼んでいるものの実態は、WordPress内部の認証処理とCookieによるログイン維持の組み合わせです。

このため、ログインフォームやログアウト処理、認証後のリダイレクトなどをカスタマイズする際には、単に見た目を変えるだけではなく、Cookieベースのログイン状態と整合性が取れているかを意識する必要があります。

nonceは認証そのものではない

WordPress開発ではNonceがよく登場しますが、ここは混同しやすいポイントです。

Nonceは「そのリクエストが意図した画面から送られたものか」を確認するための仕組みであり、ユーザー本人確認そのものではありません。つまり、Nonceが通ったから安全というわけではなく、認証や権限確認とセットで考える必要があります。

特にログイン・登録・プロフィール更新のような処理では、Nonceだけで安心せず、現在のユーザー状態や権限をきちんと検証することが重要です。

権限管理は認証とは別に考える

もう一つ大切なのが、認証と認可を分けて考えることです。

認証は「あなたは誰か」を確認する処理で、認可は「あなたは何ができるか」を決める処理です。

WordPressではロールと権限によって認可を管理します。たとえば購読者、投稿者、編集者、管理者といったロールごとに、実行できる操作が異なります。

外部認証を導入する場合も、この境界は崩してはいけません。外部サービスでログインできたからといって、そのままWordPress内で強い権限を与えてよいわけではありません。

なぜWordPress標準の会員管理だけでは悩みが出やすいのか

WordPress標準のユーザー管理は優秀ですが、会員サイトを本格的に運用しようとすると、いくつかの悩みが出てきます。

会員サイトでは認証の責任が重い

通常のブログ運営であれば、ログインするのは管理者や編集者だけということも多いです。しかし会員サイトになると、一般ユーザーが大量にログインすることになります。

このとき、認証まわりの設計は一気に重要になります。

たとえば、パスワード再発行、ログイン失敗時の挙動、複数デバイスでの利用、外部サービスとの連携、退会時の扱いなど、考えることが一気に増えます。

他システム連携を始めると設計が複雑になる

WordPress単体で閉じるのであれば標準認証だけでも運用できますが、モバイルアプリや別システムとユーザー基盤を共通化したい場合、WordPressだけに認証を持たせる設計は扱いにくくなります。

こうしたケースでは、認証基盤を外部サービスに任せ、WordPressはCMSや表示機能に集中させたほうが整理しやすくなります。

管理者と一般会員の入口を分けたくなる理由

実務では、管理者と一般ユーザーのログイン動線を分離したい場面が少なくありません。

一般会員にはブランドに合ったフロントエンドのログイン画面を見せたい一方で、管理者は従来どおり /wp-admin から安全に入れるようにしたい、という設計です。

この分離が曖昧だと、一般ユーザーが標準ログイン画面に迷い込んだり、権限昇格を意識した防御設計が難しくなったりします。

WordPressの認証をSupabaseに分離するという考え方

こうした課題に対して有効なのが、「認証はSupabase、CMSはWordPress」という役割分担です。

認証はSupabase、CMSはWordPressという役割分担

こうした構成は、実は特別なものではありません。

認証を外部サービスに任せ、アプリケーション側はユーザー管理や権限管理に集中するという設計は、Auth0やFirebase Authなどでも採用されている一般的なアーキテクチャです。

Supabase Auth も同じ思想で設計されており、JWTベースの認証を提供しています。

Supabase Auth はJWTベースの認証を提供しており、メール認証、OAuth、パスワードレスなど複数の認証方式を統一的に扱えます。

JWTベースのセッション管理とは

Supabaseでは、サインインするとセッションが作成され、その中にJWT形式のアクセストークンとリフレッシュトークンが含まれます。ただし、WordPress連携ではそのJWTをそのままフロント側の認証基盤として使いつつ、必要に応じてWordPress側でも認証Cookieを発行し、既存のログイン状態へ接続する構成が実務上は扱いやすくなります。

JWTは認証済みユーザーであることを示すトークンで、Supabaseの各機能はこのJWTを検証して利用者を判断します。

この画像は、Supabaseログイン後にブラウザの localStorage に保存される認証セッション(JWTトークン)を、開発者ツールで確認したものです。

WordPressの認証がCookieで管理されるのに対して、SupabaseではJWTトークンによってログイン状態が管理されていることが、この画面からも確認できます。

Supabaseログイン後のlocalStorageに保存されたJWTセッション

ただし、WordPress側にこのJWTを持ち込む場合でも、「JWTがある = WordPress内で何をしてもよい」ではありません。

WordPressでは、あくまでWordPressの権限体系に落とし込んで制御する必要があります。

WordPress側には最低限のユーザー情報だけを持たせる

この考え方に立つと、WordPress側に必要なのは「表示や権限付与に必要な最低限のユーザー情報」です。

本当の認証情報やパスワードそのものをWordPressで持つ必要はありません。

この発想が、自作プラグイン Edel Auth for Supabase の設計の土台になっています。

Edel Auth for Supabaseで採用した設計

ここからは、実際に私がプラグインで採用した設計を紹介します。

wp_usersに本当のパスワードを保存しない

このプラグインで最も重視したのは、WordPress側に本当のパスワードを持たせないという点です。

WordPressとの互換性のためにユーザー自体は作成しますが、wp_users に保存するのは認証用の実パスワードではなく、システムが生成したランダムなダミー文字列です。

実際のログイン判定はSupabase側で行い、WordPressはその結果を受けて必要な同期を行うだけにしています。

これにより、万一WordPress側のデータベース情報が漏えいした場合でも、Supabase側の認証情報とは分離された状態を保ちやすくなります。

Just-in-Time同期でDBを肥大化させない

もう一つのポイントが、ユーザー同期を一括で行わず、ログイン成功時だけ同期することです。

外部認証の導入時に全ユーザーをWordPressへ先回りして取り込む設計もありますが、それでは wp_userswp_usermeta が不要に膨らみやすくなります。

Edel Auth for Supabase では、ユーザーが実際にログインした瞬間にだけWordPress側へユーザーを作成または更新する、いわゆる Just-in-Time 同期を採用しました。

これにより、使われていない会員データでWordPressのDBが汚れにくくなります。

管理者と一般ユーザーのログイン経路を分離する

管理者と一般ユーザーの入口を分けているのも、意図的な設計です。

一般ユーザーはSupabase経由のフロントエンドフォームからログインし、管理者は従来どおり /wp-admin または wp-login.php を使います。

これにより、管理者がフロント側の認証導線に巻き込まれることを防ぎつつ、一般ユーザーにはブランドに沿った導線を提供できます。

実際のログイン後は「JWTだけ」ではなく、WordPressの認証Cookieも併用している

ここは誤解されやすいポイントですが、Edel Auth for Supabase の構成は、単に「SupabaseのJWTだけでWordPress全体を動かしている」わけではありません。

実際にブラウザの開発者ツールで確認すると、Supabaseログイン後には localStorage に Supabase のセッション情報が保持される一方で、WordPress側でも wordpress_logged_in_*wordpress_sec_* といった認証Cookieが発行されていました。

つまり、認証の起点はSupabaseに置きつつも、WordPress側ではログイン成功後に wp_set_auth_cookie() 相当の処理で認証Cookieを発行し、WordPress標準のログイン状態へ接続している構造です。

この設計にしておくことで、フロント側ではSupabaseのJWTベース認証を扱いつつ、WordPress内部では is_user_logged_in()get_current_user_id()、ロール・権限管理など、既存の仕組みを活かしやすくなります。

言い換えると、このプラグインは「SupabaseかWordPressか」の二者択一ではなく、Supabaseで本人確認を行い、その結果をWordPressのログイン状態へ安全に橋渡しする設計を採用しています。

ショートコードでログイン導線をフロント側に寄せる

ログインフォームや登録フォーム、パスワード再設定フォームをショートコード化したのも、単なる使いやすさのためだけではありません。

会員サイトでは、標準の wp-login.php をそのまま見せると、サイトの世界観や導線設計を壊しやすくなります。

フロント側に認証画面を置けるようにしておくと、デザインの自由度が上がるだけでなく、一般ユーザーを管理用ログイン画面から遠ざける設計にもつながります。

Supabase連携で注意したいセキュリティポイント

JWTをWordPressの権限そのものと混同しない

Supabaseで認証に成功し、JWTが取得できたとしても、それだけでWordPress内の権限が決まるわけではありません。

WordPressの投稿編集、管理画面アクセス、特定機能の利用可否は、最終的にはWordPressのロールと権限で制御すべきです。

外部認証は本人確認、WordPressは権限管理、という線引きを崩さないことが重要です。

service_role keyの扱いは特に慎重にする

Supabaseの service_role キーは強い権限を持つ秘密鍵です。導入時にも設定しますが、これは公開してよいキーではありません。Supabase公式ドキュメントでも、JWT署名や認証まわりのキー管理は重要なセキュリティ要素として扱われています。

そのため、WordPress側で扱う場合も、フロント側に露出させない、デバッグ出力しない、リポジトリへ誤って含めない、といった基本を徹底する必要があります。

キャッシュとNonceの相性に注意する

ログインフォームや登録フォームをショートコードで固定ページに置く場合、キャッシュプラグインとの相性には注意が必要です。

認証系フォームはNonceや一時的な状態を扱うため、キャッシュされたHTMLがそのまま再利用されると、ログイン失敗やフォームエラーの原因になります。

そのため、ログインページ、登録ページ、パスワード再設定ページなどは、キャッシュ除外対象にするのが基本です。

リダイレクトURLと認証後の遷移を厳密に管理する

OAuthやパスワード再設定を使う場合、リダイレクトURLの管理は非常に重要です。

Supabase側でも許可済みのリダイレクトURL設定が必要であり、WordPress側でも認証後にどこへ戻すのかを正確に揃える必要があります。設定がずれると、ログインできない、パスワード再設定が完了しない、といった不具合につながります。

こういうサイトでは相性が良い

この設計は、すべてのWordPressサイトで必要というわけではありません。

ただ、次のようなケースではかなり相性が良いと感じています。

  • 一般会員が多い会員サイト
  • 将来的に別システムやアプリと認証連携したいサイト
  • 管理者と一般ユーザーのログイン経路を分けたいサイト
  • WordPressをCMSとして使いながら、認証はよりモダンな基盤に任せたいケース

逆に、管理者数名しかログインしない小規模サイトであれば、WordPress標準認証のままで十分なことも多いです。

大切なのは、「外部認証が流行っているから使う」のではなく、サイトの要件に対して責務分離が有効かどうかで判断することだと思います。

複数のWordPressサービスで認証基盤を共通化できる

たとえば、企業が会員向けサービスAをWordPressで運営しているとします。

その後、サービスAと関連する新サービスBを、別のWordPressサイトとして開発するケースは珍しくありません。

通常のWordPress運用では、サービスAとサービスBはそれぞれ独立してユーザーを管理するため、利用者はサービスBでも改めてアカウント登録を求められることがあります。

しかし、Edel Auth for Supabase を両方のサイトに導入し、同じSupabaseプロジェクトを認証基盤として利用すれば、ユーザーは同じメールアドレス・同じパスワード、または同じGoogleアカウントでサービスAとサービスBの両方にログインできます。

つまり、WordPressごとに認証情報を分断するのではなく、認証そのものはSupabaseに集約し、各WordPressサイトは必要なタイミングでユーザー情報を同期する構成にできます。

複数のWordPressサービスを運用する場合、この構造はとても相性が良いと感じています。

まとめ

WordPressにはもともと強力なユーザー管理機能がありますが、会員サイトや外部連携を前提にすると、認証の責務をWordPressだけに集中させない設計が有効になる場面があります。

私が作成した Edel Auth for Supabase では、次のような方針を重視しました。

  • WordPress側に本当のパスワードを保存しない
  • 必要なタイミングだけユーザー同期する
  • 管理者と一般ユーザーのログイン経路を分離する
  • WordPressは権限管理、Supabaseは認証という役割分担を明確にする
  • Supabaseの認証結果を、WordPress標準のログインCookieへ橋渡しして既存機能と両立させる

会員機能を実装するときは、単に「ログインできるか」だけでなく、どこに認証情報を持たせるのか、どこで権限を判定するのか、どの導線を誰に見せるのかまで含めて設計することが大切です。

WordPressの会員管理をより安全で拡張しやすい形にしたい場合は、Supabaseのような外部認証基盤を組み合わせる選択肢も十分検討に値します。

Chat