綴Photo の勉強ノートに戻る
開発記録2026.06.25

アプリのログイン、結局どういう形? — 『誰が認証するか』と『どう戻すか』を分けたら、ほどけた

認証App Links設計綴Photo

自分のアプリのログインの形を、毎回頭がこんがらがるので整理した記録です。難しい話というより、混ぜて考えてた2つを分けたら一気にほどけた、という整理。

ここで言う「web」は、綴ったものを web でも見られるように開発中のアプリ(紡Book)のことです。

綴Photo は、モバイルアプリと、それを見せる web でできています。「アプリに本物のログインを足す」をずっと考えていて、調べるたびに頭が混乱していた。原因は単純で、別々の2つを1つの塊として考えていたから。分けたらほどけたので残しておく。

モバイルにログインを足す一連の作業のうち、「誰が認証するか」と「どう戻すか」の方式だけを抜き出して整理した回。

混乱の元:「誰が認証するか」と「どう戻すか」は別の話

ログインには2つの問いがあります。

  1. 誰がログインさせるか(本人確認の本体)
  2. その結果をどうアプリに渡すか(戻り道)

これを混ぜると「アプリにログイン機能を作る」が一個の重い塊に見えます。分けると、別々に決められます。しかも①は、自分の場合まるごと web に投げられます。

①誰が認証するか:web に丸投げ。アプリは Google を持たない

アプリは Google と直接喋りません。アプリはブラウザで自分の web のログイン画面を開きます。web 側で Google ログイン(これは前から動いています)。アプリは結果だけ受け取ります。

うまみは、アプリに Google の設定(OS ごとのクライアント登録、Console 作業)を一切入れずに済むこと。後で Apple を足すのも web 側だけで済みます。

②どう戻すか:ブラウザからアプリへ。ここが今回の本題

ブラウザとアプリは別プロセスだから、ログイン後にブラウザが引換券をアプリへ渡す道が要ります。これが戻り道。

ここで大事な気づき。どの方式でも、アプリが「この戻り道は自分が受け取る」と OS に登録するのは避けられません(後述の custom scheme でも登録は必須)。これが「結局ネイティブ認証に戻ったのでは?」という勘違いの元だった。でもこれは認証じゃなくて、ただの配管。

戻り道の作り方は2つ。

  • (a) custom scheme(アプリ名://)=軽い。でも OS はこのスキームを誰が登録したか検証しない=別アプリが同じ アプリ名:// を後から登録でき、引換券がそっちに落ちうる(横取り)。
  • (b) App Links / Universal Links=OS が「この https の戻り道を受け取れるのは、ドメイン側が正規と認めた署名のアプリだけ」を検証する https リンク。custom scheme に無かった“登録主の検証”が入るのが差。

2つの方式で「受け取り手を誰が決めるか」を並べると、差が1箇所しかないのが見えます。

(a) custom scheme(アプリ名://)(b) App Links(検証済みの https)本物のアプリ偽アプリどちらも「自分が受け取り手です」と名乗れるOS はその申告をそのまま受け入れる=確かめる材料が、どこにも無い後から名乗ったアプリにも引換券が落ちうる = 横取り下げられるのは運用だけ(構造では消えない)① ドメイン側に置く名簿「このIDと署名のアプリ」② アプリに付いた署名開発者の鍵でしか作れないどちらも偽アプリには用意できないOS が①と②を突き合わせる=確かめる材料が、両側にある一致したアプリにだけ引換券を渡す=偽の郵便受けに落ちない構造で消せる(運用に頼らない)決め手は「端末に固有の値」ではなく、ドメインの持ち主がどのアプリを指名したか。端末は出てこない

効いているのは端末の側にある何かではなく、ドメインを持っている自分が「このアプリ」と先に名指ししてあることです。名指しの相手はアプリの署名(開発者の鍵の指紋)なので、鍵を持たない偽アプリはそこに入れません。

だから偽アプリは https://tsuzuphoto.com/... の戻り道を横取りできない=引換券が偽の郵便受けに落ちません。「横取り→乗っ取り」を構造で塞げます。

大事なのは、これは「誰かを認証していいか」を確かめているんじゃなく、「引換券を本物のアプリにだけ届ける」配達保証だということ。認証は①の web のまま。「OS の認証を作る」じゃなく「OS に正しい配達先を保証させる」が近いです。

2つの方式を並べると、何を取って何を捨てたかが見える。

方式 軽さ 横取り耐性 localhost で試せる
custom scheme(アプリ名://) 軽い 運用でしか下げられない(構造では消えない) 試せる
App Links(検証済み https) 重い(本番ドメイン検証が要る) 構造で消せる 試せない(トンネル/staging が要る)

custom scheme の横取りは運用(明示確認・state=行きの要求と返ってきた結果が同じ一件かを照合する番号札・短命 code)でしか下げられません。App Links なら構造で消せます。公開を見据えるなら構造で消したいので、App Links を選んだ。

これは前に web の中継をレビューしたとき(③)に「custom scheme を足す日が本当の山場」と後回しにした穴の、回収でもある。custom scheme を足さず App Links に倒したので、あの穴は取り付ける前に塞げた。

代償もある。表の通り、App Links は本番の https ドメインに対して OS が検証する=localhost で dev 確認できません。実機確認にはトンネル(開発中の自分の PC を一時的に本物のドメインとして外から触れるようにする仕組み)か staging(本番そっくりの練習用サーバ)が要ります。custom scheme なら localhost で回せた分のトレードオフで、ここは「堅さ」を取った。

今日入れたのは web 側の土台(許可リストと戻り道ページ)を AI に入れてもらった分だけ。戻り道の https リンクを許可リストに入れる(完全一致=同じドメインでも別パスは弾く)のと、戻り道のフォールバックページ。

python
# App Links/Universal Links: 自ドメインの検証済み戻り道だけ(完全一致)。
# ↑ host も path もピッタリ一致だけ許可=同じ tsuzuphoto.com でも別パスには引換券を出さない
#   (ドメイン内に open-redirect の隙を作らない)。
# parsed = 戻ってきた URL を「https / ドメイン / パス」の部品にバラしたもの
if (
    parsed.scheme == "https"                       # 暗号化された https か
    and parsed.hostname in _HTTPS_RETURN_HOSTS   # ドメインが自分のか(例 {"tsuzuphoto.com"})
    and parsed.path == _HTTPS_RETURN_PATH         # パスもピッタリ同じか("/app-login/return")
):
    return True

残り(ドメイン直下の検証ファイル設置、アプリ側の登録)は、必要な値(アプリの署名情報など)が揃ってから。

学びポイント

  • 「ログインを作る」を「誰が認証するか」と「どう戻すか」に分けます。 混ぜると重い塊に見えるが、分けると別々に決められて、しかも①は web に丸投げできます。
  • 戻り道の OS 登録は「ネイティブ認証」じゃなく「配管」。 どの方式でも要ります。App Links はその配管を”検証済み”にするだけで、認証そのものとは別。
  • App Links は認証じゃなく「正しい配達先の保証」。 ここを混同すると「結局ネイティブに戻った?」と勘違いする(した)。
  • 堅さには代償があります。 軽さ(custom scheme・localhost で試せる)と堅さ(App Links・横取りを構造で消す)はトレードオフ。今回は公開を見据えて堅さを選んだ。

おわりに

形が見えたら、重く感じていたログインが「web に丸投げ+検証済みの戻り道」の2部品に分解できた。web 側の土台は今日 AI に入れてもらった。残りはアプリ側の登録と検証ファイルで、それは値が揃ってから。分けて考える、が今回の一番の収穫だった。


この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。