🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 kürzlich 4 Min Lesezeit
0

Your Shopify app's real OAuth problem isn't Shopify — it's the other API

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Every Shopify tutorial covers the same OAuth dance: merchant installs, you get a token, done. The framework handles 100% of it.



Then you build a real app. A real app connects to something — Xero, QuickBooks, Klaviyo, an ERP, a shipping API — because an app that only talks to Shopify is rarely worth $19/month. And that second OAuth integration is 100% yours. No framework, no guardrails.



I shipped a Shopify-to-Xero sync app. Here are the four failure modes that only showed up days or weeks after things "worked", and the patterns that survive them.






1. Refresh tokens rotate — persist BOTH tokens, every refresh



Many providers (Xero included) issue a new refresh token every time you use one. If your refresh handler only saves the new access token, everything works... until the next refresh, when your stored refresh token turns out to be single-use and already spent. The connection dies silently. The merchant blames your app.




CODE
const updated = await db.partnerConnection.update({
where: { shop },
data: {
accessToken: encryptSecret(tokens.access_token),
refreshToken: encryptSecret(tokens.refresh_token), // rotation!
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
},
});









2. Refresh tokens die of loneliness — keep-alive cron



Xero's refresh tokens expire after ~60 idle days. Think about who goes idle: a merchant with a seasonal shop installs in January, goes quiet, comes back for the summer rush — dead connection, angry merchant, support ticket.



Fix: a weekly cron that force-refreshes every stored connection, regardless of expiry. Our getValidConnection(shop, { force: true }) flag exists purely for this keep-alive job.






3. Refresh proactively, not on 401



Refreshing 60 seconds before expiry instead of reacting to a 401 saves you a failed call + retry — which matters a lot during a webhook burst (a flash sale means dozens of order webhooks, each wanting an API call).




CODE
if (!opts.force && conn.expiresAt.getTime() - 60_000 > Date.now()) {
return decrypted(conn); // still fresh
}
// otherwise refresh now, before the API call









4. Rate limits: evenly-spaced throttle + Retry-After



Partner APIs limit per tenant (Xero: 60/min). You don't control your traffic — Shopify webhooks do. Two layers:




CODE
// Layer 1: never hit the limit — evenly space calls
let nextSlot = 0;
async function throttle() {
const gapMs = Math.ceil(60_000 / CALLS_PER_MINUTE) + 100;
const wait = Math.max(0, nextSlot - Date.now());
nextSlot = Math.max(Date.now(), nextSlot) + gapMs;
if (wait > 0) await new Promise(r => setTimeout(r, wait));
}

// Layer 2: if a 429 slips through, honour Retry-After (up to 3 attempts)
if (res.status === 429 && attempt < 3) {
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "2", 10);
await new Promise(r => setTimeout(r, retryAfter * 1000));
}









Bonus gotchas





  • The consent screen can't run inside the Shopify admin iframe. Open it in a new tab, and carry the shop domain through the round-trip in the state param — the callback lands in a bare tab with no session you can trust.


  • Encrypt tokens at rest. Shopify's protected-customer-data expectations (and basic hygiene) mean your partner's refresh token shouldn't sit in plaintext in Postgres. AES-256-GCM with a versioned prefix lets you turn encryption on after launch without a migration.


  • Design the reconnect UX on day one. Connections die (revoked, expired, account closed). A null connection should render a "Reconnect" button, not crash a dashboard — and failed jobs should self-heal via a retry cron once the merchant reconnects.






Where this comes from



These patterns are lifted from the production code of my Shopify-to-Xero app, and they're the core of chapter 4 of . And even if you never buy anything: persist both tokens. Future you will thank present you.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Your Shopify app's real OAuth problem isn't Shopify — it's the other API

Thematisch verwandte Begriffe: Your, Shopify, apps, real · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...