Forest Book

App Proxyの logged_in_customer_id は空になり得る ― Shopifyストアフロント認証の切り分けとフォールバック設計

2026年7月11日

ShopifyのApp Proxyを使ってストアフロントからバックエンドへリクエストを送る構成では、ログイン済み顧客のIDが logged_in_customer_id として付与されます。ところがこの値は、ユーザーが明らかにログインしているにもかかわらず空になることがあります。当社ではこの事象に何度か遭遇しており、原因の切り分け方法と、空になっても認可を成立させる設計をパターンとして持っています。本記事は、App Proxyで顧客特定を行っているShopifyアプリ開発者、およびこれから設計する技術者に向けた内容です。

なぜ空になるのか ― 値の出所を理解する

logged_in_customer_id は、アプリのバックエンドが自力で解決している値ではありません。Shopifyのエッジがプロキシ転送する際に、ストアフロントのセッションCookieを見て付与しているものです。App Proxy自体はCookieをバックエンドに転送せず、Shopify側でCookieヘッダーを剥がした上でクエリパラメータとして注入する、という構造になっています。

つまりこの値の有無は「Shopifyのエッジから見てストアフロントセッションが確立しているか」に完全に依存します。バックエンド側の実装を疑う前に、セッションが成立していると言えるのかを疑うのが正しい順序です。

空になる代表的なケース

まずログイン直後のレース条件です。ログインや会員登録のリダイレクト完了とセッションCookieの反映の間に、ページ遷移を挟まずJSからリクエストを投げると、セッションがまだ認識されず空になることがあります。SPA的な実装で起きやすいパターンです。

次に、新しいカスタマーアカウント(OIDC型)を使っている場合。これが現在もっとも多い原因です。ログイン済みでも logged_in_customer_id がセットされない、挙動に一貫性がない、逆にログアウト後も値が残り続ける、といった報告がコミュニティに複数あります。外部の認証基盤とストアフロントセッションの同期が保証されていないためで、時間を置いても解消しません。

さらに、アカウント承認制やメール認証必須の設定では会員登録完了イコールログイン済みにはなりませんし、サンキューページや注文ステータスページからのリクエストでもこの値は付きません。カスタマーアカウントUI拡張からのリクエストにも付与されず、公式にはセッショントークンの sub クレームを使うよう案内されています。

切り分けは「Cookieが載っているか」で二分する

ここが実務上もっとも重要な判断ポイントです。DevToolsのNetworkタブでApp Proxyへのリクエストそのものを開き、リクエストヘッダーにストアフロントのセッションCookieが載っているかを見ます。ここで原因が二つに分かれます。

Cookieが載っていない場合はクライアント側の問題です。fetchのURLが絶対URLで別ホストを向いている(カスタムドメインと myshopify.com の混在、www の有無を含む)、credentials の設定が omit になっている、計測タグ系のスクリプトがリクエストを書き換えている、といった原因が考えられます。相対パスで同一オリジンに投げていれば通常は載るはずなので、URL生成箇所を確認します。

Cookieが載っているのに空の場合はプラットフォーム側の問題です。実際、テーマのLiquidでは顧客コンテキストが取得できているのにプロキシパラメータだけが空、という事象が報告されています。Shopifyの顧客認証はストアフロントのセッションとは別レイヤーのセッション構造を持つため、Liquidが参照する状態とプロキシパラメータ注入が参照する状態が食い違うケースがあり得ます。この場合はシークレットウィンドウや別ブラウザでの再現性を確認し、常に再現するならHARを添えてサポートに報告すべき事象です。

Liquidで顧客IDが取れていることは、証明にならない

現場でよくある誤解が「レスポンスのbodyに顧客IDをLiquidで埋め込んだら取れているのだから、fetchにもセッションは載っているはずだ」というものです。これは成立しません。Liquidはページ配信時にShopify側で評価されるもので、fetchはその後に発生する別のHTTPリクエストです。ページのレンダリング時点でセッションが有効だったことは証明できても、後続リクエストにCookieが載っていたことの証明にはなりません。この区別を曖昧にしたまま調査すると、原因の特定を大きく遠回りします。

フォールバックをどう安全に設計するか

プラットフォーム側の不具合が疑われる状況で、納期を待てないことは普通にあります。当社では、クライアント申告の顧客IDをフォールバックとして使う場合に、以下を必須条件としています。

クライアントが送ってきた顧客IDは、当然ながらそのままでは信頼できません。そこで、バックエンドが application/liquid としてレスポンスを返し、Shopify側のレンダリング時に実セッションで顧客IDを評価させる方式を使います。申告されたIDと実際のログイン顧客が一致した場合にのみ、短命トークンやデータが露出する条件分岐を組みます。この評価はShopify側が実セッションで行うため、クライアントからの改ざんは成立しません。

その上で、副作用のある処理はトークン検証前に絶対に実行しないこと、トークンを返すエンドポイントには必ずキャッシュ無効化のヘッダーを付けることの二点を守ります。App ProxyのGETレスポンスはCDNにキャッシュされ得るため、後者を怠ると他人にトークンが渡る事故に直結します。これは恒久対応ではなく、あくまでプラットフォーム側の不具合に対する安全な迂回路という位置づけです。

設計時に持っておくべき前提

新しいカスタマーアカウントが絡む案件では、logged_in_customer_id を顧客特定の唯一の手段にするのは危険です。Customer Account APIのセッショントークンを検証して顧客IDを得る方式に寄せるのが、現時点でもっとも確実です。

また、Shopifyの公式ドキュメントも、App Proxyの署名検証は改ざん防止を保証するだけであり、logged_in_customer_id とデータ所有者の一致検証はアプリ側の責任である、という立場を明示しています。「空の場合にどう振る舞うか」を含めた認可設計を、実装前に決めておく必要があります。

まとめ

logged_in_customer_id は便利な値ですが、Shopifyのエッジがセッションを解決できた場合にのみ付与される、条件付きの値です。当社では、この値を前提にする設計を採るときは必ず、空になった場合の切り分け手順とフォールバック経路を先に決めてから実装に入るようにしています。認証情報を「必ず来るもの」として扱うか「来ないことがあるもの」として扱うかで、後の障害対応コストが大きく変わります。