fly.io 上托管的 Shopify 应用 oAuth/Auth 回调失败

问题描述 投票:0回答:1

目前我正在尝试将我的 Shopify 自定义应用程序部署到 Fly.io。在我的开发商店中安装此应用程序成功,但在使用 oAuth 回调后立即收到错误,状态代码为 400。这是安装时显示的 URL:

https://appname.fly.dev/api/auth/callback?code=71bfdaadd63b87eb72d9d3dc516ea1ea&hmac=1efd4ff63ebca8f28c733f464ded354ba2f0995aeb1910114e0139eaefd4cce3&host=YWRtaW4uc2hvcGlmeS5jb20vc3RvcmUvc2hvb3B5bG9vcHkx&shop=shoopyloopy1.myshopify.com&state=920113322594675&timestamp=1676563785

正文中包含文字:

Invalid OAuth callback.

该应用程序在开发过程中可与所有与 ngrok 隧道一起使用的回调一起使用。只是部署到 Fly.io 时并非如此。应用程序前端在部署到 Fly.io 后也可以工作,但所有 api 和身份验证回调都无法工作。我在这些 API 调用上得到以下响应:

在 /api/ 路由上执行 API 调用时,我在 api 调用的返回中收到以下错误:

Failed to parse session token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvc2hvb3B5bG9vcHkxLm15c2hvcGlmeS5jb21cL2FkbWluIiwiZGVzdCI6Imh0dHBzOlwvXC9zaG9vcHlsb29weTEubXlzaG9waWZ5LmNvbSIsImF1ZCI6IjU4YTAzZjkwZTk4Yjc5NGRlZmE5NDZlMWZiNmVlMzRiIiwic3ViIjoiNzQ3NzAxNTM2NjEiLCJleHAiOjE2NzY1NjQyMjYsIm5iZiI6MTY3NjU2NDE2NiwiaWF0IjoxNjc2NTY0MTY2LCJqdGkiOiI0OTcyNDEwOC0zNWQ2LTRjODEtOWJkNS0wZWRkMWM4MWIxMDYiLCJzaWQiOiIxOGZmZjg5NTMyZGRiODdiOWQ3OTBhYmY1M2EwOTZiMDNkNmE4ZWU1ZTA0ZmRjZmFmOWUxOWM2OGQxZGFjN2Q2In0.XeuA5W95YjjVLZYOvmRJ9a90xpPNEukhNQ1_z4Kw_xA': signature verification failed

我的fly.toml文件:


app = "appname"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[env]
  PORT = "8081"
  HOST = "https://appname.fly.dev"
  SHOPIFY_API_KEY = "58a03f90e98b794defa946e1fb6ee34b"
  SCOPES = "write_products,read_script_tags,write_script_tags"

[experimental]
  auto_rollback = true

[[services]]
  http_checks = []
  internal_port = 8081
  processes = ["app"]
  protocol = "tcp"
  script_checks = []
  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

我的index.js 文件启动express:

// @ts-check
import { join } from "path";
import { readFileSync } from "fs";
import express from "express";
import serveStatic from "serve-static";

import shopify from "./shopify.js";
import productCreator from "./product-creator.js";
import GDPRWebhookHandlers from "./gdpr.js";
import {addScriptTag, deleteScriptTag, getProductInfo, getProductsFromIds, getScriptTags} from "./graph-functions.js";

const PORT = parseInt(process.env.BACKEND_PORT || process.env.PORT, 10);

const STATIC_PATH =
  process.env.NODE_ENV === "production"
    ? `${process.cwd()}/frontend/dist`
    : `${process.cwd()}/frontend/`;

const app = express();

// Set up Shopify authentication and webhook handling
app.get(shopify.config.auth.path, shopify.auth.begin());
app.get(
  shopify.config.auth.callbackPath,
  shopify.auth.callback(),
  shopify.redirectToShopifyOrAppRoot()
);
app.post(
  shopify.config.webhooks.path,
  shopify.processWebhooks({ webhookHandlers: GDPRWebhookHandlers })
);

// All endpoints after this point will require an active session
app.use("/api/*", shopify.validateAuthenticatedSession());

app.use(express.json());

app.get("/api/products/count", async (_req, res) => {
  const countData = await shopify.api.rest.Product.count({
    session: res.locals.shopify.session,
  });
  res.status(200).send(countData);
});

app.get("/api/get-product", async (_req, res) => {
  const products = await getProductInfo(res.locals.shopify.session, _req.query.id);
  res.status(200).send(products);
});

app.get("/api/get-products", async (_req, res) => {
  const products = await getProductsFromIds(res.locals.shopify.session,_req.query.ids);
  res.status(200).send(products);
});

app.get("/api/add-script", async (_req, res) => {
  const data = await addScriptTag(res.locals.shopify.session,_req.query.src);
  console.log(_req.query.ids);
  res.status(200).send(data);
});

app.get("/api/get-scripts", async (_req, res) => {
  const data = await getScriptTags(res.locals.shopify.session);
  res.status(200).send(data?.body?.data ? data?.body?.data : data);
});

app.get("/api/delete-script", async (_req, res) => {
  const data = await deleteScriptTag(res.locals.shopify.session,_req.query.id);
  res.status(200).send(data);
});

app.get("/api/products/create", async (_req, res) => {
  let status = 200;
  let error = null;

  try {
    await productCreator(res.locals.shopify.session);
  } catch (e) {
    console.log(`Failed to process products/create: ${e.message}`);
    status = 500;
    error = e.message;
  }
  res.status(status).send({ success: status === 200, error });
});

app.use(serveStatic(STATIC_PATH, { index: false }));

app.use("/*", shopify.ensureInstalledOnShop(), async (_req, res, _next) => {
  return res
    .status(200)
    .set("Content-Type", "text/html")
    .send(readFileSync(join(STATIC_PATH, "index.html")));
});

app.listen(PORT);

任何帮助将不胜感激。

我遵循了官方文档:Shopify官方文档

Dockerfile 具有与 Fly.toml 文件中分配的端口相同的 8081 端口。

编辑(添加了带有数据库的shopify应用程序实现):

import { LATEST_API_VERSION } from "@shopify/shopify-api";
import { shopifyApp } from "@shopify/shopify-app-express";
import { SQLiteSessionStorage } from "@shopify/shopify-app-session-storage-sqlite";
import { restResources } from "@shopify/shopify-api/rest/admin/2023-01";

const DB_PATH = `${process.cwd()}/database.sqlite`;

const shopify = shopifyApp({
  api: {
    apiVersion: LATEST_API_VERSION,
    restResources,
    billing: undefined, // or replace with billingConfig above to enable example billing
  },
  auth: {
    path: "/api/auth",
    callbackPath: "/api/auth/callback",
  },
  webhooks: {
    path: "/api/webhooks",
  },
  // This should be replaced with your preferred storage strategy
  sessionStorage: new SQLiteSessionStorage(DB_PATH),
});

export default shopify;

authentication oauth shopify shopify-app fly
1个回答
0
投票

有点晚了,但如果您重新启动 Shopify 应用程序服务器(例如,如果您在本地运行它并定期终止进程并重新启动它),然后卸载该应用程序,则 Shopify 应用程序上的卸载 Webhook 永远不会运行,并且所有后续请求都会使用无效的 API 密钥,因为 Shopify 应用程序认为它从未被卸载。

您可以通过访问后端 Shopify 应用程序页面(例如默认混音应用程序中的生成产品页面),使用新的 API 密钥“重新注册”应用程序,之后您应该没问题,下次卸载该应用程序当shopify应用程序服务器仍在运行时。 如果是像 Fly.io 这样的远程服务器问题,我建议卸载并重新安装该应用程序。

另外,让我注意到的另一件事是,您可以在 Shopify 商店后端触发的 Webhook 无法触发 Shopify 应用程序 Webhook...相反,您只需要执行通常需要的任何操作即可。

github问题的答案

非常不直观,但这就是目前的工作原理。

© www.soinside.com 2019 - 2024. All rights reserved.