App reviews, troubleshooting, and recommendations
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!
Your redirect header needs to contain:
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)
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);
}
}
}
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.
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!
Hey Community! As the holiday season unfolds, we want to extend heartfelt thanks to a...
By JasonH Dec 6, 2024Dropshipping, a high-growth, $226 billion-dollar industry, remains a highly dynamic bus...
By JasonH Nov 27, 2024Hey Community! It’s time to share some appreciation and celebrate what we have accomplis...
By JasonH Nov 14, 2024