OAuthログインを実装するときのフロントエンドとバックエンドの責務分離

WebサービスでGoogleログインやAppleログインなどのソーシャルログインを実装する場合、悩みやすいポイントの一つが、

ログインボタンや認証処理をフロント側でどこまで持つべきか
バックエンド側にどこまで任せるべきか

という責務分離です。

特にReact + Laravelのように、フロントエンドとバックエンドが分かれている構成では、
「React側でGoogle SDKを使うのか」
「Laravel側でOAuthのリダイレクトを管理するのか」
「トークン検証はどちらで行うのか」
といった設計判断が必要になります。

今回は、Google / Appleログインを例にしながら、フロントエンドとバックエンドの切り分けについて整理します。

前提

今回想定する構成は以下です。

  • フロントエンド:React
  • バックエンド:Laravel
  • 認証方式:通常ログイン + Googleログイン + Appleログイン
  • OAuthの用途:
    • 新規登録
    • ログイン
    • 既存アカウントへの連携
    • ソーシャルログインの解除

OAuthログインは単に「ログインできるようにする」だけでなく、実サービスでは以下のような分岐が発生します。

  • 新規登録として扱うのか
  • 既存アカウントへのログインとして扱うのか
  • すでに同じメールアドレスのユーザーが存在する場合どうするか
  • 他のログイン方法と競合した場合どうするか
  • 退会や解約時にどう扱うか
  • Google / Apple側の連携解除まで行うか

そのため、最初に責務をきれいに分けておかないと、あとから処理が複雑になりやすいです。

大きく2つの実装パターンがある

OAuthログインの実装は、大きく分けると以下の2パターンがあります。

1. React側でSDKを使ってログイン処理を開始する

Google Identity ServicesなどをReact側で読み込み、フロントエンド上でログインボタンを表示するパターンです。

ユーザーがボタンを押すと、React側でGoogleの認証画面を開き、IDトークンなどを取得します。
その後、取得したトークンをLaravel APIに送信し、バックエンド側で検証・ログイン処理を行います。

ざっくりした流れは以下です。

React
↓ Google SDKで認証
Google
↓ id_tokenを返却
React
↓ id_tokenをLaravelへ送信
Laravel
↓ トークン検証
Laravel
↓ ユーザー作成 or ログイン

2. Laravel側でOAuthリダイレクトを開始する

React側には普通のボタンだけを置き、クリック時にLaravelのOAuth開始URLへ遷移させるパターンです。
Laravel側でGoogle / Appleの認証URLへリダイレクトし、コールバックもLaravelで受けます。

ざっくりした流れは以下です。

React
↓ Laravelの認証開始URLへ遷移
Laravel
↓ Google / Appleへリダイレクト
Google / Apple
↓ callback URLへリダイレクト
Laravel
↓ codeをtokenに交換
Laravel
↓ ユーザー取得・作成・ログイン
Laravel
↓ Reactの画面へリダイレクト

結論:認証の本体はバックエンドに寄せるのが扱いやすい

個人的には、React + Laravel構成の場合、基本的には

Reactはログイン開始のUIを担当
LaravelはOAuthの状態管理・検証・ユーザー処理を担当

という切り分けが扱いやすいと感じました。

つまり、React側は「Googleでログイン」「Appleでログイン」というボタンを出すだけに近く、実際のOAuthフローはLaravel側で管理します。
Laravel側では、以下のような処理を担当します。

  • OAuth認証URLへのリダイレクト
  • state の生成・検証
  • code をアクセストークンへ交換
  • ユーザー情報の取得
  • メールアドレスの重複確認
  • 新規登録 / ログイン / 連携の分岐
  • セッションログイン
  • 最終的な画面へのリダイレクト

なぜバックエンドに寄せるのか

1. フロント側で偽装の可能性があるため

OAuthログインでは、最終的に「このユーザーをログイン済みにしてよいか」を判断する必要があります。
この判断は、フロントエンドではなくバックエンドで行うべきです。

理由はシンプルで、フロントエンドはユーザー側で動くため、完全には信用できないからです。
たとえばReact側で取得したメールアドレスをそのまま信じて、「このメールアドレスはGoogleから返ってきたのでログインOK」としてしまうのは危険です。

バックエンド側で、

  • トークンが正しいか
  • クライアントIDが一致しているか
  • 有効期限が切れていないか
  • メールアドレスが検証済みか
  • 既存ユーザーと紐付けてよいか

を確認する必要があります。

そのため、React側でSDKを使う場合でも、最終的な検証とログイン処理はLaravel側に置くべきです。

2. signup / signin / link などの分岐がバックエンドに集約できる

OAuthログインは、単純なログインだけなら簡単に見えます。

しかし、実際には以下のような分岐が発生します。

Googleログインボタンを押した
  ↓
これは新規登録?
既存ログイン?
既存アカウントへの連携?

この分岐をReact側に持たせすぎると、画面ごとに似たような処理が増えます。
一方、Laravel側で state に種別を持たせれば、バックエンドでまとめて処理できます。

フロントエンドの責務

フロント側(React)の責務は、基本的には以下に絞るとシンプルです。

Reactが担当すること

  • ログインボタンの表示
  • Google / AppleなどプロバイダごとのUI出し分け
  • 新規登録 / ログイン / 連携など入口の指定
  • ローディング表示
  • エラー表示
  • ユーザー操作の導線設計

React側は、ユーザーがどの認証フローに入りたいのかを表現する役割です。

バックエンドの責務

バックエンド(Laravel)側は、認証フローの本体を担当します。

Laravelが担当すること

  • OAuthリダイレクトURLの生成
  • state の生成
  • state のセッション保存
  • callback時の state 検証
  • 認可コードの検証
  • アクセストークン取得
  • ユーザー情報取得
  • メールアドレスの検証
  • ユーザー作成
  • 既存ユーザーとの紐付け
  • ログイン処理
  • エラー時のリダイレクト先決定

フロントエンドに寄せすぎると起きやすい問題

React側にOAuth処理を寄せすぎると、以下のような問題が起きやすいです。

1. 画面ごとに認証ロジックが散らばる

ログイン画面、登録画面、設定画面でそれぞれGoogleログイン処理を書くと、
似たような分岐が複数箇所に増えます。

2. セキュリティ上重要な判断がReact側に寄る

React側で下記のような判断を持つと危険です。

このメールアドレスなら登録OK
このユーザーならログインOK

UI上の分岐はReactで問題ありませんが、最終的な認可判断はLaravel側に置くべきです。

3. Google / Appleの差分がUIに漏れる

GoogleとAppleでは仕様が異なるため、React側で吸収しようとするとUIコンポーネントが複雑になりがちです。
差分はバックエンド側のServiceに閉じ込めた方が、React側はシンプルに保てます。

逆にReact側でSDKを使うのが向いているケース

ただし、すべてのケースでLaravelリダイレクト方式が正解というわけではありません。

React側でSDKを使う方が向いているケースもあります。

たとえば、

  • SPAとして画面遷移を極力減らしたい
  • モバイルアプリと同じAPI認証基盤を使いたい
  • Laravel SanctumやJWTでAPIログインしている
  • Google One Tapを使いたい
  • フロントエンド主導でログインUIを細かく制御したい

この場合は、React側でGoogle SDKを使ってIDトークンを取得し、Laravel APIに送信する構成もあります。

Reactでid_token取得
  ↓
Laravel APIへ送信
  ↓
Laravelでid_token検証
  ↓
APIトークン or セッション発行

ただしこの場合でも、重要なのは、Reactで取得したトークンをLaravelで必ず検証するという点です。
React側でログイン完了扱いにするのではなく、Laravel側で検証して初めてアプリ内ログイン完了とします。

まとめ

React + LaravelでGoogle / Appleログインを実装する場合、責務分離を意識すると後から拡張しやすくなります。

基本方針としては、

React = UIと入口
Laravel = 認証処理とユーザー状態の変更

に分けるのが扱いやすいです。

React側は、どのログイン方法を使うか、どのフローに入るかをユーザーに選ばせる役割に集中します。

一方Laravel側では、OAuthの state 検証、トークン取得、ユーザー情報取得、既存ユーザーとの紐付け、ログイン処理など、信頼性が必要な処理をまとめて担当します。

特に、以下のような処理はバックエンドに寄せるべきです。

  • トークン検証
  • メールアドレスの信頼性確認
  • ユーザー作成
  • 既存アカウントとの紐付け
  • ログイン可否の判断
  • セッション発行

OAuthログインは最初は簡単に見えますが、新規登録、ログイン、連携、解除、退会などの実運用まで考えると一気に複雑になります。
だからこそ、最初の段階でフロントエンドとバックエンドの責務を整理しておくことが大切です。