diff --git a/CHANGELOG.md b/CHANGELOG.md index 14029c3..302cba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Retry OAuth if cookie / session has expired on callback [112](https://github.com/Shopify/koa-shopify-auth/pull/112) ## [4.1.3] - 2021-04-22 ### Fixed 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 () => {