Skip to content

Commit 2600b96

Browse files
authored
Merge pull request #63 from Shopify/sfr-token
Add support for SFR Identity authentication
2 parents 42fd178 + 6714ae4 commit 2600b96

13 files changed

+164
-57
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Change Log
22

33

4+
## v1.1.0 (Oct 2, 2020)
5+
* [#63](https://github.com/Shopify/shopify-theme-inspector/pull/63) Add storefront-renderer support
6+
47
## v1.0.7 (June 4, 2020)
58
* [#54](https://github.com/Shopify/shopify-theme-inspector/pull/54) Add shopify employee scope
69

src/background.ts

+43-28
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import {env} from './env';
1+
import {env, RenderBackend} from './env';
22
import {isDev, Oauth2} from './utils';
33

4-
const DEVTOOLS_SCOPE = 'https://api.shopify.com/auth/shop.storefront.devtools';
54
const COLLABORATORS_SCOPE =
65
'https://api.shopify.com/auth/partners.collaborator-relationships.readonly';
76
let shopifyEmployee = false;
7+
let renderBackend = RenderBackend.StorefrontRenderer;
88

99
function getOauth2Client(origin: string) {
1010
const identityDomain = isDev(origin)
@@ -13,19 +13,18 @@ function getOauth2Client(origin: string) {
1313
const clientId = isDev(origin)
1414
? env.DEV_OAUTH2_CLIENT_ID
1515
: env.OAUTH2_CLIENT_ID;
16-
const subjectId = isDev(origin)
17-
? env.DEV_OAUTH2_SUBJECT_ID
18-
: env.OAUTH2_SUBJECT_ID;
1916
const clientAuthParams = [
2017
[
2118
'scope',
2219
`openid profile ${
2320
shopifyEmployee === true ? 'employee' : ''
24-
} ${DEVTOOLS_SCOPE} ${COLLABORATORS_SCOPE}`,
21+
} ${Object.values(env.DEVTOOLS_SCOPE).join(' ')} ${COLLABORATORS_SCOPE}`,
2522
],
2623
];
2724

28-
return new Oauth2(clientId, subjectId, identityDomain, {clientAuthParams});
25+
return new Oauth2(clientId, identityDomain, {
26+
clientAuthParams,
27+
});
2928
}
3029

3130
// Change icon from colored to greyscale depending on whether or not Shopify has
@@ -48,12 +47,17 @@ function setIconAndPopup(active: string, tabId: number) {
4847
chrome.pageAction.show(tabId);
4948
}
5049

50+
function getSubjectId(oauth: Oauth2, origin: string) {
51+
if (isDev(origin)) {
52+
return oauth.fetchClientId(env.DEV_OAUTH2_SUBJECT_NAME[renderBackend]);
53+
}
54+
return Promise.resolve(env.OAUTH2_SUBJECT_ID[renderBackend]);
55+
}
56+
5157
chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
5258
if (type !== 'signOut') return false;
5359

54-
const oauth2 = getOauth2Client(origin);
55-
56-
oauth2
60+
getOauth2Client(origin)
5761
.logoutUser()
5862
.then(() => {
5963
sendResponse();
@@ -85,6 +89,9 @@ chrome.runtime.onMessage.addListener((event, sender) => {
8589
chrome.runtime.onMessage.addListener((event, sender) => {
8690
if (sender.tab && sender.tab.id && event.type === 'detect-shopify') {
8791
setIconAndPopup(event.hasDetectedShopify, sender.tab.id);
92+
renderBackend = event.isCore
93+
? RenderBackend.Core
94+
: RenderBackend.StorefrontRenderer;
8895
}
8996
});
9097

@@ -95,9 +102,7 @@ chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
95102
return false;
96103
}
97104

98-
const oauth2 = getOauth2Client(origin);
99-
100-
oauth2
105+
getOauth2Client(origin)
101106
.authenticate()
102107
.then(() => {
103108
sendResponse({success: true});
@@ -111,26 +116,31 @@ chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
111116
});
112117

113118
// Listen for 'request-core-access-token' event and respond to the messenger
114-
// with a valid Shopify Core access token. This may trigger a login popup window
115-
// if needed.
119+
// with a valid access token. This may trigger a login popup window if needed.
116120
chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
117121
if (type !== 'request-core-access-token') {
118122
return false;
119123
}
120124

121-
const oauth2 = getOauth2Client(origin);
122125
const params = [
123126
[
124127
'scope',
125-
`${
126-
shopifyEmployee === true ? 'employee' : ''
127-
} ${DEVTOOLS_SCOPE} ${COLLABORATORS_SCOPE}`,
128+
`${shopifyEmployee === true ? 'employee' : ''} ${
129+
env.DEVTOOLS_SCOPE[renderBackend]
130+
} ${COLLABORATORS_SCOPE}`,
128131
],
129132
];
130-
const destination = `${origin}/admin`;
131133

132-
oauth2
133-
.getSubjectAccessToken(destination, params)
134+
// SFR does not need a destination.
135+
const destination =
136+
renderBackend === RenderBackend.Core ? `${origin}/admin` : '';
137+
138+
const oauth = getOauth2Client(origin);
139+
140+
getSubjectId(oauth, origin)
141+
.then(subjectId => {
142+
return oauth.getSubjectAccessToken(destination, subjectId, params);
143+
})
134144
.then(token => {
135145
sendResponse({token});
136146
})
@@ -146,9 +156,7 @@ chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
146156
chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
147157
if (type !== 'request-user-name') return false;
148158

149-
const oauth2 = getOauth2Client(origin);
150-
151-
oauth2
159+
getOauth2Client(origin)
152160
.getUserInfo()
153161
.then(userInfo => {
154162
const name = userInfo.given_name;
@@ -166,9 +174,7 @@ chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
166174
chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
167175
if (type !== 'request-auth-status') return false;
168176

169-
const oauth2 = getOauth2Client(origin);
170-
171-
oauth2
177+
getOauth2Client(origin)
172178
.hasValidClientToken()
173179
.then(isLoggedIn => {
174180
sendResponse({isLoggedIn});
@@ -179,3 +185,12 @@ chrome.runtime.onMessage.addListener(({type, origin}, _, sendResponse) => {
179185

180186
return true;
181187
});
188+
189+
chrome.runtime.onMessage.addListener(({type}, _, sendResponse) => {
190+
if (type !== 'request-rendering-backend') return false;
191+
192+
const name = renderBackend.toString();
193+
sendResponse({name});
194+
195+
return true;
196+
});

src/detectShopify.ts

+27-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
1-
// Use regex on document to test for a shopify site
2-
// Look for a DOM script element that contains
3-
// "Shopify.shop ="
4-
// This is auto-generated from content_for_header
5-
const findShopifyScript = Array.from(
6-
document.querySelectorAll('script'),
7-
).filter(script => {
8-
return /Shopify\.shop =/.test(script.textContent || '');
9-
});
1+
// Use regex on document to test for a Shopify site
2+
// Look for a DOM script element that contains `Shopify.shop =`.
3+
// And look for `BOOMR.application = "core"`, only present on core to detect the
4+
// back-end that rendered this page.
5+
// Both are generated by {{content_for_header}}.
6+
let hasDetectedShopify = false;
7+
let isCore = false;
108

11-
if (findShopifyScript.length) {
12-
chrome.runtime.sendMessage({
13-
type: 'detect-shopify',
14-
hasDetectedShopify: true,
15-
});
9+
const scripts = document.querySelectorAll('script');
10+
for (let i = 0; i < scripts.length; i++) {
11+
// Short-circuit if we have found everything we need
12+
if (isCore && hasDetectedShopify) break;
13+
14+
const content = scripts[i].textContent;
15+
if (typeof content === 'string') {
16+
if (/Shopify\.shop\s*=/.test(content)) {
17+
hasDetectedShopify = true;
18+
}
19+
if (/BOOMR\.application\s*=\s*"core"/.test(content)) {
20+
isCore = true;
21+
}
22+
}
1623
}
1724

25+
chrome.runtime.sendMessage({
26+
type: 'detect-shopify',
27+
hasDetectedShopify,
28+
isCore,
29+
});
30+
1831
if (document.location.search.includes('shopify_employee')) {
1932
chrome.runtime.sendMessage({
2033
type: 'detect-shopify-employee',

src/devtools.html

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ <h2>This page cannot be profiled</h2>
4646
<div data-flamegraph-wrapper class="hide">
4747
<div data-flamegraph-container class="chart"></div>
4848
<p data-total-time class="total-time">Total time to render liquid: <b>-</b></p>
49+
<p data-rendering-backend class="rendering-backend">Rendering back-end: <b>-</b></p>
4950

5051
<div data-detailed-info class="detailed-info">
5152
<p data-partial>File: <b>-</b></p>

src/devtools.ts

+18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import LiquidFlamegraph from './components/liquid-flamegraph';
44
import {
55
getProfileData,
66
setTotalTime,
7+
setRenderingBackend,
78
getBrowserTheme,
89
emptyHTMLNode,
910
} from './utils';
@@ -90,6 +91,20 @@ function getInspectedWindowURL(): Promise<URL> {
9091
});
9192
}
9293

94+
function getRenderingBackend(): Promise<string> {
95+
return new Promise((resolve, reject) => {
96+
chrome.runtime.sendMessage(
97+
{type: 'request-rendering-backend'},
98+
({name, error}) => {
99+
if (error) {
100+
return reject(error);
101+
}
102+
return resolve(name);
103+
},
104+
);
105+
});
106+
}
107+
93108
async function refreshPanel() {
94109
emptyHTMLNode(document.querySelector(selectors.initialMessage));
95110
document
@@ -117,10 +132,13 @@ async function refreshPanel() {
117132
url,
118133
);
119134

135+
const renderingBackend = await getRenderingBackend();
136+
120137
// All events happening here are synchronous. The set timeout is for UI
121138
// purposes so that timing information gets displayed after the flamegraph is shown.
122139
setTimeout(function() {
123140
setTotalTime(profile.value);
141+
setRenderingBackend(renderingBackend);
124142
}, 300);
125143

126144
document

src/env.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1+
export enum RenderBackend {
2+
Core = 'core',
3+
StorefrontRenderer = 'storefront-renderer',
4+
}
5+
16
export const env = {
27
OAUTH2_DOMAIN: 'accounts.shopify.com',
38
DEV_OAUTH2_DOMAIN: 'identity.myshopify.io',
49
OAUTH2_CLIENT_ID: 'ff2a91a2-6854-449e-a37d-c03bcd181126',
510
DEV_OAUTH2_CLIENT_ID: '1d7f695c-42e2-493a-a6dc-be12d4117d58',
6-
OAUTH2_SUBJECT_ID:
7-
'7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c',
8-
DEV_OAUTH2_SUBJECT_ID:
9-
'e92482cebb9bfb9fb5a0199cc770fde3de6c8d16b798ee73e36c9d815e070e52',
11+
OAUTH2_SUBJECT_ID: {
12+
[RenderBackend.Core]:
13+
'7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c',
14+
[RenderBackend.StorefrontRenderer]: 'ee139b3d-5861-4d45-b387-1bc3ada7811c',
15+
},
16+
DEV_OAUTH2_SUBJECT_NAME: {
17+
[RenderBackend.Core]: 'shopify-development',
18+
[RenderBackend.StorefrontRenderer]: 'storefront-renderer-development',
19+
},
20+
DEVTOOLS_SCOPE: {
21+
[RenderBackend.Core]:
22+
'https://api.shopify.com/auth/shop.storefront.devtools',
23+
[RenderBackend.StorefrontRenderer]:
24+
'https://api.shopify.com/auth/shop.storefront-renderer.devtools',
25+
},
1026
OAUTH_LOCAL_STORAGE_KEY: 'shopifyDevToolsAuthResults',
1127
};

src/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Shopify Theme Inspector for Chrome",
3-
"version": "1.0.7",
3+
"version": "1.1.0",
44
"description": "Profile and debug Liquid template on your Shopify store",
55
"devtools_page": "devtools.html",
66
"permissions": ["storage", "identity", "activeTab"],

src/styles/devtools.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ body {
165165
margin: 20px;
166166
}
167167

168-
.total-time {
168+
.total-time, .rendering-backend {
169169
margin: 20px;
170170
font-size: 13px;
171171
}

src/utils/domHelpers.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ export function setTotalTime(totalTime: number) {
22
updateInfoText('[data-total-time]', `${Math.trunc(totalTime * 1000)}ms`);
33
}
44

5+
export function setRenderingBackend(backend: string) {
6+
updateInfoText('[data-rendering-backend]', backend);
7+
}
8+
59
export function formatNodeTime(nodeTime: number) {
610
const nodeTimeMs = Math.trunc(nodeTime * 1000);
711
if (nodeTimeMs > 0) {

src/utils/helpers.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const IS_CHROME = navigator.userAgent.indexOf('Firefox') < 0;
22

33
export function isDev(origin: string): boolean {
4-
return origin.includes('shop1.myshopify');
4+
return (
5+
origin.includes('shop1.myshopify') ||
6+
origin.includes('shop1-fast.myshopify')
7+
);
58
}
69

710
export function getThemeId() {

src/utils/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export {getProfileData} from './getProfileData';
22
export {isDev, getThemeId, getBrowserTheme} from './helpers';
33
export {
44
setTotalTime,
5+
setRenderingBackend,
56
formatNodeTime,
67
emptyHTMLNode,
78
updateInfoText,

0 commit comments

Comments
 (0)