Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace legacy app bridge with app bridge cdn #361

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions src/Http/Middleware/VerifyShopify.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,20 +377,9 @@ protected function getHmacFromRequest(Request $request): array
*/
protected function getAccessTokenFromRequest(Request $request): ?string
{
if (Util::getShopifyConfig('turbo_enabled')) {
if ($request->bearerToken()) {
// Bearer tokens collect.
// Turbo does not refresh the page, values are attached to the same header.
$bearerTokens = Collection::make(explode(',', $request->header('Authorization', '')));
$newestToken = Str::substr(trim($bearerTokens->last()), 7);

return $newestToken;
}

return $request->get('token');
}

return $this->isApiRequest($request) ? $request->bearerToken() : $request->get('token');
return $this->isApiRequest($request)
? $request->bearerToken()
: $request->get('token');
}

/**
Expand Down
3 changes: 1 addition & 2 deletions src/Traits/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ public function authenticate(Request $request, AuthenticateShop $authShop)
'shopify-app::auth.fullpage_redirect',
[
'apiKey' => Util::getShopifyConfig('api_key', $shopOrigin),
'appBridgeVersion' => Util::getShopifyConfig('appbridge_version') ? '@'.config('shopify-app.appbridge_version') : '',
'authUrl' => $result['url'],
'url' => $result['url'],
'host' => $request->get('host'),
'shopDomain' => $shopDomain,
'locale' => $request->get('locale'),
Expand Down
33 changes: 0 additions & 33 deletions src/resources/config/shopify-app.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
*/

'domain' => env('SHOPIFY_DOMAIN'),

/*
|--------------------------------------------------------------------------
| Manual routes
Expand Down Expand Up @@ -138,26 +137,6 @@

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

/*
|--------------------------------------------------------------------------
| AppBridge Mode
|--------------------------------------------------------------------------
|
| AppBridge (embedded apps) are enabled by default. Set to false to use legacy
| mode and host the app inside your own container.
|
*/

'appbridge_enabled' => (bool) env('SHOPIFY_APPBRIDGE_ENABLED', true),

// Use semver range to link to a major or minor version number.
// Leaving empty will use the latest version - not recommended in production.
'appbridge_version' => env('SHOPIFY_APPBRIDGE_VERSION', 'latest'),

// Set a new CDN URL if you want to host the AppBridge JS yourself or unpkg goes down.
// DO NOT include a trailing slash.
'appbridge_cdn_url' => env('SHOPIFY_APPBRIDGE_CDN_URL', 'https://unpkg.com'),

/*
|--------------------------------------------------------------------------
| Shopify App Name
Expand Down Expand Up @@ -513,18 +492,6 @@

'config_api_callback' => null,

/*
|--------------------------------------------------------------------------
| Enable Turbolinks or Hotwire Turbo
|--------------------------------------------------------------------------
|
| If you use Turbolinks/Turbo and Livewire, turn on this setting to get
| the token assigned automatically.
|
*/

'turbo_enabled' => (bool) env('SHOPIFY_TURBO_ENABLED', false),

/*
|--------------------------------------------------------------------------
| Customize Models and Table Name
Expand Down
23 changes: 5 additions & 18 deletions src/resources/views/auth/fullpage_redirect.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,19 @@
<head>
<meta charset="utf-8">
<base target="_top">
<meta name="shopify-api-key" content="{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}"/>
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>

<title>Redirecting...</title>

<script src="{{config('shopify-app.appbridge_cdn_url') ?? 'https://unpkg.com'}}/@shopify/app-bridge{!! $appBridgeVersion !!}"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
var redirectUrl = "{!! $authUrl !!}";
var normalizedLink;
let redirectUrl = "{!! $url !!}";

if (window.top === window.self) {
// If the current window is the 'parent', change the URL by setting location.href
window.top.location.href = redirectUrl;
} else {
// If the current window is the 'child', change the parent's URL with postMessage
normalizedLink = document.createElement('a');
normalizedLink.href = redirectUrl;

var AppBridge = window['app-bridge'];
var createApp = AppBridge.default;
var Redirect = AppBridge.actions.Redirect;
var app = createApp({
apiKey: "{{ $apiKey }}",
host: "{{ $host }}",
});

var redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.REMOTE, normalizedLink.href);
open(redirectUrl, '_top');
}
});
</script>
Expand Down
32 changes: 25 additions & 7 deletions src/resources/views/auth/token.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,31 @@

@section('scripts')
@parent

@if(config('shopify-app.appbridge_enabled'))
<script>
const host = new URLSearchParams(location.search).get("host")
utils.getSessionToken(app).then((token) => {
window.location.href = `{!! $target !!}{!! Str::contains($target, '?') ? '&' : '?' !!}token=${token}{{ Str::contains($target, 'host')? '' : '&host=${host}'}}`;
});
// If no host is found, we need to throw an error
const host = new URLSearchParams(location.search).get("host");
if (!host) {
throw new Error('No host found in the URL');
}

// If shopify is not defined, then we are not in a Shopify context redirect to the homepage as it
if (typeof shopify === 'undefined') {
open("{{ route('home') }}", "_self");
}

shopify.idToken().then((token) => {

let url = new URL(`{!! $target !!}`, window.location.origin);
// Enforce HTTPS if the current page is using HTTPS
if (window.location.protocol === 'https:') {
url.protocol = 'https:';
}

url.searchParams.set('token', token);
url.searchParams.set('host', host);

open(url.toString(), "_self");
history.pushState(null, '', url.toString());
});
</script>
@endif
@endsection
23 changes: 10 additions & 13 deletions src/resources/views/billing/fullpage_redirect.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,21 @@
<head>
<meta charset="utf-8">
<base target="_top">
<meta name="shopify-api-key" content="{{ config('shopify-app.api_key') }}" />
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>

<title>Redirecting...</title>
<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>

<script type="text/javascript">
const redirectUrl = "{!! $url !!}";
document.addEventListener('DOMContentLoaded', function () {
let redirectUrl = "{!! $url !!}";

const AppBridge = window['app-bridge'];
const createApp = AppBridge.default;
const Redirect = AppBridge.actions.Redirect;
const app = createApp({
apiKey: "{{ $apiKey }}",
host: "{{ $host }}",
if (window.top === window.self) {
window.top.location.href = redirectUrl;
} else {
open(redirectUrl, '_top');
}
});

console.log( 'app', app );

const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.REMOTE, redirectUrl);
</script>
</head>
<body>
Expand Down
16 changes: 4 additions & 12 deletions src/resources/views/home/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
@endsection

@section('content')
<ui-title-bar title="Welcome"></ui-title-bar>

<div class="flex-center position-ref full-height">
<div class="content">
<div class="title m-b-md">
Expand All @@ -17,20 +19,10 @@
<p>&nbsp;</p>

<div class="links">
<a href="https://github.com/osiset/laravel-shopify" target="_blank">Package</a>
<a href="https://github.com/Kyon147/laravel-shopify" target="_blank">Package</a>
<a href="https://laravel.com" target="_blank">Laravel</a>
<a href="https://github.com/osiset/laravel-shopify" target="_blank">GitHub</a>
<a href="https://github.com/Kyon147/laravel-shopify" target="_blank">GitHub</a>
</div>
</div>
</div>
@endsection

@section('scripts')
@parent

@if(config('shopify-app.appbridge_enabled'))
<script>
actions.TitleBar.create(app, { title: 'Welcome' });
</script>
@endif
@endsection
25 changes: 4 additions & 21 deletions src/resources/views/layouts/default.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<head>
<meta charset="utf-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="shopify-api-key" content="{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}"/>
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>

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

Expand All @@ -17,28 +19,9 @@
</div>
</div>

@if(\Osiset\ShopifyApp\Util::getShopifyConfig('appbridge_enabled') && \Osiset\ShopifyApp\Util::useNativeAppBridge())
<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>
<script
@if(\Osiset\ShopifyApp\Util::getShopifyConfig('turbo_enabled'))
data-turbolinks-eval="false"
@endif
>
var AppBridge = window['app-bridge'];
var actions = AppBridge.actions;
var utils = AppBridge.utilities;
var createApp = AppBridge.default;
var app = createApp({
apiKey: "{{ \Osiset\ShopifyApp\Util::getShopifyConfig('api_key', $shopDomain ?? Auth::user()->name ) }}",
host: "{{ \Request::get('host') }}",
forceRedirect: true,
});
</script>

@if(\Osiset\ShopifyApp\Util::useNativeAppBridge())
@include('shopify-app::partials.token_handler')
@include('shopify-app::partials.flash_messages')
@endif

@yield('scripts')
</body>
</html>
22 changes: 0 additions & 22 deletions src/resources/views/partials/flash_messages.blade.php

This file was deleted.

30 changes: 10 additions & 20 deletions src/resources/views/partials/token_handler.blade.php
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
<script data-turbolinks-eval="false">
var SESSION_TOKEN_REFRESH_INTERVAL = {{ \Osiset\ShopifyApp\Util::getShopifyConfig('session_token_refresh_interval') }};
var LOAD_EVENT = '{{ \Osiset\ShopifyApp\Util::getShopifyConfig('turbo_enabled') ? 'turbolinks:load' : 'DOMContentLoaded' }}';
<script>
var SESSION_TOKEN_REFRESH_INTERVAL = {{ config('shopify-app.session_token_refresh_interval') }};
var LOAD_EVENT = 'DOMContentLoaded'

// Token updates
document.addEventListener(LOAD_EVENT, () => {
retrieveToken(app);
keepRetrievingToken(app);
retrieveToken();
keepRetrievingToken();
});

// Retrieve session token
async function retrieveToken(app) {
window.sessionToken = await utils.getSessionToken(app);
async function retrieveToken() {
window.sessionToken = await shopify.idToken();

// Update everything with the session-token class
Array.from(document.getElementsByClassName('session-token')).forEach((el) => {
if (el.hasAttribute('value')) {
el.value = window.sessionToken;
Expand All @@ -23,8 +20,8 @@
});

const bearer = `Bearer ${window.sessionToken}`;

if (window.jQuery) {
// jQuery
if (window.jQuery.ajaxSettings.headers) {
window.jQuery.ajaxSettings.headers['Authorization'] = bearer;
} else {
Expand All @@ -41,20 +38,13 @@
}

if (window.axios) {
// Axios
window.axios.defaults.headers.common['Authorization'] = bearer;
}
}

// Keep retrieving a session token periodically
function keepRetrievingToken(app) {
function keepRetrievingToken() {
setInterval(() => {
retrieveToken(app);
retrieveToken();
}, SESSION_TOKEN_REFRESH_INTERVAL);
}

document.addEventListener('turbolinks:request-start', (event) => {
var xhr = event.data.xhr;
xhr.setRequestHeader('Authorization', `Bearer ${window.sessionToken}`);
});
</script>
2 changes: 1 addition & 1 deletion tests/Traits/AuthControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function testAuthRedirectsToShopifyWhenNoCode(): void
// Check the redirect happens and location is set properly in the header.
$response->assertViewHas('shopDomain', 'example.myshopify.com');
$response->assertViewHas(
'authUrl',
'url',
'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'
);

Expand Down
1 change: 1 addition & 0 deletions tests/Traits/BillingControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function testSendsShopToBillingScreen(): void

// Run the call
$response = $this->call('get', '/billing', ['shop' => $shop->getDomain()->toNative()]);

$response->assertViewHas(
'url',
'https://example.myshopify.com/admin/charges/1029266947/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f'
Expand Down
10 changes: 5 additions & 5 deletions tests/Traits/HomeControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ public function testHomeRoute(): void
$host = base64_encode($shop->getDomain()->toNative().'/admin');
$this->call('get', '/', ['token' => $this->buildToken(), 'host' => $host])
->assertOk()
->assertSee('apiKey: "'.Util::getShopifyConfig('api_key').'"', false)
->assertSee("host: \"{$host}\"", false);
->assertSee('name="shopify-api-key" content="'.Util::getShopifyConfig('api_key').'"', false)
->assertSee('https://cdn.shopify.com/shopifycloud/app-bridge.js');
}

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

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