后端OAuth2.0认证流程 1. 安装依赖 Codeblock 1 npm install express crypto axios 2. 配置⽂件/config/index.js ⽤于存放APP_NAME,CLIENT_ID,CLIENT_SECRET,REDIRECT_URI Codeblock const CLIENT_ID = ""; const CLIENT_SECRET = ""; const BASE_URL = ""; const REDIRECT_URI = `${BASE_URL}/auth/shoplazza/callback`; let access_token = {}; 3. HMAC校验中间件/middleware/hmacValidator.js Codeblock 9 import crypto from "crypto"; export const hmacValidator = async (ctx, next) => { const { hmac, ...queryParams } = ctx.query; if (!hmac) { ctx.status = 400; ctx.body = { error: "Missing HMAC parameter" }; return; } // 计算 HMAC 校验 const message = Object.keys(queryParams) .sort() .map((key) => `${key}=${queryParams[key]}`) .join("&"); const generatedHmac = crypto .createHmac("sha256", process.env.CLIENT_SECRET) .update(message) .digest("hex"); if (crypto.timingSafeEqual(Buffer.from(generatedHmac), Buffer.from(hmac))) { await next(); } else { ctx.status = 403; ctx.body = { error: "HMAC validation failed" }; } }; 4. 认证路由/routes/auth.js Codeblock import Router from "koa-router"; import crypto from "crypto"; import axios from "axios"; import { APP_NAME, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } from "../config/index.js"; import { hmacValidator } from "../middleware/hmacValidator.js"; const router = new Router({ prefix: "/api" }); router.get(`/auth/install`, async (ctx) => { const shop = ctx.query.shop; if (!shop) { ctx.status = 400; ctx.body = { error: "Missing shop parameter" }; return; } const scopes = "read_customer,read_product,write_product"; const state = crypto.randomBytes(16).toString("hex"); const redirectUri = `https://${ctx.host}${REDIRECT_URI}`; const authUrl = `https://${shop}/admin/oauth/authorize? client_id=${CLIENT_ID}&scope=${scopes}&redirect_uri=${redirectUri}&response_typ e=code&state=${state}`; ctx.redirect(authUrl); }); // ** Step 2: 处理 OAuth 回调 ** router.get(`/auth/callback`, hmacValidator, async (ctx) => { const { code, shop } = ctx.query; if (!shop || !code) { ctx.status = 400; ctx.body = { error: "Missing required parameters" }; return; } const redirectUri = `https://${ctx.host}${REDIRECT_URI}`; try { const response = await axios.post(`https://${shop}/admin/oauth/token`, { client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code, grant_type: "authorization_code", redirect_uri: redirectUri, }); console.log(" 获取 access_token 成功 :", response.data); ctx.body = response.data; // 返回 token 及 store 信息 } catch (error) { console.error(" 获取 access_token 失败 :", error.message); ctx.status = 500; ctx.body = { error: "Failed to obtain access token" }; } }); export default router; 5. 启动服务器挂载oauth,认证路由 Codeblock 1 2 const PORT = process.env.PORT || 3000; app.listen(PORT, () => { 3 4 console.log(`Server running on http://localhost:${PORT}`); });