diff --git a/src/auth/index.ts b/src/auth/index.ts index 0ceefd0..2fc4377 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -107,8 +107,10 @@ export default function createShopifyAuth(options: OAuthStartOptions) { case (e instanceof Shopify.Errors.InvalidOAuthError): ctx.throw(400, e.message); break; + case (e instanceof Shopify.Errors.CookieNotFound): case (e instanceof Shopify.Errors.SessionNotFound): - ctx.throw(403, e.message); + // This is likely because the OAuth session cookie expired before the merchant approved the request + ctx.redirect(`${oAuthStartPath}?shop=${ctx.query.shop}`); break; default: ctx.throw(500, e.message); diff --git a/src/auth/test/index.test.ts b/src/auth/test/index.test.ts index 84d46ca..9ee3e22 100644 --- a/src/auth/test/index.test.ts +++ b/src/auth/test/index.test.ts @@ -212,7 +212,7 @@ describe('Index', () => { expect(ctx.throw).toHaveBeenCalledWith(400, ''); }); - it('throws a 403 if the session does not exist', async () => { + it('retries if the session does not exist', async () => { Shopify.Auth.validateAuthCallback = jest.fn(() => Promise.reject(new Shopify.Errors.SessionNotFound)); const ctx = createMockContext({ @@ -223,7 +223,21 @@ describe('Index', () => { const shopifyAuth = createShopifyAuth(baseConfig); await shopifyAuth(ctx, nextFunction); - expect(ctx.throw).toHaveBeenCalledWith(403, ''); + expect(ctx.redirect).toHaveBeenCalledTimes(1); + }); + + it('retries if the cookie does not exist', async () => { + Shopify.Auth.validateAuthCallback = jest.fn(() => Promise.reject(new Shopify.Errors.CookieNotFound)); + + const ctx = createMockContext({ + url: `${baseCallbackUrl}?${querystring.stringify(queryData)}`, + throw: jest.fn(), + }); + + const shopifyAuth = createShopifyAuth(baseConfig); + await shopifyAuth(ctx, nextFunction); + + expect(ctx.redirect).toHaveBeenCalledTimes(1); }); it('throws a 500 on any other errors', async () => {