Skip to main content

Authentication & Session

To authenticate customers with the Recharge JavaScript SDK you will need to do it after customers authenticate with Shopify. It is recommended to piggy-back on the Shopify login APIs and use Shopify's session for storing the Recharge session. You can also clear the session on logout.

In the example below we are showing one way of doing this that works specifically with a HydrogenSession object for saving the Recharge Session within that session. If you aren't using the default Session storage options for Hydrogen you may need to alter this approach.

Note that both tokens used below are Shopify tokens:

Login Utility Function

Create a rechargeUtil.ts module where you can export a loginRecharge function that can be reused.

We are also adding rechargeQueryWrapper to help with data fetching (which is covered in the next section). This helper function handles any needed login retries or/and error handling.

NOTE In this example rechargeQueryWrapper can update the session object. Make sure you are committing the session (in case it changes) after calling the rechargeQueryWrapper function in the response from your loader/action.

/app/lib/rechargeUtils.js

import { loginShopifyApi } from '@rechargeapps/storefront-client';
import { json } from '@shopify/remix-oxygen';

// Recharge session is good for 60 minutes so set to 55 minutes to avoid race conditions
const RECHARGE_SESSION_DURATION = 1000 * 60 * 55;

// loginHelper function
export async function loginRecharge({ hydrogenSession, shopifyStorefrontToken }) {
const customerAccessToken = await hydrogenSession.get('customerAccessToken');
const rechargeSession = await loginShopifyApi(shopifyStorefrontToken, customerAccessToken);

if (rechargeSession) {
const sessionWithExpires = {
...rechargeSession,
expiresAt: Date.now() + RECHARGE_SESSION_DURATION,
};
await hydrogenSession.set('rechargeSession', sessionWithExpires);
} else {
// this should match your catch boundary
throw json('No session created', { status: 400 });
}

return rechargeSession;
}

// helper function for data fetching
export async function rechargeQueryWrapper(rechargeFn, { hydrogenSession, shopifyStorefrontToken }) {
let rechargeSession = await hydrogenSession.get('rechargeSession');
if (!rechargeSession || rechargeSession.expiresAt < Date.now()) {
rechargeSession = await loginRecharge({
hydrogenSession,
shopifyStorefrontToken,
});
}

try {
return await rechargeFn(rechargeSession);
} catch (e) {
try {
if (e?.status === 401) {
// handle auth error, login again and retry request
rechargeSession = await loginRecharge({
hydrogenSession,
shopifyStorefrontToken,
});
return await rechargeFn(rechargeSession);
}
// this should match your catch boundary
throw json(e.message, { status: e?.status });
} catch (error) {
// this should match your catch boundary
throw json(e.message, { status: e?.status });
}
}
}

Login

Now import the loginRecharge function and call it.

NOTE In this example loginRecharge updates the session object. Both of the files being altered already commit the session changes as part of their response, but if you are using this somewhere else you will need to make sure you are committing the session changes after calling the loginRecharge function.

/app/routes/($locale).account.login.jsx & /app/routes/($locale).account.register.jsx - in action functions after getting a customerAccessToken

import { loginRecharge } from '~/lib/rechargeUtils';

// after `session.set('customerAccessToken', customerAccessToken);`
await loginRecharge({
hydrogenSession: session,
shopifyStorefrontToken: context.env.PUBLIC_STOREFRONT_API_TOKEN,
});

Logout

Make sure you clear the rechargeSession from your session on logout.

/app/routes/($locale).account.logout.jsx in action or doLogout function depending on the template chosen

// right after unsetting the customerAccessToken
session.unset('rechargeSession');