You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

有效离线会话下App Proxy调用Shopify GraphQL API仍报401未授权

使用Shopify App Remix离线会话调用Admin GraphQL API返回401未授权的排查方案

问题描述

使用官方@shopify/shopify-app-remix框架搭配PrismaSessionStorage开发Shopify应用时,在App Proxy路由中通过api.session.getOfflineId(shop)获取离线会话(offline session)发送Admin GraphQL请求,始终收到以下错误:

GraphQL Error: HttpResponseError: Received an error response (401 Unauthorized) from Shopify: "GraphQL Client: Unauthorized"

已验证的有效条件

  • App Proxy的HMAC签名已验证且有效
  • 通过getOfflineId()成功从Prisma中获取会话
  • session.accessToken存在且本地显示未过期
  • 更新权限范围后已重新安装应用
  • shopify.app.toml和.env中的权限范围配置一致且正确
  • 即使是{ shop { name } }这类最简单的GraphQL查询也失败

相关配置与代码

shopify.app.toml

client_id = "..."
name = "Cart Reminder Workflow Trigger"
application_url = "https://my-app-url.trycloudflare.com"
embedded = true

[access_scopes]
scopes = "write_app_proxy,read_customers,write_customers"

[app_proxy]
url = "https://my-app-url.trycloudflare.com/api"
prefix = "apps"
subpath = "cart-reminder"

[auth]
redirect_urls = [
  "https://my-app-url.trycloudflare.com/auth/callback",
  "https://my-app-url.trycloudflare.com/api/auth/callback"
]

shopify.server.ts

import "@shopify/shopify-app-remix/adapters/node";
import {
  ApiVersion,
  AppDistribution,
  shopifyApp,
} from "@shopify/shopify-app-remix/server";
import { shopifyApi } from "@shopify/shopify-api";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import prisma from "./db.server";

const sessionStorage = new PrismaSessionStorage(prisma);
const appUrl = process.env.SHOPIFY_APP_URL || "";
const url = new URL(appUrl);

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY!,
  apiSecretKey: process.env.SHOPIFY_API_SECRET!,
  apiVersion: ApiVersion.January25,
  scopes: process.env.SCOPES!.split(","),
  appUrl,
  authPathPrefix: "/auth",
  sessionStorage,
  distribution: AppDistribution.AppStore,
  future: {
    unstable_newEmbeddedAuthStrategy: true,
    removeRest: true,
  },
});

export const api = shopifyApi({
  apiKey: process.env.SHOPIFY_API_KEY!,
  apiSecretKey: process.env.SHOPIFY_API_SECRET!,
  apiVersion: ApiVersion.January25,
  scopes: process.env.SCOPES!.split(","),
  hostName: url.hostname,
  hostScheme: url.protocol.replace(":", "") as "http" | "https",
  isEmbeddedApp: true,
});

export default shopify;
export const authenticate = shopify.authenticate;
export const sessionStorageInstance = sessionStorage;

App Proxy路由(app/routes/api.backend-collector.tsx)

const sessionId = api.session.getOfflineId(shop);
const session = await shopify.sessionStorage.loadSession(sessionId);

if (!session || !session.accessToken) {
  return json({ error: "Unauthorized" }, { status: 401 });
}

const admin = new api.clients.Graphql({ session });

const result = await admin.request(`{ shop { name } }`);

可能的原因及解决方法

1. 离线会话的Shop域名格式不匹配

Shopify会话存储中,shop域名要求是完整的小写myshopify.com格式(如example.myshopify.com),如果传入getOfflineId的shop参数是简化格式(如example、example.com)或大小写不一致,会导致加载的会话与目标店铺不匹配,token无法授权。

  • 解决:获取shop参数后,用shopify.utils.sanitizeShop(shop)进行标准化处理,确保格式完全一致。

2. 独立实例化的shopifyApi与shopifyApp配置不一致

代码中同时初始化了shopifyApp和独立的shopifyApi实例,两者的配置(如apiVersion、isEmbeddedApp)若存在差异,会导致会话验证逻辑冲突,生成的请求无法通过Shopify的授权校验。

  • 解决:直接复用shopifyApp实例内置的api对象,替换独立初始化的shopifyApi:
    // 替换原有的export const api = shopifyApi(...)
    export const api = shopify.api;
    

3. 离线会话的accessToken实际已失效

即使本地存储的token显示未过期,也可能因店铺重新安装时未刷新会话、Shopify后台手动撤销token等原因导致token无效。

  • 解决:
    • 加载会话后,先用REST API测试token有效性:await api.clients.Rest({ session }).get({ path: 'shop' })
    • 卸载应用并清除本地Prisma中的会话数据,重新安装应用以获取新的离线token。

4. App Proxy请求中的shop参数未标准化

App Proxy请求传入的shop参数可能存在变种(如带端口、非标准子域名),导致加载的会话与实际请求店铺不匹配。

  • 解决:在处理App Proxy请求时,先对shop参数进行标准化:
    const sanitizedShop = shopify.utils.sanitizeShop(request.url.searchParams.get('shop')!, true);
    const sessionId = api.session.getOfflineId(sanitizedShop);
    

5. 权限范围未实际生效

虽然配置了权限并重新安装,但可能因安装时授权流程未完成、Shopify后台权限缓存未更新,导致离线token未包含所需权限(即使是基础查询也会因token无效返回401)。

  • 解决:
    • 在店铺后台的「应用管理」中查看该应用的实际权限,确认与配置一致
    • 卸载应用后,清除本地会话存储,重新完成安装授权流程

内容的提问来源于stack exchange,提问作者Vincent Gates

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:44:54