Skip to content

Commit 39f33ca

Browse files
committed
refactor
1 parent 4c4d968 commit 39f33ca

File tree

5 files changed

+44
-81
lines changed

5 files changed

+44
-81
lines changed

packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import {ApiGatewayV1Adapter} from '@h4ad/serverless-adapter/lib/adapters/aws'
6464
import {ExpressFramework} from '@h4ad/serverless-adapter/lib/frameworks/express'
6565
import {is as typeis} from 'type-is'
6666
import {getConfig} from '../../utils/ssr-config'
67-
import {applyHttpOnlySessionCookies} from './process-token-response'
67+
import {setHttpOnlySessionCookies} from './process-token-response'
6868

6969
/**
7070
* An Array of mime-types (Content-Type values) that are considered
@@ -100,7 +100,7 @@ export const isBinary = (headers) => {
100100
* sets the Authorization header, and appends refresh_token to the query string.
101101
* @private
102102
*/
103-
export const injectLogoutTokens = (proxyRequest, incomingRequest) => {
103+
export const setTokensInLogoutRequest = (proxyRequest, incomingRequest) => {
104104
const cookieHeader = incomingRequest.headers.cookie
105105
if (!cookieHeader) return
106106

@@ -111,29 +111,26 @@ export const injectLogoutTokens = (proxyRequest, incomingRequest) => {
111111
'x-site-id header is missing on SLAS logout request. ' +
112112
'Token injection skipped. ' +
113113
'Ensure the x-site-id header is set in CommerceApiProvider headers.',
114-
{namespace: 'injectLogoutTokens'}
114+
{namespace: 'setTokensInLogoutRequest'}
115115
)
116116
return
117117
}
118118

119-
const site = siteId.trim()
120-
121119
// Inject Bearer token from access token cookie
122-
const accessToken = cookies[`cc-at_${site}`]
120+
const accessToken = cookies[`cc-at_${siteId}`]
123121
if (accessToken) {
124122
proxyRequest.setHeader('Authorization', `Bearer ${accessToken}`)
125123
}
126124

127125
// Inject refresh_token into query string from HttpOnly cookie
128-
const refreshToken = cookies[`cc-nx_${site}`]
126+
const refreshToken = cookies[`cc-nx_${siteId}`]
129127
if (refreshToken) {
130-
const url = new URL(proxyRequest.path, 'http://localhost')
131-
url.searchParams.set('refresh_token', refreshToken)
132-
proxyRequest.path = url.pathname + url.search
128+
const separator = proxyRequest.path.includes('?') ? '&' : '?'
129+
proxyRequest.path += `${separator}refresh_token=${encodeURIComponent(refreshToken)}`
133130
} else {
134131
logger.warn(
135-
`Refresh token cookie (cc-nx_${site}) not found for ${incomingRequest.path}. The logout request may fail.`,
136-
{namespace: 'injectLogoutTokens'}
132+
`Refresh token cookie (cc-nx_${siteId}) not found for ${incomingRequest.path}. The logout request may fail.`,
133+
{namespace: 'setTokensInLogoutRequest'}
137134
)
138135
}
139136
}
@@ -214,11 +211,6 @@ export const RemoteServerFactory = {
214211
// cookies applied when that feature is enabled. Users can override this in ssr.js.
215212
tokenResponseEndpoints: SLAS_TOKEN_RESPONSE_ENDPOINTS,
216213

217-
// A regex for matching the SLAS logout endpoint. When HttpOnly session cookies are
218-
// enabled, the proxy injects the Bearer token and refresh token from HttpOnly cookies
219-
// for this endpoint. Users can override this in ssr.js.
220-
slasLogoutEndpoint: SLAS_LOGOUT_ENDPOINT,
221-
222214
// Custom callback to modify the SLAS private client proxy request. This callback is invoked
223215
// after the built-in proxy request handling. Users can provide additional
224216
// request modifications (e.g., custom headers).
@@ -1011,9 +1003,9 @@ export const RemoteServerFactory = {
10111003
proxyRequest.setHeader('Authorization', `Basic ${encodedSlasCredentials}`)
10121004
} else if (
10131005
process.env.MRT_DISABLE_HTTPONLY_SESSION_COOKIES === 'false' &&
1014-
incomingRequest.path?.match(options.slasLogoutEndpoint)
1006+
incomingRequest.path?.match(SLAS_LOGOUT_ENDPOINT)
10151007
) {
1016-
injectLogoutTokens(proxyRequest, incomingRequest)
1008+
setTokensInLogoutRequest(proxyRequest, incomingRequest)
10171009
}
10181010

10191011
// Allow users to apply additional custom modifications to the proxy request
@@ -1045,7 +1037,7 @@ export const RemoteServerFactory = {
10451037
isTokenEndpoint
10461038
) {
10471039
try {
1048-
workingBuffer = applyHttpOnlySessionCookies(
1040+
workingBuffer = setHttpOnlySessionCookies(
10491041
workingBuffer,
10501042
proxyRes,
10511043
req,
@@ -1501,9 +1493,7 @@ export const RemoteServerFactory = {
15011493
* @param {RegExp} [options.tokenResponseEndpoints] - A regex pattern to match SLAS endpoints
15021494
* that return tokens in the response body. Used to determine which responses should have HttpOnly
15031495
* session cookies applied. Defaults to /\/oauth2\/(token|passwordless\/token)$/.
1504-
* @param {RegExp} [options.slasLogoutEndpoint] - A regex pattern to match the SLAS logout endpoint.
1505-
* When HttpOnly session cookies are enabled, the proxy injects the Bearer token and refresh token
1506-
* from HttpOnly cookies for this endpoint. Defaults to /\/oauth2\/logout/.
1496+
15071497
* @param {function} [options.onSLASPrivateProxyReq] - Custom callback to modify SLAS private client
15081498
* proxy requests. Called after built-in request handling. Signature: (proxyRequest, incomingRequest, res) => void.
15091499
* Use this to add custom headers or modify the proxy request.

packages/pwa-kit-runtime/src/ssr/server/process-token-response.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ function getTokenClaims(accessToken) {
6767
* strip token fields from body, and append our Set-Cookie headers (preserving upstream cookies).
6868
* @private
6969
*/
70-
export function applyHttpOnlySessionCookies(responseBuffer, proxyRes, req, res, options) {
70+
export function setHttpOnlySessionCookies(responseBuffer, proxyRes, req, res, options) {
7171
const siteId = req.headers?.['x-site-id']
72-
if (!siteId || typeof siteId !== 'string' || siteId.trim() === '') {
72+
if (!siteId) {
7373
throw new Error(
7474
'HttpOnly session cookies are enabled but siteId is missing. ' +
7575
'Ensure the x-site-id header is set on the request.'
@@ -83,7 +83,7 @@ export function applyHttpOnlySessionCookies(responseBuffer, proxyRes, req, res,
8383
return responseBuffer
8484
}
8585

86-
const site = siteId.trim()
86+
const site = siteId
8787

8888
// Decode JWT and extract claims
8989
let isGuest = true

packages/pwa-kit-runtime/src/ssr/server/process-token-response.test.js

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7-
import {getRefreshTokenCookieTTL, applyHttpOnlySessionCookies} from './process-token-response'
7+
import {getRefreshTokenCookieTTL, setHttpOnlySessionCookies} from './process-token-response'
88
import {parse as parseSetCookie} from 'set-cookie-parser'
99

1010
jest.mock('../../utils/logger-instance', () => ({
@@ -24,17 +24,6 @@ function makeJWT(payload) {
2424
return `${header}.${payloadPart}.sig`
2525
}
2626

27-
function makeOptions() {
28-
return {
29-
mobify: {
30-
app: {
31-
commerceAPI: {
32-
parameters: {}
33-
}
34-
}
35-
}
36-
}
37-
}
3827

3928
function makeReq(siteId = 'testsite') {
4029
return {headers: {'x-site-id': siteId}}
@@ -126,7 +115,7 @@ describe('getRefreshTokenCookieTTL', () => {
126115
})
127116
})
128117

129-
describe('applyHttpOnlySessionCookies', () => {
118+
describe('setHttpOnlySessionCookies', () => {
130119
beforeEach(() => {
131120
jest.clearAllMocks()
132121
})
@@ -135,24 +124,15 @@ describe('applyHttpOnlySessionCookies', () => {
135124
const res = makeRes()
136125
const buf = makeResponseBuffer({access_token: 'x'})
137126
const req = {headers: {}}
138-
expect(() => applyHttpOnlySessionCookies(buf, {}, req, res, makeOptions())).toThrow(
139-
/siteId is missing/
140-
)
141-
})
142-
143-
test('throws when x-site-id header is empty string', () => {
144-
const res = makeRes()
145-
const buf = makeResponseBuffer({access_token: 'x'})
146-
const req = makeReq(' ')
147-
expect(() => applyHttpOnlySessionCookies(buf, {}, req, res, makeOptions())).toThrow(
127+
expect(() => setHttpOnlySessionCookies(buf, {}, req, res, {})).toThrow(
148128
/siteId is missing/
149129
)
150130
})
151131

152132
test('returns buffer unchanged for non-JSON response', () => {
153133
const res = makeRes()
154134
const buf = Buffer.from('not json', 'utf8')
155-
const result = applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
135+
const result = setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
156136
expect(result).toBe(buf)
157137
expect(res.append).not.toHaveBeenCalled()
158138
})
@@ -172,7 +152,7 @@ describe('applyHttpOnlySessionCookies', () => {
172152
expires_in: 1800,
173153
customer_id: 'cust123'
174154
})
175-
const result = applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
155+
const result = setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
176156

177157
// cc-at: access token (HttpOnly)
178158
const atCookie = parseCookie(res.cookies.find((c) => c.includes('cc-at_testsite=')))
@@ -238,7 +218,7 @@ describe('applyHttpOnlySessionCookies', () => {
238218
refresh_token: 'refresh-value',
239219
expires_in: 1800
240220
})
241-
const result = applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
221+
const result = setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
242222

243223
// cc-at (HttpOnly)
244224
const atCookie = parseCookie(res.cookies.find((c) => c.includes('cc-at_testsite=')))
@@ -283,15 +263,15 @@ describe('applyHttpOnlySessionCookies', () => {
283263
refresh_token: 'refresh-value',
284264
expires_in: 1800
285265
})
286-
applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
266+
setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
287267

288268
expect(res.cookies.find((c) => c.includes('uido_testsite'))).toBeUndefined()
289269
})
290270

291271
test('throws when access token JWT is invalid', () => {
292272
const res = makeRes()
293273
const buf = makeResponseBuffer({access_token: 'not-a-jwt', expires_in: 1800})
294-
expect(() => applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())).toThrow(
274+
expect(() => setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})).toThrow(
295275
/Failed to decode access token JWT/
296276
)
297277
})
@@ -300,7 +280,7 @@ describe('applyHttpOnlySessionCookies', () => {
300280
const res = makeRes()
301281
const accessToken = makeJWT({iat: 5000, exp: 6800, isb: 'uido:ecom::upn:Guest'})
302282
const buf = makeResponseBuffer({access_token: accessToken})
303-
applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
283+
setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
304284

305285
const expCookie = res.cookies.find((c) => c.includes('cc-at-expires_testsite='))
306286
const parsed = parseCookie(expCookie)
@@ -310,30 +290,18 @@ describe('applyHttpOnlySessionCookies', () => {
310290
test('handles response with no tokens (no cookies set, body returned stripped)', () => {
311291
const res = makeRes()
312292
const buf = makeResponseBuffer({expires_in: 1800, other_field: 'value'})
313-
const result = applyHttpOnlySessionCookies(buf, {}, makeReq(), res, makeOptions())
293+
const result = setHttpOnlySessionCookies(buf, {}, makeReq(), res, {})
314294
const body = JSON.parse(result.toString('utf8'))
315295

316296
expect(res.cookies).toHaveLength(0)
317297
expect(body.other_field).toBe('value')
318298
})
319299

320-
test('trims x-site-id and uses trimmed value in cookie names', () => {
321-
const res = makeRes()
322-
const accessToken = makeJWT({iat: 1000, isb: 'uido:ecom::upn:Guest'})
323-
const buf = makeResponseBuffer({access_token: accessToken, expires_in: 1800})
324-
applyHttpOnlySessionCookies(buf, {}, makeReq(' mysite '), res, makeOptions())
325-
326-
const atCookie = res.cookies.find((c) => c.includes('cc-at_mysite='))
327-
expect(atCookie).toBeDefined()
328-
// No leading/trailing spaces in cookie name
329-
expect(res.cookies.find((c) => c.includes('cc-at_ mysite'))).toBeUndefined()
330-
})
331-
332300
test('uses x-site-id header to resolve correct cookie names', () => {
333301
const res = makeRes()
334302
const accessToken = makeJWT({iat: 1000, exp: 2800, isb: 'uido:ecom::upn:Guest'})
335303
const buf = makeResponseBuffer({access_token: accessToken, expires_in: 1800})
336-
applyHttpOnlySessionCookies(buf, {}, makeReq('othersite'), res, makeOptions())
304+
setHttpOnlySessionCookies(buf, {}, makeReq('othersite'), res, {})
337305

338306
const atCookie = res.cookies.find((c) => c.includes('cc-at_othersite='))
339307
expect(atCookie).toBeDefined()

packages/pwa-kit-runtime/src/utils/ssr-server/configure-proxy.basic.test.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7-
import {applyProxyRequestHeaders, applyScapiAuthHeaders, configureProxy} from './configure-proxy'
7+
import {applyProxyRequestHeaders, setScapiAuthRequestHeaders, configureProxy} from './configure-proxy'
88
import * as ssrProxying from '../ssr-proxying'
99
import * as utils from './utils'
1010
import cookie from 'cookie'
@@ -100,7 +100,7 @@ describe('configureProxy ALLOWED_CACHING_PROXY_REQUEST_METHODS', () => {
100100
})
101101
})
102102

103-
describe('applyScapiAuthHeaders', () => {
103+
describe('setScapiAuthRequestHeaders', () => {
104104
beforeEach(() => {
105105
jest.clearAllMocks()
106106
})
@@ -121,7 +121,7 @@ describe('applyScapiAuthHeaders', () => {
121121
}
122122
}
123123

124-
applyScapiAuthHeaders({
124+
setScapiAuthRequestHeaders({
125125
proxyRequest,
126126
incomingRequest,
127127
caching: false,
@@ -149,7 +149,7 @@ describe('applyScapiAuthHeaders', () => {
149149
}
150150
}
151151

152-
applyScapiAuthHeaders({
152+
setScapiAuthRequestHeaders({
153153
proxyRequest,
154154
incomingRequest,
155155
caching: true,
@@ -172,7 +172,7 @@ describe('applyScapiAuthHeaders', () => {
172172
headers: {}
173173
}
174174

175-
applyScapiAuthHeaders({
175+
setScapiAuthRequestHeaders({
176176
proxyRequest,
177177
incomingRequest,
178178
caching: false,
@@ -201,7 +201,7 @@ describe('applyScapiAuthHeaders', () => {
201201
}
202202
}
203203

204-
applyScapiAuthHeaders({
204+
setScapiAuthRequestHeaders({
205205
proxyRequest,
206206
incomingRequest,
207207
caching: false,
@@ -223,7 +223,7 @@ describe('applyScapiAuthHeaders', () => {
223223
headers: {'x-site-id': 'RefArch'}
224224
}
225225

226-
applyScapiAuthHeaders({
226+
setScapiAuthRequestHeaders({
227227
proxyRequest,
228228
incomingRequest,
229229
caching: false,
@@ -249,7 +249,7 @@ describe('applyScapiAuthHeaders', () => {
249249
}
250250
}
251251

252-
applyScapiAuthHeaders({
252+
setScapiAuthRequestHeaders({
253253
proxyRequest,
254254
incomingRequest,
255255
caching: false,

packages/pwa-kit-runtime/src/utils/ssr-server/configure-proxy.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ const generalProxyPathRE = /^\/mobify\/proxy\/([^/]+)(\/.*)$/
4444
* @param caching {Boolean} true for a caching proxy, false for a standard proxy
4545
* @param targetHost {String} the target hostname (host+port)
4646
*/
47-
export const applyScapiAuthHeaders = ({proxyRequest, incomingRequest, caching, targetHost}) => {
47+
export const setScapiAuthRequestHeaders = ({
48+
proxyRequest,
49+
incomingRequest,
50+
caching,
51+
targetHost
52+
}) => {
4853
const url = incomingRequest.url
4954
const resolvedSiteId = incomingRequest.headers?.['x-site-id']
5055

@@ -56,7 +61,7 @@ export const applyScapiAuthHeaders = ({proxyRequest, incomingRequest, caching, t
5661
if (!resolvedSiteId) {
5762
logger.warn(
5863
'x-site-id header is missing on SCAPI proxy request. Bearer token injection skipped.',
59-
{namespace: 'configureProxy.applyScapiAuthHeaders'}
64+
{namespace: 'configureProxy.setScapiAuthRequestHeaders'}
6065
)
6166
return
6267
}
@@ -66,7 +71,7 @@ export const applyScapiAuthHeaders = ({proxyRequest, incomingRequest, caching, t
6671
if (!cookieHeader) return
6772

6873
const cookies = cookie.parse(cookieHeader)
69-
const tokenKey = `cc-at_${resolvedSiteId.trim()}`
74+
const tokenKey = `cc-at_${resolvedSiteId}`
7075
const accessToken = cookies[tokenKey]
7176

7277
if (accessToken) {
@@ -257,7 +262,7 @@ export const configureProxy = ({
257262

258263
// Apply Authorization header with shopper's access token from HttpOnly cookie
259264
if (process.env.MRT_DISABLE_HTTPONLY_SESSION_COOKIES === 'false') {
260-
applyScapiAuthHeaders({
265+
setScapiAuthRequestHeaders({
261266
proxyRequest,
262267
incomingRequest,
263268
caching,

0 commit comments

Comments
 (0)