Shopify自定义应用前端如何更新即将过期的Access Token?
Shopify自定义应用前端更新Access Token方案
核心逻辑
前端无法直接完成token刷新——因为Shopify的token刷新依赖客户端密钥(Client Secret),这个敏感信息绝对不能暴露在前端代码中。正确流程是:前端检测token过期/失效 → 请求后端服务执行刷新逻辑 → 后端返回新token → 前端更新本地存储的token。
具体实现步骤
1. 前端检测token状态
针对Session Token(JWT)
如果使用Shopify嵌入式应用的Session Token,可以解码JWT的exp字段判断过期时间:
// 解码JWT获取过期时间(转换为毫秒) function getTokenExpiration(token) { const payload = JSON.parse(atob(token.split('.')[1])); return payload.exp * 1000; } // 提前1分钟触发刷新 const currentToken = localStorage.getItem('shopify_access_token'); if (currentToken) { const expTime = getTokenExpiration(currentToken); if (Date.now() >= expTime - 60000) { refreshToken(); } }
针对在线访问模式的OAuth Token
后端返回token时需附带过期时间戳,前端存储后定期检查:
const expiresAt = localStorage.getItem('shopify_token_expires_at'); if (Date.now() >= expiresAt - 60000) { refreshToken(); }
2. 后端实现刷新接口
只有开启在线访问模式的Shopify应用才会返回refresh token,后端通过Shopify的OAuth接口完成刷新:
// Express示例 app.post('/api/refresh-shopify-token', async (req, res) => { const { shopDomain, refreshToken } = req.body; try { const response = await fetch(`https://${shopDomain}/admin/oauth/access_token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_id: process.env.SHOPIFY_API_KEY, client_secret: process.env.SHOPIFY_API_SECRET, refresh_token: refreshToken, grant_type: 'refresh_token' }) }); const data = await response.json(); res.json({ accessToken: data.access_token, refreshToken: data.refresh_token || refreshToken, expiresAt: Date.now() + (data.expires_in * 1000) }); } catch (err) { res.status(500).json({ error: 'Token刷新失败' }); } });
3. 前端调用刷新接口并更新token
async function refreshToken() { const shop = localStorage.getItem('shopify_shop_domain'); const refreshToken = localStorage.getItem('shopify_refresh_token'); try { const response = await fetch('/api/refresh-shopify-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ shopDomain: shop, refreshToken }) }); const result = await response.json(); // 更新本地存储 localStorage.setItem('shopify_access_token', result.accessToken); localStorage.setItem('shopify_refresh_token', result.refreshToken); localStorage.setItem('shopify_token_expires_at', result.expiresAt); } catch (err) { // 刷新失败则引导用户重新授权 window.location.href = '/auth'; } }
4. 处理API调用时的401错误
提前检测仍可能出现token过期,需捕获API返回的401状态并重试:
async function callShopifyApi(endpoint, options = {}) { const token = localStorage.getItem('shopify_access_token'); const shop = localStorage.getItem('shopify_shop_domain'); const headers = { 'X-Shopify-Access-Token': token, ...options.headers }; try { const response = await fetch(`https://${shop}/admin/api/2024-07/${endpoint}`, { ...options, headers }); if (response.status === 401) { await refreshToken(); return callShopifyApi(endpoint, options); // 刷新后重试请求 } return response.json(); } catch (err) { throw err; } }
特殊情况:Session Token刷新
如果使用Shopify嵌入式应用的Session Token(有效期24小时),无法通过refresh token刷新,需通过App Bridge重新获取:
import { getSessionToken } from '@shopify/app-bridge-utils'; import createApp from '@shopify/app-bridge'; const app = createApp({ apiKey: 'YOUR_API_KEY', shopOrigin: localStorage.getItem('shopify_shop_domain') }); async function refreshSessionToken() { const newToken = await getSessionToken(app); localStorage.setItem('shopify_access_token', newToken); }
内容的提问来源于stack exchange,提问作者Vinod
相关产品推荐
相关产品推荐

