Authentication issue when calling Remix endpoint from Shopify Liquid template

I’m working on a project where I need to send data from a Shopify theme to my Remix application. I have a contact form in my Liquid template that posts data to my Remix server.

axios.post('https://my-app-tunnel.ngrok.io/submit/participants', {
  data: formData,
  headers: {
    'Content-Type': 'application/json',
  }
});

My Remix action handler works perfectly when I don’t add any authentication middleware:

export const action = async ({ request }: ActionFunctionArgs) => {
  const corsHeaders = new Headers();
  corsHeaders.set('Access-Control-Allow-Origin', '*');

  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  if (request.method === 'POST') {
    const requestData = await request.json();
    
    return json(
      { success: true, payload: requestData },
      { headers: corsHeaders }
    );
  }
};

But as soon as I try to protect the route with Shopify’s authentication, it redirects to the login page:

import { authenticate } from '~/shopify.server';

export const action = async ({ request }: ActionFunctionArgs) => {
  const { admin } = await authenticate.admin(request);
  // Route gets redirected to /auth/login
};

The problem is that my Liquid theme doesn’t send any auth tokens with the request. How do I properly authenticate these cross-origin requests from my Shopify theme to the protected Remix routes?

The authenticate.admin() method only works with session tokens from Shopify’s Admin interface, not your storefront theme. You need a different approach for theme-to-Remix communication. I ran into the same problem and solved it by creating a public endpoint that validates requests using HMAC verification with your app’s secret. Just include the shop domain and timestamp in your axios request, then verify it’s legit on the Remix side without admin authentication. If you need authenticated requests to Shopify APIs from your theme, use Shopify’s storefront access tokens instead. But for your own Remix endpoints, custom validation logic works best.

I ran into this exact problem recently and found a much cleaner fix. You can’t use admin authentication for public theme requests - it just won’t work. Skip the custom HMAC validation and use Shopify’s App Proxy instead. Set up a proxy route in your app settings that forwards theme requests to your Remix app. Shopify handles authentication automatically by adding signature parameters to each request. Your Remix route can then validate the proxy signature using Shopify’s built-in methods - no admin session tokens needed. It’s way more secure than a completely public endpoint and plays nicer with Shopify’s ecosystem. Plus the proxy handles CORS automatically since requests look like they’re coming from your shop’s domain.

Hmm, looks like you’re trying to use admin auth for theme requests - that won’t work. You’ll need to create a separate endpoint without authenticate.admin() and validate using the shop domain plus your webhook secret instead. Themes can’t send admin tokens anyway, so you need a public route with custom validation.

Had this exact problem a few months ago building something similar. Shopify’s authenticate.admin() wants requests from the admin panel with session cookies - not from your theme. Here’s what actually worked: keep the unprotected endpoint but validate where requests come from. Check the Referer header to make sure it’s coming from your Shopify store domain, then verify the shop parameter against your database of installed shops. Stops random external calls but lets legitimate theme requests through. Throw in some rate limiting per shop domain and you’re good. Not as secure as full OAuth, but way better than the redirect loop you’re stuck in right now.

you’re mixing up the auth here. authenticate.admin() is for backend stuff, not theme requests. try using session tokens instead, or create an unprotected endpoint that verifies shopify webhook signatures. that’s how i handle this.

This topic was automatically closed 4 days after the last reply. New replies are no longer allowed.