0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「google認証だけでいいでしょ」はどこまで正しいか — toCサービスのアカウント管理設計

0
Last updated at Posted at 2026-08-16

背景

toCのサービスを作るとき、こういう話になります。

アカウント登録を自前で作るより、「Googleでログイン」「LINEでログイン」を並べれば済むのでは?

パスワードを持たなくてよくなり、二要素認証も向こうが面倒を見てくれる。実装は数日で終わります。

この直感は、かなりの部分で正しいです。 この記事では、どこまで正しくて、どこから自社でIDを持つ必要が出るのかを整理します。従業員向けの社内システムやBtoB SaaSは扱いません。一般消費者が自分で登録・退会するサービスに絞ります。

用語について。 認証を提供する側を IdP(Identity Provider) と呼びます。Google、LINE、Yahoo! JAPAN、Appleがこれにあたります。認証を受け取る自社サービス側は RP(Relying Party) です。両者のやり取りを定めた規格が OIDC(OpenID Connect) で、以降に出てくる値の多くはこの規格で定義されています。


1. ソーシャルログインは、思っているより多くを解決します

引き受けてくれるものが、3つあります。

1.1 パスワードにまつわる仕事が丸ごと消える

自前でパスワード認証を持つと、次のすべてが自社の責任になります。

  • ハッシュ化して保存する(アルゴリズムの選定と、将来の移行)
  • パスワードリセットのメール送信とトークン管理
  • 二要素認証の実装
  • 総当たり攻撃・リスト型攻撃の検知と遮断
  • 他社から漏洩したパスワードの使い回しへの対策

ソーシャルログインだけにすれば、これが全部なくなります。自社にパスワードが存在しないので、パスワード漏洩という事故そのものが起きません。

1.2 ログインがタップだけで終わる

スマホで「Googleでログイン」を押したとき、パスワードを入力させられることはほとんどないはずです。すでにIdP側にログイン済みのセッションが再利用されるからです。

これは偶然ではなく、規格で保証された挙動です。OIDCの prompt=none について、Googleはこう説明しています。

The authorization server does not display any authentication or user consent screens; it will return an error if the user is not already authenticated
(認可サーバーは認証画面も同意画面も表示しない。ユーザーがすでに認証済みでなければエラーを返す)

— Google Identity: OpenID Connect

すでに認証済みなら聞き直さない。この仕組みをSSO(シングルサインオン)と呼びます。ログイン画面での離脱が減ることは、事業に直結します。

1.3 ユーザーを一意に特定できます

IdPが返すIDトークンには sub(Subject Identifier、利用者の一意識別子)が含まれます。OIDCの仕様は、これを「Issuer(発行者)内で一意であり、再割り当てされない識別子」と定義しています(OpenID Connect Core 1.0)。

Googleはさらに踏み込んだ説明をしています。

An identifier for the user, unique among all Google Accounts and never reused.
(すべてのGoogleアカウントの中で一意で、再利用されることのない識別子)

再利用されないというのが効きます。退会したユーザーの値が別人に割り当てられることがありません。ユーザーを識別する値として、これ以上のものを自前で用意するのは困難です。

この3つが揃えば、設計は最小限で済みます。 ログイン手段と sub を並べたテーブルを1枚持つだけで足ります。プロバイダが1つなら発行者は自明なので、会員テーブルに sub の1カラムを足すだけになります。会員基盤と呼べるものを作る必要はありません。

自社で会員IDを持つ理由になるのは、2つだけです。


2. 自社で会員IDを持つ理由は、2つです

どちらも「放っておくと事故が起きる」ではなく、要件があるかどうかの話です。当てはまらないなら、ソーシャルログインだけで足ります。

2.1 IdPが返さない情報を、複数のサービスで共有したい

IdPが返すのは、IdPが持っている情報だけです。sub、表示名、(取得できれば)メールアドレス。それだけです。

  • ポイント残高
  • 会員ランクと継続期間
  • 契約プラン・課金状態・解約予約
  • 購入履歴、配送先
  • 利用目的ごとの同意と、その取得日時

こうした情報は、どのIdPからも返ってきません。自社で持つしかありません。

サービスが1つなら、ここで話は終わります。そのサービスのデータベースに持てばよく、ログインした人と結びつける値も手元にあります。会員基盤と呼ぶほどのものは要りません。

変わるのは、この情報を複数のサービスで共有したくなったときです。 サービスAで貯めたポイントをサービスBで使う。会員ランクをグループ共通にする。ひとつの同意で全サービスをカバーする。

このとき、ポイント残高を置く場所はサービスAでもBでもありません。両方の外側です。

そして外側に置いた残高を引くには、どのサービスから見ても同じ人を指す値が要ります。IdPはそれを与えてくれません。認証しか提供していないからです。

この「外側」を、この記事では 会員基盤 と呼びます。自社が発行する会員IDと、IdPが返さない情報を置く場所のことです。全サービスがそこを参照します。

言い換えると、会員基盤は認証のために作るものではありません。IdPが返さない情報を複数サービスで共有するために作られます。

逆に、サービスが1つで増える予定がないなら、この理由は当てはまりません。

2.2 認証を、他社に預けたままにしたくない

ログインは、サービスの入口です。それを外部に委ねるとき、契約上どういう立場になるかは読んでおく価値があります。

Google APIs 利用規約には、こう書かれています。

Google reserves the right to terminate the Terms with you or discontinue the APIs or any portion or feature or your access thereto for any reason and at any time without liability
(Googleは、理由を問わず、いつでも、責任を負うことなく、本規約を終了し、またはAPI・その一部・機能・お客様のアクセスを停止する権利を留保します)

— Google APIs Terms of Service

LINE Developers Agreement も同様です。

The Company may terminate the Agreement without any notice and suspend the provision of the Service, and shall not be liable to the Application Developer for any damage caused by such termination.
(当社は、予告なく本契約を終了し、サービスの提供を停止することができ、それによって生じた損害について開発者に対し責任を負いません)

— Terms and policies - LINE Developers

理由不問、予告なし、無責任。止まっても文句を言えないことが、規約に書いてあります。

実際に起きています。2023年のTwitter API有料化のとき、オリジナルグッズ作成サービス「SUZURI」は「Twitterの仕様変更により、今後『Twitterアカウント連携によるログイン』がご利用いただけなくなる可能性がございます」と告知しました(参考)。

ユーザー側からも断絶が起きます。ユーザーがIdP側のアカウントを削除・凍結されれば、自社サービスにもログインできなくなります。逆にIdP側のアカウントが乗っ取られれば、連携先すべてに不正ログインされます。

このとき効いてくるのが、ユーザーの記録を sub だけで持っているかどうかです。 sub はそのIdPが解決できる値です。IdPが去れば、残った行が誰のものなのか自社では二度と分かりません。ポイントの持ち主に連絡することも、本人確認して引き継ぐこともできなくなります。

このリスクを取るかどうかは、事業判断です。


3. 会員基盤の最小形

2章のどちらかに当てはまるなら、2.1で言う会員基盤を自社で持つことになります。ここからはその中身です。大きな仕組みに聞こえますが、最小形はテーブル2枚です。

3.1 基盤が持つもの

中身は2つです。自社が発行する会員IDと、IdPが返さない情報。

CREATE TABLE members (
  member_id UUID PRIMARY KEY,   -- 自社発行。不変。ユーザーにも外部にも見せない
  email     TEXT,               -- 連絡先。キーではない(3.4)
  rank      TEXT
  -- ポイント・契約・同意記録なども、ここを軸にぶら下げる
);

member_id は自社が発行するUUIDです。ユーザーにも外部にも見せません。各サービスは、自分が持つデータをこのIDで参照します。

要件は1つだけです。member_id が変わらないこと。 ここが動くと全サービスの参照先が一斉に壊れるので、あとから変わりうるものを member_id にしません。 メールアドレスも、外部IdPが返す値も該当します。

3.2 認証は、基盤の外側に付きます

会員IDに対して、ログイン手段がぶら下がる。関係はそれだけです。実際、どちらの形も動いています。

  • 自前の認証だけ — リクルートIDのログイン画面は、メールアドレスとパスワードのみです。外部IdPのボタンはありません(リクルートID ログイン、2026年8月時点)
  • 自前の認証 + 外部IdP — 一休.comは、Yahoo! JAPAN ID・Google・Facebook・LINE・Appleでの登録とログインを受け付けます。「提携サイトのアカウントで登録すると、そのアカウントに登録のメールアドレスで一休.com会員が作成され、同時に提携アカウントが連携されます」(一休.com ヘルプ)

後者は、1人の会員がログイン手段を複数持っている状態です。一休.comの連携解除の案内が、それをそのまま示しています。

連携解除した提携サイトアカウントを使ってログインできなくなるため、事前に会員アカウントの「メールアドレスまたは携帯番号」と「パスワード」で同じ会員アカウントにログインできることを確認した上で

パスワードと提携アカウントが同居しているから、片方を外す前にもう片方を確認しろ、という案内になります。

会員基盤の形はどちらも同じです。 違うのは member_id に何をぶら下げているかだけで、2章の2つ目(認証を他社に預けるかどうか)は、ここの選択にあたります。

3.3 外部IdPを足すなら

ぶら下げる側のテーブルです。

CREATE TABLE login_methods (
  member_id UUID NOT NULL REFERENCES members(member_id),
  issuer    TEXT NOT NULL,   -- 発行者
  subject   TEXT NOT NULL,   -- そのIdP内での識別子(sub)
  PRIMARY KEY (issuer, subject)
);

1対多にしてあるので、プロバイダの増減は行の追加と削除で済みます。members に google_sub のようなカラムを足す形にすると、2つ目のプロバイダで破綻します。

主キーを (issuer, subject) にしているのは、同じIdPアカウントが2人の会員に紐づくのを防ぐためです。一休.comも「1つの提携サイトアカウントを同時に複数の一休.com会員アカウントと連携することはできません」と案内しています。データベースの制約で担保しておけば、アプリケーション側で気をつける必要がなくなります。

sub の一意性はそのIdPの中でしか保証されないので(OpenID Connect Core 1.0)、発行者とセットにします。OIDC準拠のIdPなら、発行者はIDトークンの iss クレームです。例外が2つあります。

  • X — OIDCではなくOAuth 2.0のみで、iss は来ません。GET /2/users/me が返す id を subject に入れ、issuer は自社で決めた固定文字列にします
  • Facebook — アプリごとに異なるID(app-scoped user ID)を発行します。サービスを複数持つなら、同じ人でもアプリが違えば値が変わります(Mapping Users Across Apps and Pages)

行を足すのは、ログイン済みの本人です。

1. ユーザーが、いつも使っているログイン手段でログインする
2. ログイン済みの状態で「〇〇と連携する」を実行してもらう
3. 得られた (issuer, subject) を、その member_id の行として追加する
4. 次回以降はどちらのログイン手段からでも同じアカウントに入れる

本人がログインした状態でしか、「このIdPのアカウントはこの人のものだ」と確定できません。メールアドレスが一致しているという理由で自動的に繋いではいけない、というのが3.4の話です。

3.4 メールアドレスをキーにしてはいけません

member_id の代わりに使いたくなりますが、2つの理由で使えません。

値が揃いません。

  • LINE — 「emailを指定して……取得権限を要求するには、あらかじめメールアドレス取得権限を申請してください」と事前申請が必要なうえ、「ユーザーは権限の付与に同意せずにウェブアプリにアクセスする場合があります」。取れないことがあります(LINE Developers)
  • Apple — 「メールを非公開」を選ぶと @privaterelay.appleid.com のアプリごとに固有の転送用アドレスが返ります。実アドレスではないので、他のIdPが返す値と一致しません(Apple Support)

揃っても、他人のアカウントに入られます。 メールアドレスは誰でも「自分のものだ」と主張できる文字列で、正しさを保証しているのは検証したIdPだけです。メールアドレスを確認しないIdPで victim@example.com を名乗ってログインされれば、被害者の会員に紐づいてしまいます。email_verified が false のアドレスを、本人確認の根拠にしてはいけません。

Google自身も明記しています。

Don't use the email field as a unique identifier for a user. Always use the sub field.
(email フィールドをユーザーの一意な識別子として使わないでください。常に sub を使ってください)

メールアドレスは連絡先として持ちます。キーではなく属性です。

3.5 「Googleで登録したのにパスワードを求められる」の正体

Googleで会員登録したはずなのに、あとからパスワードの設定を求められる。ユーザーから見れば「管理するものがまた増えた」という話です。login_methods の行が、この現象を説明します。 理由は2つあります。

1. そもそも行を作っていない。

Googleのボタンには、2つの使い方があります。

ボタンの役割 login_methods に行を作るか 次回ログイン
フォーム補完 — 氏名やメールアドレスを取って登録フォームに埋める 作らない Googleでは入れない
ログイン手段 — (発行者, sub) を保存する 作る Googleで入れる

前者は実際に使われている使い方で、「新規会員登録の際にソーシャルログインを利用すると、各SNSプロバイダから取得できる氏名やメールアドレスなどの情報を登録フォームに埋めることができる」と説明されています(ソーシャルPLUS)。

同じボタン、同じ画面遷移でも、行を1本保存したかどうかだけが違います。 保存していなければ、ユーザーにとっては「Googleで登録したのに、次からはパスワード」になります。実装する前に、どちらのつもりなのかを決めておきます。

2. 行が1本しかない会員を、放置できない。

login_methods の行が1本しかない会員は、その1本を失った時点で打つ手がなくなります。

  • ユーザーがGoogleアカウントを削除された、または凍結された
  • ユーザーが「メールを非公開」で使っていたApple Accountを消した
  • 2.2のとおり、IdPが提供を止めた

このとき自社に残っているのは (発行者, sub) だけです。sub はそのIdPしか解決できない値なので、問い合わせてきた人が本人かどうかを確かめる方法がありません。ポイントも購入履歴も、持ち主に返せなくなります。だから2本目を持たせにいきます。

ただし、2本目がパスワードである必要はありません。

2本目の候補 ユーザーの負担 前提
パスワード 記憶と管理が増える なし
別の外部IdP ボタンを1回押すだけ その会員が別のIdPも使っている
メールへのワンタイムコード 記憶するものは増えない 届くアドレスがある(3.4)
パスキー 管理はOSに任せられる 対応端末・ブラウザ

要るのは復旧できることであって、パスワードそのものではありません。どれを選ぶかで、ユーザーの体験だけが変わります。


4. 結論

「基盤」の呼び方が割れる理由

認証基盤、ID基盤、アカウント基盤、会員基盤。同じものを指しているようで指していません。層が2つあります。

層 答える問い 外部に任せられるか
認証 この人は本人か 任せられる(Google・LINE・Apple)
会員 この人が自社との間に何を持っているか 任せられない

ソーシャルログインが引き受けるのは、上の層だけです。IdPはメールアドレスや表示名も返しますが、それは認証に付いてくるプロフィールであって、ポイント残高や契約状態のような自社との関係ではありません。呼び方が割れるのは、2つをひとつの「基盤」として語るからで、分けてしまえば判断する項目も2つになります。

答え

  • 自社サービスが1つで、会員情報を他のサービスと共有する予定がないか(2.1)
  • 認証が他社の都合で止まるリスクを、事業として受け入れられるか(2.2)

どちらもYesなら、どちらの層も作りません。 サービスのデータベースに (発行者, sub) を1組持てば足ります。「基盤」と呼べるものは要らない、というのがこの記事の結論です。

1つ目がNoなら、会員層を作ります。 サービスの外側に member_id を置き、ポイントも契約も同意もそこに集める(3.1)。認証は外部IdPのままで構いません。一休.com型です。

2つ目がNoなら、認証層を自前にします。 会員層の形は変わりません。member_id にぶら下げるログイン手段が自前のものになるだけです(3.2)。外部の sub が存在しなくなるので、会員IDは必然的に自社発行になります。リクルートID型です。

分かれ目は認証方式ではありません。 IdPが返さない情報をどこに置くか、そして認証を誰に任せるか。この2つは独立に決められます。


補足: すでに会員がいるサービスに足す場合

この記事は「自前の会員基盤を作らずに済むか」を扱っているので、すでに会員基盤があるサービスは判断の対象外です。ただし1つだけ、確実に言えることがあります。

ソーシャルログインを足しても、既存の認証手段は捨てられません。 既存会員の sub は、まだ誰も持っていないからです。3.3の手順を本人に実行してもらうまで紐づかず、実行しないユーザーが一定数残るので(割合は事例により幅があり、公開データを確認できていません)、元の入り口を閉じるとその人たちが締め出されます。


参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?