Closed ghussain08 closed 1 month ago
Update
I'm not sure if this is the correct approach, but here’s my current solution:
I’ve added a new column named isScopeUpdated
to the Session table. Whenever I deploy the app with updated scopes, I set isScopeUpdated
to false
for all stores. When fetching the session in loadSession
, I only fetch sessions where isScopeUpdated
is true
. This forces Shopify to re-trigger token generation and fire the afterAuth
hook.
I'm not sure if this is the correct approach, but here’s my current solution:
I’ve added a new column named isScopeUpdated
to the Session table. Whenever I deploy the app with updated scopes, I set isScopeUpdated
to false
for all stores. When fetching the session in loadSession
, I only fetch sessions where isScopeUpdated
is true
. This forces Shopify to re-trigger token generation and fire the afterAuth
hook.
Updated CustomSessionStorage
import type { SessionStorage } from "@shopify/shopify-app-session-storage";
import prisma from "./db.server";
import { Session } from "@shopify/shopify-api";
export default class CustomSessionStorage implements SessionStorage {
async deleteSession(id: string): Promise<boolean> {
await prisma.session.update({
where: { id: id },
data: { isDeleted: true },
});
return true;
}
async deleteSessions(ids: string[]): Promise<boolean> {
await prisma.session.updateMany({
where: { id: { in: ids } },
data: { isDeleted: true },
});
return true;
}
async findSessionsByShop(shop: string): Promise<Session[]> {
const sessions = await prisma.session.findMany({
where: { shop, isDeleted: false, isScopeUpdated: true },
});
return sessions.map(
(session) =>
new Session({
id: session.id,
accessToken: session.accessToken,
state: session.state,
shop: session.shop,
isOnline: true,
}),
);
}
async loadSession(id: string): Promise<Session | undefined> {
const session = await prisma.session.findUnique({
where: { id, isDeleted: false, isScopeUpdated: true },
});
if (!session) return undefined;
console.log(`Session loaded: ${session.id}`);
return new Session({
id: session.id,
accessToken: session.accessToken,
state: session.state,
shop: session.shop,
isOnline: true,
scope: session.scope ? session.scope : undefined,
});
}
async storeSession(session: Session): Promise<boolean> {
console.log(`Storing session: ${session.id}`, session);
await prisma.session.upsert({
where: { id: session.id },
update: {
accessToken: session.accessToken,
scope: session.scope,
isDeleted: false,
isScopeUpdated: true,
state: session.state,
},
create: {
id: session.id,
accessToken: session.accessToken!,
scope: session.scope,
isDeleted: false,
state: session.state,
shop: session.shop,
installedAt: new Date(),
isScopeUpdated: true,
},
});
console.log(`Session stored: ${session.id}`);
return true;
}
}
Hey @ghussain08 --
I'm sorry to hear you had to find a work-around to re-run afterAuth
. afterAuth
runs after token exchange occurs. Once the app is installed, token exchange usually will not be performed unless the access token has been invalidated by Shopify, the app, or is expired. In this case you're manually "invalidating" the accessToken by returning an undefined session.
If you want to know when the installed access scopes has been changed, we added a new Webhook topic app/scopes_update that notifies when the granted access scopes has been modified for an installation. It'll be available in the 10/2024 API release. You can subscribe to that topic and upon receiving that notification:
afterAuth
operations (it should eliminate your work-around)scopes
column in the DB to reflect the newly granted scopes
I've noticed that the
afterAuth
hook is not invoked when access scopes are updated in theshopify.app.toml
and.env
file and the app is redeployed. Merchants see the approval screen and can update the access scopes, but the afterAuth hook, which is supposed to handle authentication, is only triggered during the initial app installation.The name
afterAuth
suggests it should be invoked after any authentication event, but it is only invoked on app installation. Wouldn't a name likeafterInstall
be more appropriate to avoid confusion?Is there a way to detect access scope changes when a merchant updates the app and approves new access scopes?
This is my setup code
I'm using CustomSessionStorage