What's your biggest current challenge? Have your say in Community Polls along the right column.

Re: OAuth Authorization URL for Shopify app development

OAuth Authorization URL for Shopify app development

ironpro
Shopify Partner
24 0 4

 

Hi there!

I am building a Shopify public app (sales channel) using Shopify Polaris and Laravel, hosted on Heroku. I created the app on the Shopify Partner account dashboard and configured it with an app URL and a redirection URL. For the app URL, I added the public link for the Laravel application provided by Heroku. And for the redirection URL, I added the same link followed by /auth/shopify.



After that I set laravel routes as follows.

Route::get('/auth/shopify', [ShopifyAuthController::class, 'redirectToShopify']);
Route::get('/auth/shopify/callback', [ShopifyAuthController::class, 'handleShopifyCallback']);

And here are functions in ShopifyAuthController.

  public function redirectToShopify()
    {
        $shopifyDomain = request('shop');
        $scopes = ['read_products', 'write_products']; // Add necessary scopes for your app
    
        $query = http_build_query([
            'client_id' => env('SHOPIFY_API_KEY'),
            'redirect_uri' => env('SHOPIFY_REDIRECT_URI'),
            'scope' => implode(',', $scopes),
            'state' => csrf_token(),
        ]);
    
        // Construct the OAuth authorization URL
        $authorizationUrl = "https://{$shopifyDomain}/admin/oauth/authorize?{$query}";
    
        return redirect($authorizationUrl);
    }
    
    public function handleShopifyCallback()
    {
        $code = request('code');
        $state = request('state');

        if ($state === null || !hash_equals(csrf_token(), $state)) {
            abort(403, 'Invalid state parameter.');
        }

        // Extract store domain from the callback URL
        $storeDomain = request('shop'); // 'shop' is the query parameter containing the store domain

        // Construct the OAuth endpoint URL for the specific store
        $oauthEndpointUrl = "https://{$storeDomain}/admin/oauth/access_token";

        try {
            $response = Http::post($oauthEndpointUrl, [
                'client_id' => env('SHOPIFY_API_KEY'),
                'client_secret' => env('SHOPIFY_API_SECRET'),
                'code' => $code,
            ]);

            $responseBody = $response->json();

            if (isset($responseBody['access_token'])) {
                return redirect('/home');
            } else {
                throw new \Exception('Access token not found in response.');
            }
        } catch (\Exception $e) {
            // Log or handle the error appropriately
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

Now when I click on the 'Install app' button to add the app to my dev store, it redirects to the application hosted on Heroku instead of being embedded in the Shopify dashboard. Additionally, in the inspect console, I can see the following information.

 

 

hmac: xxxxx 
host: xxxxx
shop: sales-channel-test.myshopify.com
timestamp: 1713581168

 

 

I'm not sure what is missing from my configuration. I think the authentication redirection callback isn't working.

Could you help me resolve this?


Thank you!

 

Developer
Replies 4 (4)

YOD_Solutions
Shopify Partner
258 28 36

Your redirect header needs to contain: 

 "Content-Security-Policy": "frame-ancestors https://"+shop+" https://admin.shopify.com" for your app to be embedded in Shopify admin iframe.

You also need to check if the "embedded" header is set to 1 or 0 - to decide whether you need to redirect from server side or frontend using App Bridge. Your redirection needs to include the state cookie containing a signed state token.  Everything is documented here: Implement authorization code grant manually (shopify.dev)

Founder @ JsRates: Custom Shipping Rates
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more about JsRates visit the JsRates home page or JsRates documentation
- Find JsRates on Shopify app store
ironpro
Shopify Partner
24 0 4

Hi, YOD_Solutions!
Thank you for your kind response!
I have updated the functions according to your reply, but it still shows same. 
It opens the original Laravel app hosted on Heroku, not embedded in Shopify dashboard.


Here is updated ShopifyAuthController.

 

 

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cookie;

class ShopifyAuthController extends Controller
{
    public function redirectToShopify(Request $request)
    {
        $shopifyDomain = $request->input('shop');
        $embedded = $request->header('embedded');
    
        if ($embedded === '1') {
            // If embedded header is set to 1, redirect from frontend using App Bridge
            // Return a response indicating that the redirection will be handled in frontend
            return response()->json(['message' => 'Redirecting from frontend using App Bridge']);
        } else {
            // If embedded header is not set or set to 0, redirect from server-side
            // Construct the OAuth authorization URL
            $scopes = ['read_products', 'write_products']; // Add necessary scopes for your app
            $query = http_build_query([
                'client_id' => env('SHOPIFY_API_KEY'),
                'redirect_uri' => env('SHOPIFY_REDIRECT_URI'),
                'scope' => implode(',', $scopes),
                'state' => csrf_token(),
            ]);
            $authorizationUrl = "https://{$shopifyDomain}/admin/oauth/authorize?{$query}";
    
            // Set the Content-Security-Policy header
            $response = response()->make(redirect($authorizationUrl));
            $response->header('Content-Security-Policy', 'frame-ancestors https://' . $shopifyDomain . ' https://admin.shopify.com');
    
            // Generate and set state cookie
            $state = csrf_token();
            $response->withCookie(Cookie::make('state', $state));
    
            return $response;
        }
    }   

    public function handleShopifyCallback(Request $request)
    {
        // Retrieve the state token from the request
        $state = $request->input('state');
    
        // Verify the state token to ensure it matches the expected value
        if ($state === null || !hash_equals($state, $request->session()->pull('state'))) {
            // If the state token doesn't match, abort the process with an error
            abort(403, 'Invalid state parameter.');
        }
    
        // Retrieve other parameters from the request, such as code and shop
        $code = $request->input('code');
        $storeDomain = $request->input('shop');
    
        // Construct the OAuth endpoint URL for the specific store
        $oauthEndpointUrl = "https://{$storeDomain}/admin/oauth/access_token";
    
        try {
            $response = Http::post($oauthEndpointUrl, [
                'client_id' => env('SHOPIFY_API_KEY'),
                'client_secret' => env('SHOPIFY_API_SECRET'),
                'code' => $code,
            ]);
    
            $responseBody = $response->json();
    
            if (isset($responseBody['access_token'])) {
                // You'll need this access token for making API requests to Shopify
                return redirect('/home');
            } else {
                throw new \Exception('Access token not found in response.');
            }
        } catch (\Exception $e) {
            // Log or handle the error appropriately
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }    
}






 

 

Developer
YOD_Solutions
Shopify Partner
258 28 36

The content security policy header should be "frame-ancestors https://your-shop.myshopify.com https://admin.shopify.com"   - replace "your-shop"  with your shop myshopify domain name. For additional info, please refer to the shopify document I included earlier. Everything is elaborated there.

Founder @ JsRates: Custom Shipping Rates
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more about JsRates visit the JsRates home page or JsRates documentation
- Find JsRates on Shopify app store
ironpro
Shopify Partner
24 0 4

Hi YOD_Solutions! I already set the content security policy header as you can see my last code. Would you mind checking it and give me an advice? Thank you so much!

Developer