Hi everyone,
We are using:
-
Shopify React Router
-
@shopify/shopify-app-session-storage-prisma -
Prisma
-
Expiring offline access tokens
-
Shopify Flow/background requests
Our Shopify configuration has:
sessionStorage: new PrismaSessionStorage(prisma),
future: {
expiringOfflineAccessTokens: true,
},
How we handle the session
For Flow/background requests, we first check for the offline session in Prisma:
export async function getStoreAccessTokenFromPrisma(
shop: string,
): Promise<string | null> {
const shopSession = await prisma.session.findFirst({
where: { shop, isOnline: false },
});
const accessToken = shopSession?.accessToken;
return typeof accessToken === "string" && accessToken.trim()
? accessToken
: null;
}
Then we use unauthenticated.admin(shop) when the Prisma session exists:
export async function getAdminForShop(
shop: string,
): Promise<GetAdminForShopResult | null> {
const prismaAccessToken = await getStoreAccessTokenFromPrisma(shop);
if (prismaAccessToken) {
return unauthenticated.admin(shop);
}
}
So the expected flow is:
Shopify Flow request
↓
Check Prisma offline session
↓
Session exists
↓
unauthenticated.admin(shop)
↓
Shopify handles token refresh
We also added temporary logging to check the session state:
await logErrorToFirebase({
error: new Error("SESSION_DEBUG_SNAPSHOT"),
module: "SESSION_DEBUG_SNAPSHOT",
context: {
shop,
found: !!shopSession,
expires: shopSession?.expires ?? null,
hasRefreshToken: !!shopSession?.refreshToken,
refreshTokenExpires: shopSession?.refreshTokenExpires ?? null,
now: new Date().toISOString(),
},
});
What we observed
For our test store, on September 18:
found: true
hasRefreshToken: true
expires: September 18, 2026 4:26:47 PM IST
refreshTokenExpires: December 17, 2026 3:26:47 PM IST
So the Prisma session definitely existed and had a refresh token.
On September 19, the same lookup returned:
found: false
hasRefreshToken: false
expires: null
refreshTokenExpires: null
So the entire Prisma session row was missing.
There is no prisma.session.delete() or prisma.session.deleteMany() in this code path.
What happens after opening the app
After we observed that the Prisma session was missing, we opened the Shopify app again.
Our afterAuth hook runs after authentication:
sessionStorage: new PrismaSessionStorage(prisma),
future: {
expiringOfflineAccessTokens: true,
},
hooks: {
afterAuth: async ({ session }) => {
await saveStoreData({
shop: session.shop,
accessToken: session.accessToken!,
});
const { admin } = await shopify.unauthenticated.admin(session.shop);
// other afterAuth logic...
},
},
We added temporary logging inside afterAuth and got:
shop: xxxxx.myshopify.com
isOnline: false
hasAccessToken: true
hasRefreshToken: true
expires: 2026-09-19T17:36:17.318Z
refreshTokenExpires: 2026-12-18T16:36:18.318Z
This suggests the lifecycle is:
September 18
Prisma offline session exists
refresh token exists
↓
September 19
Prisma offline session is missing
↓
Merchant opens the app
↓
afterAuth runs
↓
Offline session exists again
with access token + refresh token
We are trying to understand what could cause the original Prisma session row to disappear before the app was opened again.
Multiple requests
Our app can receive multiple Flow/background requests for the same shop close together.
For example:
08:56:16 → POST /api/flow/action/assignTier
08:56:22 → hasPrismaAccessToken: true
08:56:29 → POST /api/flow/action/assignTier
08:56:30 → hasPrismaAccessToken: true
These requests can both reach:
unauthenticated.admin(shop);
We are therefore investigating whether concurrent refreshes could be related to the session disappearing.
Questions
Could someone help us understand:
-
Does
unauthenticated.admin(shop)already handle concurrent refreshes for the same shop? -
What happens if two requests try to refresh the same offline token at the same time?
-
Can a
401during token refresh cause@shopify/shopify-app-session-storage-prismato delete the Prisma session? -
Is there another reason an existing Prisma offline session row could disappear?
Our main concern is that the session existed with a refresh token on September 18, the entire Prisma row was missing on September 19, and opening the app caused an offline session with a new access & refresh-token pair to be available again.
Any guidance would be appreciated. Thank you!