@faststore/core 3.100.2 → 3.100.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +47 -47
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/config.json +3 -3
  5. package/.next/cache/webpack/client-production/0.pack +0 -0
  6. package/.next/cache/webpack/client-production/index.pack +0 -0
  7. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  8. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  9. package/.next/cache/webpack/server-production/0.pack +0 -0
  10. package/.next/cache/webpack/server-production/index.pack +0 -0
  11. package/.next/prerender-manifest.js +1 -1
  12. package/.next/prerender-manifest.json +1 -1
  13. package/.next/routes-manifest.json +1 -1
  14. package/.next/server/functions-config-manifest.json +1 -1
  15. package/.next/server/middleware-build-manifest.js +1 -1
  16. package/.next/server/middleware-manifest.json +2 -2
  17. package/.next/server/pages/api/fs/password-protection/unlock.js +1 -0
  18. package/.next/server/pages/en-US/404.html +2 -2
  19. package/.next/server/pages/en-US/500.html +2 -2
  20. package/.next/server/pages/en-US/checkout.html +2 -2
  21. package/.next/server/pages/en-US/login.html +2 -2
  22. package/.next/server/pages/en-US/{fs-auth-login.html → password-protection.html} +2 -2
  23. package/.next/server/pages/en-US/s.html +2 -2
  24. package/.next/server/pages/en-US.html +2 -2
  25. package/.next/server/pages-manifest.json +1 -1
  26. package/.next/server/src/middleware.js +33 -33
  27. package/.next/server/src/middleware.js.map +1 -1
  28. package/.next/static/0OKcQtIHO8diwBsczup15/_buildManifest.js +1 -0
  29. package/.next/static/chunks/main-a2d1cfe626f2fef1.js +1 -0
  30. package/.next/static/chunks/pages/password-protection-8dece47ea041d372.js +1 -0
  31. package/.next/static/chunks/{webpack-95c583fdc5f7d468.js → webpack-25c67512e1907efd.js} +1 -1
  32. package/.next/static/css/033b9397a0c0cf71.css +1 -0
  33. package/.next/trace +156 -156
  34. package/.turbo/turbo-build.log +18 -18
  35. package/.turbo/turbo-test.log +8 -8
  36. package/CHANGELOG.md +6 -0
  37. package/package.json +2 -2
  38. package/src/middleware.ts +14 -13
  39. package/src/pages/api/fs/{auth/login.ts → password-protection/unlock.ts} +4 -4
  40. package/src/pages/{fs-auth-login → password-protection}/index.tsx +10 -7
  41. package/src/pages/{fs-auth-login/fs-auth-login.module.scss → password-protection/password-protection.module.scss} +1 -1
  42. package/src/server/{authentication-service.ts → password-protection-service.ts} +22 -18
  43. package/src/utils/{loginResponse.ts → unlockResponse.ts} +7 -2
  44. package/test/server/{authentication-service.test.ts → password-protection-service.test.ts} +71 -59
  45. package/.next/server/pages/api/fs/auth/login.js +0 -1
  46. package/.next/static/chunks/main-083a618181bb5fbb.js +0 -1
  47. package/.next/static/chunks/pages/fs-auth-login-ba2d1b3ef0ef0415.js +0 -1
  48. package/.next/static/css/3bd4edd425711792.css +0 -1
  49. package/.next/static/qnFvdN4P3Em9yV6nJ1gX8/_buildManifest.js +0 -1
  50. /package/.next/server/pages/api/fs/{auth/login.js.nft.json → password-protection/unlock.js.nft.json} +0 -0
  51. /package/.next/server/pages/{fs-auth-login.js.nft.json → password-protection.js.nft.json} +0 -0
  52. /package/.next/static/{qnFvdN4P3Em9yV6nJ1gX8 → 0OKcQtIHO8diwBsczup15}/_ssgManifest.js +0 -0
@@ -2,9 +2,9 @@ import { decodeJwt, errors, importSPKI, jwtVerify } from 'jose'
2
2
  import { NextRequest } from 'next/server'
3
3
 
4
4
  import {
5
- AuthenticationService,
5
+ PasswordProtectionService,
6
6
  resetPasswordProtectionPublicKeyCacheForTests,
7
- } from '../../src/server/authentication-service'
7
+ } from '../../src/server/password-protection-service'
8
8
 
9
9
  function setNodeEnv(value: string | undefined): void {
10
10
  const env = process.env as Record<string, string | undefined>
@@ -62,7 +62,7 @@ function previewRequest(
62
62
  })
63
63
  }
64
64
 
65
- describe('AuthenticationService', () => {
65
+ describe('PasswordProtectionService', () => {
66
66
  const originalNodeEnv = process.env.NODE_ENV
67
67
  const originalCustomDomains = process.env.CUSTOM_DOMAINS_PROTECTION_ENABLED
68
68
 
@@ -83,8 +83,10 @@ describe('AuthenticationService', () => {
83
83
  it('skips protection in development', async () => {
84
84
  setNodeEnv('development')
85
85
 
86
- const service = new AuthenticationService()
87
- const { response } = await service.authenticateRequest(previewRequest('/p'))
86
+ const service = new PasswordProtectionService()
87
+ const { response } = await service.checkStoreProtection(
88
+ previewRequest('/p')
89
+ )
88
90
 
89
91
  expect(response.status).toBe(200)
90
92
  expect(global.fetch).not.toHaveBeenCalled()
@@ -99,13 +101,13 @@ describe('AuthenticationService', () => {
99
101
  }),
100
102
  })
101
103
 
102
- const service = new AuthenticationService()
103
- const { response } = await service.authenticateRequest(
104
+ const service = new PasswordProtectionService()
105
+ const { response } = await service.checkStoreProtection(
104
106
  previewRequest('/products')
105
107
  )
106
108
 
107
109
  expect(response.status).toBe(200)
108
- expect(response.cookies.get('__fs_auth_token')?.value).toBe(
110
+ expect(response.cookies.get('__fs_password_protection')?.value).toBe(
109
111
  'neg-cache-token'
110
112
  )
111
113
  expect(global.fetch).toHaveBeenCalledWith(
@@ -118,7 +120,7 @@ describe('AuthenticationService', () => {
118
120
  )
119
121
  })
120
122
 
121
- it('redirects to login when protected and host matches DEFAULT_DOMAINS scope', async () => {
123
+ it('redirects to unlock page when protected and host matches DEFAULT_DOMAINS scope', async () => {
122
124
  ;(global.fetch as jest.Mock).mockResolvedValue({
123
125
  ok: true,
124
126
  json: async () => ({
@@ -128,14 +130,14 @@ describe('AuthenticationService', () => {
128
130
  }),
129
131
  })
130
132
 
131
- const service = new AuthenticationService()
132
- const { response } = await service.authenticateRequest(
133
+ const service = new PasswordProtectionService()
134
+ const { response } = await service.checkStoreProtection(
133
135
  previewRequest('/secret')
134
136
  )
135
137
 
136
138
  expect(response.status).toBe(307)
137
139
  const location = response.headers.get('location') ?? ''
138
- expect(location).toContain('/fs-auth-login')
140
+ expect(location).toContain('/password-protection')
139
141
  expect(location).toContain('returnTo=%2Fsecret')
140
142
  })
141
143
 
@@ -148,13 +150,15 @@ describe('AuthenticationService', () => {
148
150
  }),
149
151
  })
150
152
 
151
- const service = new AuthenticationService()
152
- const { response } = await service.authenticateRequest(previewRequest('/p'))
153
+ const service = new PasswordProtectionService()
154
+ const { response } = await service.checkStoreProtection(
155
+ previewRequest('/p')
156
+ )
153
157
 
154
158
  expect(response.status).toBe(200)
155
159
  })
156
160
 
157
- it('redirects to login when protected for ALL_DOMAINS on custom host', async () => {
161
+ it('redirects to unlock page when protected for ALL_DOMAINS on custom host', async () => {
158
162
  process.env.CUSTOM_DOMAINS_PROTECTION_ENABLED = 'true'
159
163
  ;(global.fetch as jest.Mock).mockResolvedValue({
160
164
  ok: true,
@@ -164,8 +168,8 @@ describe('AuthenticationService', () => {
164
168
  }),
165
169
  })
166
170
 
167
- const service = new AuthenticationService()
168
- const { response } = await service.authenticateRequest(
171
+ const service = new PasswordProtectionService()
172
+ const { response } = await service.checkStoreProtection(
169
173
  new NextRequest('https://shop.example.com/checkout', {
170
174
  headers: { host: 'shop.example.com' },
171
175
  })
@@ -190,10 +194,10 @@ describe('AuthenticationService', () => {
190
194
  protectedHeader: { alg: 'RS256' },
191
195
  } as unknown as Awaited<ReturnType<typeof jwtVerify>>)
192
196
 
193
- const service = new AuthenticationService()
194
- const { response } = await service.authenticateRequest(
197
+ const service = new PasswordProtectionService()
198
+ const { response } = await service.checkStoreProtection(
195
199
  previewRequest('/account', {
196
- headers: { cookie: '__fs_auth_token=valid' },
200
+ headers: { cookie: '__fs_password_protection=valid' },
197
201
  })
198
202
  )
199
203
 
@@ -220,12 +224,12 @@ describe('AuthenticationService', () => {
220
224
  protectedHeader: { alg: 'RS256' },
221
225
  } as unknown as Awaited<ReturnType<typeof jwtVerify>>)
222
226
 
223
- const service = new AuthenticationService()
224
- const { response } = await service.authenticateRequest(
227
+ const service = new PasswordProtectionService()
228
+ const { response } = await service.checkStoreProtection(
225
229
  previewRequest('/p', {
226
230
  headers: {
227
231
  host: 'preview.vtex.app',
228
- cookie: '__fs_auth_token=abc',
232
+ cookie: '__fs_password_protection=abc',
229
233
  },
230
234
  })
231
235
  )
@@ -254,11 +258,11 @@ describe('AuthenticationService', () => {
254
258
  }),
255
259
  })
256
260
 
257
- const service = new AuthenticationService()
258
- const { response } = await service.authenticateRequest(
261
+ const service = new PasswordProtectionService()
262
+ const { response } = await service.checkStoreProtection(
259
263
  previewRequest('/x', {
260
264
  headers: {
261
- cookie: '__fs_auth_token=bad',
265
+ cookie: '__fs_password_protection=bad',
262
266
  },
263
267
  })
264
268
  )
@@ -280,15 +284,17 @@ describe('AuthenticationService', () => {
280
284
  }),
281
285
  })
282
286
 
283
- const service = new AuthenticationService()
284
- const { response } = await service.authenticateRequest(
287
+ const service = new PasswordProtectionService()
288
+ const { response } = await service.checkStoreProtection(
285
289
  previewRequest('/p', {
286
- headers: { cookie: '__fs_auth_token=garbage' },
290
+ headers: { cookie: '__fs_password_protection=garbage' },
287
291
  })
288
292
  )
289
293
 
290
294
  expect(response.status).toBe(200)
291
- expect(response.cookies.get('__fs_auth_token')?.value).toBe('after-bad-jwt')
295
+ expect(response.cookies.get('__fs_password_protection')?.value).toBe(
296
+ 'after-bad-jwt'
297
+ )
292
298
  expect(jwtVerifyMock).toHaveBeenCalled()
293
299
  expect(global.fetch).toHaveBeenCalledTimes(2)
294
300
  })
@@ -316,18 +322,20 @@ describe('AuthenticationService', () => {
316
322
  }),
317
323
  })
318
324
 
319
- const service = new AuthenticationService()
320
- const { response } = await service.authenticateRequest(
325
+ const service = new PasswordProtectionService()
326
+ const { response } = await service.checkStoreProtection(
321
327
  previewRequest('/p', {
322
- headers: { cookie: '__fs_auth_token=old' },
328
+ headers: { cookie: '__fs_password_protection=old' },
323
329
  })
324
330
  )
325
331
 
326
332
  expect(response.status).toBe(200)
327
- expect(response.cookies.get('__fs_auth_token')?.value).toBe('renewed-token')
333
+ expect(response.cookies.get('__fs_password_protection')?.value).toBe(
334
+ 'renewed-token'
335
+ )
328
336
  })
329
337
 
330
- it('redirects to login when JWT is expired and renew response is not ok', async () => {
338
+ it('redirects to unlock page when JWT is expired and renew response is not ok', async () => {
331
339
  ;(global.fetch as jest.Mock).mockResolvedValueOnce({
332
340
  ok: true,
333
341
  json: async () => ({ publicKey: 'test-pem' }),
@@ -345,17 +353,17 @@ describe('AuthenticationService', () => {
345
353
  status: 502,
346
354
  })
347
355
 
348
- const service = new AuthenticationService()
349
- const { response } = await service.authenticateRequest(
356
+ const service = new PasswordProtectionService()
357
+ const { response } = await service.checkStoreProtection(
350
358
  previewRequest('/p', {
351
- headers: { cookie: '__fs_auth_token=expired' },
359
+ headers: { cookie: '__fs_password_protection=expired' },
352
360
  })
353
361
  )
354
362
 
355
363
  expect(response.status).toBe(307)
356
364
  })
357
365
 
358
- it('redirects to login when JWT is expired and renew body is not valid', async () => {
366
+ it('redirects to unlock page when JWT is expired and renew body is not valid', async () => {
359
367
  ;(global.fetch as jest.Mock).mockResolvedValueOnce({
360
368
  ok: true,
361
369
  json: async () => ({ publicKey: 'test-pem' }),
@@ -373,10 +381,10 @@ describe('AuthenticationService', () => {
373
381
  json: async () => ({ valid: false }),
374
382
  })
375
383
 
376
- const service = new AuthenticationService()
377
- const { response } = await service.authenticateRequest(
384
+ const service = new PasswordProtectionService()
385
+ const { response } = await service.checkStoreProtection(
378
386
  previewRequest('/p', {
379
- headers: { cookie: '__fs_auth_token=expired' },
387
+ headers: { cookie: '__fs_password_protection=expired' },
380
388
  })
381
389
  )
382
390
 
@@ -398,61 +406,65 @@ describe('AuthenticationService', () => {
398
406
  } as ReturnType<typeof decodeJwt>)
399
407
  ;(global.fetch as jest.Mock).mockRejectedValueOnce(new Error('renew down'))
400
408
 
401
- const service = new AuthenticationService()
402
- const { response } = await service.authenticateRequest(
409
+ const service = new PasswordProtectionService()
410
+ const { response } = await service.checkStoreProtection(
403
411
  previewRequest('/p', {
404
- headers: { cookie: '__fs_auth_token=old' },
412
+ headers: { cookie: '__fs_password_protection=old' },
405
413
  })
406
414
  )
407
415
 
408
416
  expect(response.status).toBe(200)
409
417
  })
410
418
 
411
- it('fail-closes to login on default domain when status request fails', async () => {
419
+ it('fail-closes to unlock page on default domain when status request fails', async () => {
412
420
  ;(global.fetch as jest.Mock).mockRejectedValue(new Error('network'))
413
421
 
414
- const service = new AuthenticationService()
415
- const { response } = await service.authenticateRequest(previewRequest('/p'))
422
+ const service = new PasswordProtectionService()
423
+ const { response } = await service.checkStoreProtection(
424
+ previewRequest('/p')
425
+ )
416
426
 
417
427
  expect(response.status).toBe(307)
418
- expect(response.headers.get('location')).toContain('/fs-auth-login')
428
+ expect(response.headers.get('location')).toContain('/password-protection')
419
429
  })
420
430
 
421
- it('fail-closes to login when status endpoint returns a non-ok HTTP status', async () => {
431
+ it('fail-closes to unlock page when status endpoint returns a non-ok HTTP status', async () => {
422
432
  ;(global.fetch as jest.Mock).mockResolvedValue({
423
433
  ok: false,
424
434
  status: 503,
425
435
  })
426
436
 
427
- const service = new AuthenticationService()
428
- const { response } = await service.authenticateRequest(previewRequest('/p'))
437
+ const service = new PasswordProtectionService()
438
+ const { response } = await service.checkStoreProtection(
439
+ previewRequest('/p')
440
+ )
429
441
 
430
442
  expect(response.status).toBe(307)
431
- expect(response.headers.get('location')).toContain('/fs-auth-login')
443
+ expect(response.headers.get('location')).toContain('/password-protection')
432
444
  })
433
445
 
434
- it('fail-closes to login on custom domain when status request fails', async () => {
446
+ it('fail-closes to unlock page on custom domain when status request fails', async () => {
435
447
  process.env.CUSTOM_DOMAINS_PROTECTION_ENABLED = 'true'
436
448
  ;(global.fetch as jest.Mock).mockRejectedValue(new Error('network'))
437
449
 
438
- const service = new AuthenticationService()
450
+ const service = new PasswordProtectionService()
439
451
  const request = new NextRequest('https://shop.example.com/page', {
440
452
  headers: { host: 'shop.example.com' },
441
453
  })
442
454
 
443
- const { response } = await service.authenticateRequest(request)
455
+ const { response } = await service.checkStoreProtection(request)
444
456
 
445
457
  expect(response.status).toBe(307)
446
- expect(response.headers.get('location')).toContain('/fs-auth-login')
458
+ expect(response.headers.get('location')).toContain('/password-protection')
447
459
  })
448
460
 
449
461
  it('does not enforce protection on custom domain when env gate is off', async () => {
450
- const service = new AuthenticationService()
462
+ const service = new PasswordProtectionService()
451
463
  const request = new NextRequest('https://shop.example.com/page', {
452
464
  headers: { host: 'shop.example.com' },
453
465
  })
454
466
 
455
- const { response } = await service.authenticateRequest(request)
467
+ const { response } = await service.checkStoreProtection(request)
456
468
 
457
469
  expect(response.status).toBe(200)
458
470
  expect(global.fetch).not.toHaveBeenCalled()
@@ -1 +0,0 @@
1
- "use strict";(()=>{var e={};e.id=1453,e.ids=[1453],e.modules={56330:e=>{e.exports=require("deepmerge")},20145:e=>{e.exports=require("next/dist/compiled/next-server/pages-api.runtime.prod.js")},92824:e=>{e.exports=require("next/server")},99369:e=>{e.exports=import("jose")},87179:(e,t,s)=>{s.a(e,async(e,r)=>{try{s.r(t),s.d(t,{default:()=>l});var a=s(40870),n=s.n(a),o=s(91930),i=s(99391),u=e([i]);i=(u.then?(await u)():u)[0];let isSafeReturnToPath=e=>e.startsWith("/")&&!e.startsWith("//"),isWebOpsSessionPayload=e=>!("object"!=typeof e||null===e||Array.isArray(e))&&"boolean"==typeof e.valid&&(void 0===e.token||"string"==typeof e.token),handler=async(e,t)=>{if("POST"!==e.method){t.status(405).end();return}let s=n().api.storeId;try{let{password:r}=e.body??{};if(!r||"string"!=typeof r){t.status(400).json({success:!1,error:"Password is required"});return}let a=await fetch((0,o.BW)(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({storeId:s,password:r}),signal:AbortSignal.timeout(o.nn.defaultMs)});if(!a.ok){if(401===a.status||403===a.status){t.status(401).json({success:!1,error:"Invalid password"});return}t.status(503).json({success:!1,error:"Service temporarily unavailable"});return}let n=await a.json();if(!isWebOpsSessionPayload(n)){t.status(500).json({success:!1,error:"Internal server error"});return}if(n.valid&&n.token){let s="string"==typeof e.query.returnTo?e.query.returnTo:"/",r=isSafeReturnToPath(s)?s:"/";t.setHeader("Set-Cookie",[`${i.B$}=${n.token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=${i.Ax}`]),t.status(200).json({success:!0,redirectUrl:r})}else t.status(401).json({success:!1,error:"Invalid password"})}catch{t.status(503).json({success:!1,error:"Service temporarily unavailable"})}},l=handler;r()}catch(e){r(e)}})},99391:(e,t,s)=>{s.a(e,async(e,r)=>{try{s.d(t,{Ax:()=>i,B$:()=>o}),s(92824);var a=s(99369),n=e([a]);a=(n.then?(await n)():n)[0];let o="__fs_auth_token",i=600;r()}catch(e){r(e)}})},91930:(e,t,s)=>{function sessionUrl(){return new URL("/api/v1/password-protection/session",function(){let e=process.env.WEBOPS_API_URL?.trim()??"",t=e.replace(/\/+$/,""),s=t.length>0?t:"https://faststore.vtex.com";return/^https?:\/\//i.test(s)?s:`https://${s}`}())}s.d(t,{BW:()=>sessionUrl,nn:()=>r});let r={publicKeyMs:5e3,defaultMs:1e4}},36117:(e,t,s)=>{s.a(e,async(e,r)=>{try{s.r(t),s.d(t,{config:()=>c,default:()=>l,routeModule:()=>d});var a=s(59820),n=s(44708),o=s(64312),i=s(87179),u=e([i]);i=(u.then?(await u)():u)[0];let l=(0,o.l)(i,"default"),c=(0,o.l)(i,"config"),d=new a.PagesAPIRouteModule({definition:{kind:n.x.PAGES_API,page:"/api/fs/auth/login",pathname:"/api/fs/auth/login",bundlePath:"",filename:""},userland:i});r()}catch(e){r(e)}})}};var t=require("../../../../webpack-api-runtime.js");t.C(e);var __webpack_exec__=e=>t(t.s=e),s=t.X(0,[2249,870],()=>__webpack_exec__(36117));module.exports=s})();