Skip to content

Commit

Permalink
Adding tests for new flow auth
Browse files Browse the repository at this point in the history
  • Loading branch information
paulomarg committed Feb 5, 2024
1 parent a91fe95 commit 3fdae8e
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/ninety-mugs-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopify/shopify-app-remix": minor
---

Added a new method `shopify.authenticate.flow(request)`, which will validate a Flow extension request, and return the payload / API clients to the app.
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {SessionStorage} from '@shopify/shopify-app-session-storage';
import {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';

import {shopifyApp} from '../../..';
import {
expectAdminApiClient,
getHmac,
getThrownResponse,
setUpValidSession,
testConfig,
} from '../../../__test-helpers';

const FLOW_URL = 'https://example.myapp.io/authenticate/flow';

describe('authenticating flow requests', () => {
it('throws a 405 response if the request method is not POST', async () => {
// GIVEN
const shopify = shopifyApp(testConfig());

// WHEN
const response = await getThrownResponse(
shopify.authenticate.flow,
new Request(FLOW_URL, {method: 'GET'}),
);

// THEN
expect(response.status).toBe(405);
expect(response.statusText).toBe('Method not allowed');
});

it('throws a 400 response if the is missing the HMAC signature', async () => {
// GIVEN
const shopify = shopifyApp(testConfig());

// WHEN
const response = await getThrownResponse(
shopify.authenticate.flow,
new Request(FLOW_URL, {method: 'POST'}),
);

// THEN
expect(response.status).toBe(400);
expect(response.statusText).toBe('Bad Request');
});

it('throws a 400 response if the request has an invalid HMAC signature', async () => {
// GIVEN
const shopify = shopifyApp(testConfig());

// WHEN
const response = await getThrownResponse(
shopify.authenticate.flow,
new Request(FLOW_URL, {
method: 'POST',
headers: {
'X-Shopify-Hmac-Sha256': 'not-the-right-signature',
},
}),
);

// THEN
expect(response.status).toBe(400);
expect(response.statusText).toBe('Bad Request');
});

it('throws a 400 response if there is no session for the shop', async () => {
// GIVEN
const shopify = shopifyApp(testConfig());
const body = {shopify_domain: 'not-the-right-shop.myshopify.io'};

// WHEN
const response = await getThrownResponse(
shopify.authenticate.flow,
new Request(FLOW_URL, {
body: JSON.stringify(body),
method: 'POST',
headers: {
'X-Shopify-Hmac-Sha256': getHmac(JSON.stringify(body)),
},
}),
);

// THEN
expect(response.status).toBe(400);
expect(response.statusText).toBe('Bad Request');
});

it('valid requests with a session succeed', async () => {
// GIVEN
const sessionStorage = new MemorySessionStorage();
const shopify = shopifyApp(testConfig({sessionStorage}));

const {
request,
body,
session: expectedSession,
} = await getValidRequest(sessionStorage);

// WHEN
const {payload, session} = await shopify.authenticate.flow(request);

// THEN
expect(session).toEqual(expectedSession);
expect(payload).toEqual(body);
});

describe('valid requests include an API client object', () => {
expectAdminApiClient(async () => {
const sessionStorage = new MemorySessionStorage();
const shopify = shopifyApp(testConfig({sessionStorage}));

const {request, session: expectedSession} =
await getValidRequest(sessionStorage);

const {admin, session: actualSession} =
await shopify.authenticate.flow(request);

if (!admin) {
throw new Error('No admin client');
}

return {admin, expectedSession, actualSession};
});
});
});

async function getValidRequest(sessionStorage: SessionStorage) {
const session = await setUpValidSession(sessionStorage, {isOnline: false});

const body = {shopify_domain: session.shop};
const bodyString = JSON.stringify(body);

const request = new Request(FLOW_URL, {
body: bodyString,
method: 'POST',
headers: {
'X-Shopify-Hmac-Sha256': getHmac(bodyString),
},
});

return {body, request, session};
}
5 changes: 4 additions & 1 deletion packages/shopify-app-remix/src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ interface Authenticate<Config extends AppConfigArg> {
* export async function action({ request }: ActionFunctionArgs) {
* const {admin, session, payload} = authenticate.flow(request);
*
* return json(await admin.rest.resources.Product.count({ session }));
* // Perform flow extension logic
*
* // Return a 200 response
* return null;
* }
* ```
* ```ts
Expand Down

0 comments on commit 3fdae8e

Please sign in to comment.