アプリの審査項目について

Topic summary

開発者がShopifyアプリの審査で2つのエラーに直面している。

発生している問題:

  • クリックジャッキング対策のセキュリティヘッダーが設定されていない
  • Shopifyからのリクエストの真正性検証ができていない
  • いずれもアプリのインストールエラーにより、セキュリティチェックが実行できない状態

技術環境:

  • Next.js、Firestore、Vercelを使用
  • 埋め込みアプリではなく、App Bridgeは不使用
  • OAuthフローを独自実装(per-userとofflineの2回実行でユーザー情報取得とトークン管理を両立)

提案された解決策:

  • iframe保護のドキュメントに記載のセキュリティヘッダーをVercel/Next.jsに設定
  • Shopifyからのリクエスト検証処理を追加(webhookやapp proxiesを使用している場合は特に重要)

現状: 具体的な解決には至っておらず、実装方法の詳細が求められている段階。

Summarized with AI on November 23. AI used: claude-sonnet-4-5-20250929.

現在作成したアプリの審査過程なのですが、以下の問題が解決できずに困っております。

Your primary app listing has 2 issues to fix before you can submit your app for review

App must set security headers to protect against clickjacking.
There was an error installing your app. The app must be installed to perform the security check. Failed to install app.
App must verify the authenticity of the request from Shopify.
There was an error installing your app. The app must be installed to perform the security check. Failed to install app.
開発言語等は以下です。

フレームワーク: Next.js

ストレージ: firestore

デプロイ: vercel

埋め込みアプリではないため、app bridgeは採用しておりません。

Oauthフローはテンプレートは使わずにスクラッチしております。

Oauthフローは以下の流れで作成しました。

  1. アプリ設定に記入したURL

  2. access_mode: per-userで/admin/oauth/authorizeに遷移させる

  3. callback urlで受け取ったユーザ情報を利用して会員登録処理

  4. access_mode: offlineでの認証を裏で済ませ、トークンを更新

  5. アプリのログイン画面に飛ばす

通常のofflineのトークンで運用したいが、ユーザ情報が得られないため

できるだけユーザ登録の手間をかけさせないように、oauthフローは2回行っています。

nonceはフロー1(アプリ設定に記入したURLにアクセス)時にfirestoreに保存してcallback時にチェックしています。

access_mode: offline時も流れは同様です。

アプリ側のログや、firestoreのデータを見る限りインストールは成功しており

ローカルベースではありますが、必要なsecurity headerと認識しているCSP frame-ancestor: 'none’は追加されているようです。

またtop level cookieですが以下のURLを発行しています。

/api/auth/toplevel

/auth/toplevel

/auth

(ただし、合っているか確信が持てておりませんが、埋め込みアプリでないので必要ない認識でおります)

処理はそれぞれ

/api/auth/toplevel

サーバーサイド
shopify_top_level_oauthという名前のcookieを値「1」、sameSite: strict、httpOnlyで設定し以下のhtmlでレスポンス
クライアント

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/@shopify/app-bridge@2"></script>
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        if (window.top === window.self) {
          window.location.href = '/auth?${serializedQuery}';
        } else {
          var AppBridge = window['app-bridge'];
          var createApp = AppBridge.default;
          var Redirect = AppBridge.actions.Redirect;
          const app = createApp({
            apiKey: '${apiKey}',
            host: '${host}',
          });
          const redirect = Redirect.create(app);
          redirect.dispatch(
            Redirect.Action.REMOTE,
            'https://${hostName}/auth/toplevel?${serializedQuery}',
          );
        }
      });
    </script>
  </head>
  <body></body>
</html>

/auth/toplevel

こちらもshopify_top_level_oauthを

next.jsのgetServerSideProps内部(サーバーサイド)でセットし、/authにリダイレクト

/auth

アプリ設定に記入したURLで処理される内容と同等のoauthフローがスタート

なにが間違っているのか全くわからず状態です。

どなたかお力添えをお願いいたします。

App must set security headers to protect against clickjacking.

レスポンスにセキュリティーヘッダーが不足しているようです。

https://shopify.dev/apps/store/security/iframe-protection

↑に記載のヘッダーを vercel or nextjs に指定する必要がありそうです。

App must verify the authenticity of the request from Shopify.

利用している webhook または app proxies で shopify からのリクエストの検証ができてないようです。

https://shopify.dev/apps/webhooks/configuration/https#step-5-verify-the-webhook

↑に記載の通り、shopify からのリクエストが本当にshopifyからのものか検証する処理を追加するとよさそうです。