Getting CSRF token mismatch / 419 when making POST call to backend

Solved

Getting CSRF token mismatch / 419 when making POST call to backend

atwoodr41
Shopify Partner
3 1 0

I'm using the React/PHP Shopify App template.  I'm trying to connect the React frontend to the PHP backend using web.php to define my routes and using the php Controller classes outlined in the template.  

 

GET requests to my own Controller endpoints are working fine, but when I try to make a POST, I get the following http response:
Response code: 419

 

"message": "CSRF token mismatch.",
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException",
    "file": "/Users/ryan/Repos/ddm-spring-calculator/web/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
    "line": 389,
    "trace": [
        {
            "file": "/Users/ryan/Repos/ddm-spring-calculator/web/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
            "line": 332,
            "function": "prepareException",
            "class": "Illuminate\\Foundation\\Exceptions\\Handler",
            "type": "->"
        },
        {
            "file": "/Users/ryan/Repos/ddm-spring-calculator/web/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
            "line": 51,
            "function": "render",
            "class": "Illuminate\\Foundation\\Exceptions\\Handler",
            "type": "->"
        },
        {
            "file": "/Users/ryan/Repos/ddm-spring-calculator/web/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
            "line": 172,
            "function": "handleException",
            "class": "Illuminate\\Routing\\Pipeline",
            "type": "->"
        },
etc...

 

 

In my `web.php` file, I have this line (along with everything else that comes in the template:

Route::post('/api/springs', [SpringsController::class, 'getSprings']);

My react code looks like this:

 

 const fetchSprings = async () => {
        const response = await httpService.post(`/springs`, {1: "ryan"});
        setResultText("The response is: " + response[1]);
    };
import { useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge/utilities";

class HttpService {
    private authFetch;

    constructor() {
        this.authFetch = authenticatedFetch(useAppBridge());
    }
  
    public async post(route: string, data: any) {
        try {
            const response = await this.authFetch(this.getFullRoute(route), {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(data),
            });

            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }

            const responseData = await response.json();
            console.log(responseData); // Handle the response as needed

            return responseData; // You may want to return the data for further use
        } catch (error) {
            console.error('Error making POST request:', error);
            throw error; // Rethrow the error or handle it as needed
        }
    };

    private getFullRoute(route: string): string {
        const normalizedRoute: string = route.startsWith("/api") ? route : `/api${route}`;
        return `${normalizedRoute}?shop=quickstart-f783802a.myshopify.com`;
    }
}

 

 

And my `SpringsController.php` looks like this:

 

 

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SpringsController extends Controller
{
    public function getSprings(Request $request)
    {
        // Get the JSON data from the request
        $jsonData = $request->json()->all();

        // Access the value using the key '1'
        $value = $jsonData['1'];

        $data = array();
        $data[1] = $value;

        return response()->json($data);
    }
}

 

 

 

In my .env file, I have these values (obfuscated here):

APP_NAME="DDM Spring Calculator"
APP_ENV=local
APP_KEY=base64:***
APP_DEBUG=true
APP_URL=https://quickstart-f783802a.myshopify.com

LOG_CHANNEL=stack
LOG_LEVEL=debug

## sqlite ##
DB_CONNECTION=sqlite
DB_FOREIGN_KEYS=true
DB_DATABASE=/Users/ryan/Repos/ddm-spring-calculator/web/storage/db.sqlite

SHOPIFY_API_KEY=***
SHOPIFY_API_SECRET=***  

 

Any idea why I am getting the CSRF token issue?

Accepted Solution (1)

atwoodr41
Shopify Partner
3 1 0

This is an accepted solution.

I was able to solve this using the extremely helpful post here: https://rafaelcg.com/blog/developer/csrf-on-shopify-apps/ (Thanks Rafael!)

 

There is 1 caveat though.  I got it working based on this general idea.  Here is how I actually got it working:

 

 

 

import { useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge/utilities";

const MyComponent: React.FC = () => {
    const fetch = authenticatedFetch(useAppBridge());
    const csrf = useCsrf();

    const getMyStuff = async () => {
        const response = await fetch('/api/stuff?shop=quickstart-f783802a.myshopify.com', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': await csrf()
            },
        });

        if (!response.ok) {
            setResultText("Rats and shucks!");
            throw new Error(`HTTP error! Status: ${response.status}`);
        } else {
            setResultText("Oh snap, it worked!");
        }
    };

 

View solution in original post

Reply 1 (1)

atwoodr41
Shopify Partner
3 1 0

This is an accepted solution.

I was able to solve this using the extremely helpful post here: https://rafaelcg.com/blog/developer/csrf-on-shopify-apps/ (Thanks Rafael!)

 

There is 1 caveat though.  I got it working based on this general idea.  Here is how I actually got it working:

 

 

 

import { useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge/utilities";

const MyComponent: React.FC = () => {
    const fetch = authenticatedFetch(useAppBridge());
    const csrf = useCsrf();

    const getMyStuff = async () => {
        const response = await fetch('/api/stuff?shop=quickstart-f783802a.myshopify.com', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': await csrf()
            },
        });

        if (!response.ok) {
            setResultText("Rats and shucks!");
            throw new Error(`HTTP error! Status: ${response.status}`);
        } else {
            setResultText("Oh snap, it worked!");
        }
    };