Skip to content

Commit 3e99d44

Browse files
enmaboyaKyon147
andauthored
Replace legacy app bridge with app bridge cdn (#361)
* remove old config parameters * use shopify global object from app bridge cdn * remove unsupported turbolinks * update tests * Update to use the utils helper. Force https on the url if there is one from the origin. This can help with some ngrok issues when testing locally and stops mix matched requests. * Add some defensive checks to the token blade file * need to write the url updates * use the helper * Update the test to still check for a correct api key --------- Co-authored-by: Luke Walsh <kyon.rf@gmail.com>
1 parent c9eb7df commit 3e99d44

13 files changed

+69
-168
lines changed

src/Http/Middleware/VerifyShopify.php

+3-14
Original file line numberDiff line numberDiff line change
@@ -377,20 +377,9 @@ protected function getHmacFromRequest(Request $request): array
377377
*/
378378
protected function getAccessTokenFromRequest(Request $request): ?string
379379
{
380-
if (Util::getShopifyConfig('turbo_enabled')) {
381-
if ($request->bearerToken()) {
382-
// Bearer tokens collect.
383-
// Turbo does not refresh the page, values are attached to the same header.
384-
$bearerTokens = Collection::make(explode(',', $request->header('Authorization', '')));
385-
$newestToken = Str::substr(trim($bearerTokens->last()), 7);
386-
387-
return $newestToken;
388-
}
389-
390-
return $request->get('token');
391-
}
392-
393-
return $this->isApiRequest($request) ? $request->bearerToken() : $request->get('token');
380+
return $this->isApiRequest($request)
381+
? $request->bearerToken()
382+
: $request->get('token');
394383
}
395384

396385
/**

src/Traits/AuthController.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ public function authenticate(Request $request, AuthenticateShop $authShop)
6464
'shopify-app::auth.fullpage_redirect',
6565
[
6666
'apiKey' => Util::getShopifyConfig('api_key', $shopOrigin),
67-
'appBridgeVersion' => Util::getShopifyConfig('appbridge_version') ? '@'.config('shopify-app.appbridge_version') : '',
68-
'authUrl' => $result['url'],
67+
'url' => $result['url'],
6968
'host' => $request->get('host'),
7069
'shopDomain' => $shopDomain,
7170
'locale' => $request->get('locale'),

src/resources/config/shopify-app.php

-33
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
*/
3838

3939
'domain' => env('SHOPIFY_DOMAIN'),
40-
4140
/*
4241
|--------------------------------------------------------------------------
4342
| Manual routes
@@ -138,26 +137,6 @@
138137

139138
'prefix' => env('SHOPIFY_APP_PREFIX', ''),
140139

141-
/*
142-
|--------------------------------------------------------------------------
143-
| AppBridge Mode
144-
|--------------------------------------------------------------------------
145-
|
146-
| AppBridge (embedded apps) are enabled by default. Set to false to use legacy
147-
| mode and host the app inside your own container.
148-
|
149-
*/
150-
151-
'appbridge_enabled' => (bool) env('SHOPIFY_APPBRIDGE_ENABLED', true),
152-
153-
// Use semver range to link to a major or minor version number.
154-
// Leaving empty will use the latest version - not recommended in production.
155-
'appbridge_version' => env('SHOPIFY_APPBRIDGE_VERSION', 'latest'),
156-
157-
// Set a new CDN URL if you want to host the AppBridge JS yourself or unpkg goes down.
158-
// DO NOT include a trailing slash.
159-
'appbridge_cdn_url' => env('SHOPIFY_APPBRIDGE_CDN_URL', 'https://unpkg.com'),
160-
161140
/*
162141
|--------------------------------------------------------------------------
163142
| Shopify App Name
@@ -513,18 +492,6 @@
513492

514493
'config_api_callback' => null,
515494

516-
/*
517-
|--------------------------------------------------------------------------
518-
| Enable Turbolinks or Hotwire Turbo
519-
|--------------------------------------------------------------------------
520-
|
521-
| If you use Turbolinks/Turbo and Livewire, turn on this setting to get
522-
| the token assigned automatically.
523-
|
524-
*/
525-
526-
'turbo_enabled' => (bool) env('SHOPIFY_TURBO_ENABLED', false),
527-
528495
/*
529496
|--------------------------------------------------------------------------
530497
| Customize Models and Table Name

src/resources/views/auth/fullpage_redirect.blade.php

+5-18
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,19 @@
33
<head>
44
<meta charset="utf-8">
55
<base target="_top">
6+
<meta name="shopify-api-key" content="{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}"/>
7+
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
68

79
<title>Redirecting...</title>
810

9-
<script src="{{config('shopify-app.appbridge_cdn_url') ?? 'https://unpkg.com'}}/@shopify/app-bridge{!! $appBridgeVersion !!}"></script>
1011
<script type="text/javascript">
1112
document.addEventListener('DOMContentLoaded', function () {
12-
var redirectUrl = "{!! $authUrl !!}";
13-
var normalizedLink;
13+
let redirectUrl = "{!! $url !!}";
14+
1415
if (window.top === window.self) {
15-
// If the current window is the 'parent', change the URL by setting location.href
1616
window.top.location.href = redirectUrl;
1717
} else {
18-
// If the current window is the 'child', change the parent's URL with postMessage
19-
normalizedLink = document.createElement('a');
20-
normalizedLink.href = redirectUrl;
21-
22-
var AppBridge = window['app-bridge'];
23-
var createApp = AppBridge.default;
24-
var Redirect = AppBridge.actions.Redirect;
25-
var app = createApp({
26-
apiKey: "{{ $apiKey }}",
27-
host: "{{ $host }}",
28-
});
29-
30-
var redirect = Redirect.create(app);
31-
redirect.dispatch(Redirect.Action.REMOTE, normalizedLink.href);
18+
open(redirectUrl, '_top');
3219
}
3320
});
3421
</script>

src/resources/views/auth/token.blade.php

+25-7
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,31 @@
3434

3535
@section('scripts')
3636
@parent
37-
38-
@if(config('shopify-app.appbridge_enabled'))
3937
<script>
40-
const host = new URLSearchParams(location.search).get("host")
41-
utils.getSessionToken(app).then((token) => {
42-
window.location.href = `{!! $target !!}{!! Str::contains($target, '?') ? '&' : '?' !!}token=${token}{{ Str::contains($target, 'host')? '' : '&host=${host}'}}`;
43-
});
38+
// If no host is found, we need to throw an error
39+
const host = new URLSearchParams(location.search).get("host");
40+
if (!host) {
41+
throw new Error('No host found in the URL');
42+
}
43+
44+
// If shopify is not defined, then we are not in a Shopify context redirect to the homepage as it
45+
if (typeof shopify === 'undefined') {
46+
open("{{ route('home') }}", "_self");
47+
}
48+
49+
shopify.idToken().then((token) => {
50+
51+
let url = new URL(`{!! $target !!}`, window.location.origin);
52+
// Enforce HTTPS if the current page is using HTTPS
53+
if (window.location.protocol === 'https:') {
54+
url.protocol = 'https:';
55+
}
56+
57+
url.searchParams.set('token', token);
58+
url.searchParams.set('host', host);
59+
60+
open(url.toString(), "_self");
61+
history.pushState(null, '', url.toString());
62+
});
4463
</script>
45-
@endif
4664
@endsection

src/resources/views/billing/fullpage_redirect.blade.php

+10-13
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,21 @@
33
<head>
44
<meta charset="utf-8">
55
<base target="_top">
6+
<meta name="shopify-api-key" content="{{ config('shopify-app.api_key') }}" />
7+
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
68

79
<title>Redirecting...</title>
8-
<script src="{{config('shopify-app.appbridge_cdn_url') ?? 'https://unpkg.com'}}/@shopify/app-bridge{{ \Osiset\ShopifyApp\Util::getShopifyConfig('appbridge_version') ? '@'.config('shopify-app.appbridge_version') : '' }}"></script>
10+
911
<script type="text/javascript">
10-
const redirectUrl = "{!! $url !!}";
12+
document.addEventListener('DOMContentLoaded', function () {
13+
let redirectUrl = "{!! $url !!}";
1114
12-
const AppBridge = window['app-bridge'];
13-
const createApp = AppBridge.default;
14-
const Redirect = AppBridge.actions.Redirect;
15-
const app = createApp({
16-
apiKey: "{{ $apiKey }}",
17-
host: "{{ $host }}",
15+
if (window.top === window.self) {
16+
window.top.location.href = redirectUrl;
17+
} else {
18+
open(redirectUrl, '_top');
19+
}
1820
});
19-
20-
console.log( 'app', app );
21-
22-
const redirect = Redirect.create(app);
23-
redirect.dispatch(Redirect.Action.REMOTE, redirectUrl);
2421
</script>
2522
</head>
2623
<body>

src/resources/views/home/index.blade.php

+4-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
@endsection
66

77
@section('content')
8+
<ui-title-bar title="Welcome"></ui-title-bar>
9+
810
<div class="flex-center position-ref full-height">
911
<div class="content">
1012
<div class="title m-b-md">
@@ -17,20 +19,10 @@
1719
<p>&nbsp;</p>
1820

1921
<div class="links">
20-
<a href="https://github.com/osiset/laravel-shopify" target="_blank">Package</a>
22+
<a href="https://github.com/Kyon147/laravel-shopify" target="_blank">Package</a>
2123
<a href="https://laravel.com" target="_blank">Laravel</a>
22-
<a href="https://github.com/osiset/laravel-shopify" target="_blank">GitHub</a>
24+
<a href="https://github.com/Kyon147/laravel-shopify" target="_blank">GitHub</a>
2325
</div>
2426
</div>
2527
</div>
2628
@endsection
27-
28-
@section('scripts')
29-
@parent
30-
31-
@if(config('shopify-app.appbridge_enabled'))
32-
<script>
33-
actions.TitleBar.create(app, { title: 'Welcome' });
34-
</script>
35-
@endif
36-
@endsection

src/resources/views/layouts/default.blade.php

+4-21
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
<head>
44
<meta charset="utf-8">
55
<meta name="csrf-token" content="{{ csrf_token() }}">
6+
<meta name="shopify-api-key" content="{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}"/>
7+
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
68

7-
<title>{{ \Osiset\ShopifyApp\Util::getShopifyConfig('app_name') }}</title>
9+
<title>{{ config('shopify-app.app_name') }}</title>
810
@yield('styles')
911
</head>
1012

@@ -17,28 +19,9 @@
1719
</div>
1820
</div>
1921

20-
@if(\Osiset\ShopifyApp\Util::getShopifyConfig('appbridge_enabled') && \Osiset\ShopifyApp\Util::useNativeAppBridge())
21-
<script src="{{config('shopify-app.appbridge_cdn_url') ?? 'https://unpkg.com'}}/@shopify/app-bridge{{ \Osiset\ShopifyApp\Util::getShopifyConfig('appbridge_version') ? '@'.config('shopify-app.appbridge_version') : '' }}"></script>
22-
<script
23-
@if(\Osiset\ShopifyApp\Util::getShopifyConfig('turbo_enabled'))
24-
data-turbolinks-eval="false"
25-
@endif
26-
>
27-
var AppBridge = window['app-bridge'];
28-
var actions = AppBridge.actions;
29-
var utils = AppBridge.utilities;
30-
var createApp = AppBridge.default;
31-
var app = createApp({
32-
apiKey: "{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}",
33-
host: "{{ \Request::get('host') }}",
34-
forceRedirect: true,
35-
});
36-
</script>
37-
22+
@if(\Osiset\ShopifyApp\Util::useNativeAppBridge())
3823
@include('shopify-app::partials.token_handler')
39-
@include('shopify-app::partials.flash_messages')
4024
@endif
41-
4225
@yield('scripts')
4326
</body>
4427
</html>

src/resources/views/partials/flash_messages.blade.php

-22
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
<script data-turbolinks-eval="false">
2-
var SESSION_TOKEN_REFRESH_INTERVAL = {{ \Osiset\ShopifyApp\Util::getShopifyConfig('session_token_refresh_interval') }};
3-
var LOAD_EVENT = '{{ \Osiset\ShopifyApp\Util::getShopifyConfig('turbo_enabled') ? 'turbolinks:load' : 'DOMContentLoaded' }}';
1+
<script>
2+
var SESSION_TOKEN_REFRESH_INTERVAL = {{ config('shopify-app.session_token_refresh_interval') }};
3+
var LOAD_EVENT = 'DOMContentLoaded'
44
5-
// Token updates
65
document.addEventListener(LOAD_EVENT, () => {
7-
retrieveToken(app);
8-
keepRetrievingToken(app);
6+
retrieveToken();
7+
keepRetrievingToken();
98
});
109
11-
// Retrieve session token
12-
async function retrieveToken(app) {
13-
window.sessionToken = await utils.getSessionToken(app);
10+
async function retrieveToken() {
11+
window.sessionToken = await shopify.idToken();
1412
15-
// Update everything with the session-token class
1613
Array.from(document.getElementsByClassName('session-token')).forEach((el) => {
1714
if (el.hasAttribute('value')) {
1815
el.value = window.sessionToken;
@@ -23,8 +20,8 @@
2320
});
2421
2522
const bearer = `Bearer ${window.sessionToken}`;
23+
2624
if (window.jQuery) {
27-
// jQuery
2825
if (window.jQuery.ajaxSettings.headers) {
2926
window.jQuery.ajaxSettings.headers['Authorization'] = bearer;
3027
} else {
@@ -41,20 +38,13 @@
4138
}
4239
4340
if (window.axios) {
44-
// Axios
4541
window.axios.defaults.headers.common['Authorization'] = bearer;
4642
}
4743
}
4844
49-
// Keep retrieving a session token periodically
50-
function keepRetrievingToken(app) {
45+
function keepRetrievingToken() {
5146
setInterval(() => {
52-
retrieveToken(app);
47+
retrieveToken();
5348
}, SESSION_TOKEN_REFRESH_INTERVAL);
5449
}
55-
56-
document.addEventListener('turbolinks:request-start', (event) => {
57-
var xhr = event.data.xhr;
58-
xhr.setRequestHeader('Authorization', `Bearer ${window.sessionToken}`);
59-
});
6050
</script>

tests/Traits/AuthControllerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function testAuthRedirectsToShopifyWhenNoCode(): void
3030
// Check the redirect happens and location is set properly in the header.
3131
$response->assertViewHas('shopDomain', 'example.myshopify.com');
3232
$response->assertViewHas(
33-
'authUrl',
33+
'url',
3434
'https://example.myshopify.com/admin/oauth/authorize?client_id='.Util::getShopifyConfig('api_key').'&scope=read_products%2Cwrite_products%2Cread_themes&redirect_uri=https%3A%2F%2Flocalhost%2Fauthenticate'
3535
);
3636

tests/Traits/BillingControllerTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public function testSendsShopToBillingScreen(): void
4545

4646
// Run the call
4747
$response = $this->call('get', '/billing', ['shop' => $shop->getDomain()->toNative()]);
48+
4849
$response->assertViewHas(
4950
'url',
5051
'https://example.myshopify.com/admin/charges/1029266947/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f'

tests/Traits/HomeControllerTest.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ public function testHomeRoute(): void
2727
$host = base64_encode($shop->getDomain()->toNative().'/admin');
2828
$this->call('get', '/', ['token' => $this->buildToken(), 'host' => $host])
2929
->assertOk()
30-
->assertSee('apiKey: "'.Util::getShopifyConfig('api_key').'"', false)
31-
->assertSee("host: \"{$host}\"", false);
30+
->assertSee('name="shopify-api-key" content="'.Util::getShopifyConfig('api_key').'"', false)
31+
->assertSee('https://cdn.shopify.com/shopifycloud/app-bridge.js');
3232
}
3333

3434
public function testHomeRouteHostAdmin(): void
3535
{
36-
$shop = factory($this->model)->create(['name' => 'shop-name.myshopify.com']);
36+
factory($this->model)->create(['name' => 'shop-name.myshopify.com']);
3737

3838
$host = base64_encode('admin.shopify.com/store/shop-name');
3939
$this->call('get', '/', ['token' => $this->buildToken(), 'host' => $host])
4040
->assertOk()
41-
->assertSee('apiKey: "'.Util::getShopifyConfig('api_key').'"', false)
42-
->assertSee("host: \"{$host}\"", false);
41+
->assertSee('name="shopify-api-key" content="'.Util::getShopifyConfig('api_key').'"', false)
42+
->assertSee('https://cdn.shopify.com/shopifycloud/app-bridge.js');
4343
}
4444
}

0 commit comments

Comments
 (0)