diff --git a/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts b/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts index f0d58f680..151761786 100644 --- a/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts +++ b/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts @@ -111,6 +111,24 @@ describe('validateAuthenticatedSession', () => { ).toBe(`/api/auth?shop=my-shop.myshopify.io`); }); + it('no session, no shop param, with auth header, returns 403 with correct headers', async () => { + jest + .spyOn(shopify.api.session, 'getCurrentId') + .mockResolvedValueOnce(undefined); + + const response = await request(app) + .get('/test/shop') + .set({Authorization: `Bearer ${validJWT}`}) + .expect(403); + + expect( + response.headers['x-shopify-api-request-failure-reauthorize'], + ).toBe('1'); + expect( + response.headers['x-shopify-api-request-failure-reauthorize-url'], + ).toBe(`/api/auth?shop=my-shop.myshopify.io`); + }); + it('no session, without auth header redirects to auth', async () => { const response = await request(app) .get('/test/shop?shop=my-shop.myshopify.io') diff --git a/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts b/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts index e38fe502b..c414cd74a 100644 --- a/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts +++ b/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts @@ -49,7 +49,7 @@ export function validateAuthenticatedSession({ } } - const shop = req.query.shop || session?.shop; + let shop = req.query.shop || session?.shop; if (session && shop && session.shop !== shop) { config.logger.debug( @@ -82,6 +82,17 @@ export function validateAuthenticatedSession({ } } + const bearerPresent = req.headers.authorization?.match(/Bearer (.*)/); + if (bearerPresent) { + if (!shop) { + shop = await setShopFromSessionOrToken( + api, + session, + bearerPresent[1], + ); + } + } + const redirectUrl = `${config.auth.path}?shop=${shop}`; config.logger.info( `Session was not valid. Redirecting to ${redirectUrl}`, @@ -91,7 +102,7 @@ export function validateAuthenticatedSession({ return returnTopLevelRedirection({ res, config, - bearerPresent: Boolean(req.headers.authorization?.match(/Bearer (.*)/)), + bearerPresent: Boolean(bearerPresent), redirectUrl, }); }; @@ -110,3 +121,19 @@ async function handleSessionError(_req: Request, res: Response, error: Error) { break; } } + +async function setShopFromSessionOrToken( + api: Shopify, + session: Session | undefined, + token: string, +): Promise { + let shop: string | undefined; + + if (session) { + shop = session.shop; + } else if (api.config.isEmbeddedApp) { + const payload = await api.session.decodeSessionToken(token); + shop = payload.dest.replace('https://', ''); + } + return shop; +}