@faststore/core 3.99.6 → 3.100.0

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 (104) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +47 -40
  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/react-loadable-manifest.json +6 -6
  14. package/.next/routes-manifest.json +1 -1
  15. package/.next/server/chunks/3779.js +1 -0
  16. package/.next/server/chunks/7098.js +2 -2
  17. package/.next/server/chunks/83.js +1 -1
  18. package/.next/server/chunks/948.js +3 -3
  19. package/.next/server/chunks/{9984.js → UINavbarSlider.js} +1 -1
  20. package/.next/server/edge-runtime-webpack.js +24 -0
  21. package/.next/server/edge-runtime-webpack.js.map +1 -1
  22. package/.next/server/instrumentation.js.nft.json +1 -1
  23. package/.next/server/middleware-build-manifest.js +1 -1
  24. package/.next/server/middleware-manifest.json +27 -2
  25. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  26. package/.next/server/pages/404.js +1 -1
  27. package/.next/server/pages/404.js.nft.json +1 -1
  28. package/.next/server/pages/500.js +1 -1
  29. package/.next/server/pages/500.js.nft.json +1 -1
  30. package/.next/server/pages/[...slug].js +1 -1
  31. package/.next/server/pages/[...slug].js.nft.json +1 -1
  32. package/.next/server/pages/[slug]/p.js +1 -1
  33. package/.next/server/pages/[slug]/p.js.nft.json +1 -1
  34. package/.next/server/pages/_app.js.nft.json +1 -1
  35. package/.next/server/pages/_document.js.nft.json +1 -1
  36. package/.next/server/pages/_error.js.nft.json +1 -1
  37. package/.next/server/pages/api/fs/auth/login.js +1 -0
  38. package/.next/server/pages/api/fs/auth/login.js.nft.json +1 -0
  39. package/.next/server/pages/api/fs/logout.js.nft.json +1 -1
  40. package/.next/server/pages/api/graphql.js.nft.json +1 -1
  41. package/.next/server/pages/api/health/live.js.nft.json +1 -1
  42. package/.next/server/pages/api/health/ready.js.nft.json +1 -1
  43. package/.next/server/pages/api/preview.js.nft.json +1 -1
  44. package/.next/server/pages/checkout.js +1 -1
  45. package/.next/server/pages/checkout.js.nft.json +1 -1
  46. package/.next/server/pages/en-US/404.html +2 -2
  47. package/.next/server/pages/en-US/500.html +2 -2
  48. package/.next/server/pages/en-US/checkout.html +2 -2
  49. package/.next/server/pages/en-US/fs-auth-login.html +42 -0
  50. package/.next/server/pages/en-US/login.html +2 -2
  51. package/.next/server/pages/en-US/s.html +2 -2
  52. package/.next/server/pages/en-US.html +2 -2
  53. package/.next/server/pages/fs-auth-login.js.nft.json +1 -0
  54. package/.next/server/pages/index.js +1 -1
  55. package/.next/server/pages/index.js.nft.json +1 -1
  56. package/.next/server/pages/login.js +1 -1
  57. package/.next/server/pages/login.js.nft.json +1 -1
  58. package/.next/server/pages/pvt/account/403.js +1 -1
  59. package/.next/server/pages/pvt/account/403.js.nft.json +1 -1
  60. package/.next/server/pages/pvt/account/404.js +1 -1
  61. package/.next/server/pages/pvt/account/404.js.nft.json +1 -1
  62. package/.next/server/pages/pvt/account/[...unknown].js.nft.json +1 -1
  63. package/.next/server/pages/pvt/account/orders/[id].js +1 -1
  64. package/.next/server/pages/pvt/account/orders/[id].js.nft.json +1 -1
  65. package/.next/server/pages/pvt/account/orders.js +1 -1
  66. package/.next/server/pages/pvt/account/orders.js.nft.json +1 -1
  67. package/.next/server/pages/pvt/account/profile.js +1 -1
  68. package/.next/server/pages/pvt/account/profile.js.nft.json +1 -1
  69. package/.next/server/pages/pvt/account/security.js +1 -1
  70. package/.next/server/pages/pvt/account/security.js.nft.json +1 -1
  71. package/.next/server/pages/pvt/account/user-details.js +1 -1
  72. package/.next/server/pages/pvt/account/user-details.js.nft.json +1 -1
  73. package/.next/server/pages/pvt/account.js.nft.json +1 -1
  74. package/.next/server/pages/s.js +1 -1
  75. package/.next/server/pages/s.js.nft.json +1 -1
  76. package/.next/server/pages-manifest.json +1 -1
  77. package/.next/server/src/middleware.js +5417 -0
  78. package/.next/server/src/middleware.js.map +1 -0
  79. package/.next/server/webpack-runtime.js +1 -1
  80. package/.next/static/9kMY_8ze-x7sawSXOfQGe/_buildManifest.js +1 -0
  81. package/.next/static/chunks/{2927.5a79877943a6bf7c.js → 2927.23bae2c79f0ac0f3.js} +1 -1
  82. package/.next/static/chunks/{UIToast.19a8664c01a00d3a.js → UIToast.de15325248043ce5.js} +1 -1
  83. package/.next/static/chunks/main-083a618181bb5fbb.js +1 -0
  84. package/.next/static/chunks/pages/fs-auth-login-ba2d1b3ef0ef0415.js +1 -0
  85. package/.next/static/chunks/webpack-ff85816fb58111fa.js +1 -0
  86. package/.next/static/css/3bd4edd425711792.css +1 -0
  87. package/.next/trace +156 -143
  88. package/.turbo/turbo-build.log +44 -19
  89. package/.turbo/turbo-test.log +11 -10
  90. package/CHANGELOG.md +6 -0
  91. package/package.json +3 -2
  92. package/src/middleware.ts +87 -0
  93. package/src/pages/api/fs/auth/login.ts +123 -0
  94. package/src/pages/fs-auth-login/fs-auth-login.module.scss +41 -0
  95. package/src/pages/fs-auth-login/index.tsx +90 -0
  96. package/src/server/authentication-service.ts +270 -0
  97. package/src/server/password-protection/webops-api.ts +38 -0
  98. package/src/utils/loginResponse.ts +20 -0
  99. package/test/server/authentication-service.test.ts +460 -0
  100. package/.next/static/UmIFhJcdMOfy3xrHNNZS2/_buildManifest.js +0 -1
  101. package/.next/static/chunks/main-ec03882c4375091d.js +0 -1
  102. package/.next/static/chunks/webpack-65b1de5f075b8367.js +0 -1
  103. package/src/middleware__DISABLED.ts +0 -65
  104. /package/.next/static/{UmIFhJcdMOfy3xrHNNZS2 → 9kMY_8ze-x7sawSXOfQGe}/_ssgManifest.js +0 -0
@@ -1,23 +1,23 @@
1
1
 
2
- > @faststore/core@3.99.4 prebuild /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.6 prebuild /home/runner/work/faststore/faststore/packages/core
3
3
  > na run partytown && na run generate
4
4
 
5
5
 
6
- > @faststore/core@3.99.4 partytown /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@3.99.6 partytown /home/runner/work/faststore/faststore/packages/core
7
7
  > partytown copylib ./public/~partytown
8
8
 
9
9
  Partytown lib copied to: /home/runner/work/faststore/faststore/packages/core/public/~partytown
10
10
 
11
- > @faststore/core@3.99.4 generate /home/runner/work/faststore/faststore/packages/core
11
+ > @faststore/core@3.99.6 generate /home/runner/work/faststore/faststore/packages/core
12
12
  > na run generate:schema && na run generate:codegen && na run format:generated
13
13
 
14
14
 
15
- > @faststore/core@3.99.4 generate:schema /home/runner/work/faststore/faststore/packages/core
15
+ > @faststore/core@3.99.6 generate:schema /home/runner/work/faststore/faststore/packages/core
16
16
  > tsx src/server/generator/generateGraphQLSchemaFile.ts
17
17
 
18
18
  Schema GraphQL file generated successfully
19
19
 
20
- > @faststore/core@3.99.4 generate:codegen /home/runner/work/faststore/faststore/packages/core
20
+ > @faststore/core@3.99.6 generate:codegen /home/runner/work/faststore/faststore/packages/core
21
21
  > graphql-codegen
22
22
 
23
23
  [STARTED] Parse Configuration
@@ -37,11 +37,11 @@ Running lifecycle hook "afterStart" scripts...
37
37
  [CLI] Loading Documents
38
38
  [CLI] Generating output
39
39
 
40
- > @faststore/core@3.99.4 format:generated /home/runner/work/faststore/faststore/packages/core
40
+ > @faststore/core@3.99.6 format:generated /home/runner/work/faststore/faststore/packages/core
41
41
  > prettier --write "@generated/**/*.{ts,js,tsx,jsx,json}" --loglevel error
42
42
 
43
43
 
44
- > @faststore/core@3.99.4 build /home/runner/work/faststore/faststore/packages/core
44
+ > @faststore/core@3.99.6 build /home/runner/work/faststore/faststore/packages/core
45
45
  > next build
46
46
 
47
47
  ⚠ No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
@@ -58,7 +58,7 @@ Browserslist: caniuse-lite is outdated. Please run:
58
58
  Creating an optimized production build ...
59
59
  Disabled SWC as replacement for Babel because of custom Babel configuration ".babelrc.js" https://nextjs.org/docs/messages/swc-disabled
60
60
  Using external babel configuration from /home/runner/work/faststore/faststore/packages/core/.babelrc.js
61
- Browserslist: browsers data (caniuse-lite) is 16 months old. Please run:
61
+ Browserslist: browsers data (caniuse-lite) is 17 months old. Please run:
62
62
  npx update-browserslist-db@latest
63
63
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
64
64
  <w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|/home/runner/work/faststore/faststore/node_modules/.pnpm/next@13.5.11_@babel+core@7.26.7_@opentelemetry+api@1.4.1_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.83.4/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[7].oneOf[9].use[1]!/home/runner/work/faststore/faststore/node_modules/.pnpm/next@13.5.11_@babel+core@7.26.7_@opentelemetry+api@1.4.1_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.83.4/node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[7].oneOf[9].use[2]!/home/runner/work/faststore/faststore/node_modules/.pnpm/next@13.5.11_@babel+core@7.26.7_@opentelemetry+api@1.4.1_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.83.4/node_modules/next/dist/build/webpack/loaders/resolve-url-loader/index.js??ruleSet[1].rules[7].oneOf[9].use[3]!/home/runner/work/faststore/faststore/node_modules/.pnpm/next@13.5.11_@babel+core@7.26.7_@opentelemetry+api@1.4.1_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.83.4/node_modules/next/dist/compiled/sass-loader/cjs.js??ruleSet[1].rules[7].oneOf[9].use[4]!/home/runner/work/faststore/faststore/packages/core/src/components/common/Toast/section.module.scss': No serializer registered for Warning
@@ -77,19 +77,39 @@ Import trace for requested module:
77
77
  ./src/components/common/Toast/index.ts
78
78
  ./src/components/cms/RenderSections.tsx
79
79
 
80
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/deflate.js
81
+ A Node.js API is used (CompressionStream at line: 10) which is not supported in the Edge Runtime.
82
+ Learn more: https://nextjs.org/docs/api-reference/edge-runtime
83
+
84
+ Import trace for requested module:
85
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/deflate.js
86
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js
87
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/index.js
88
+ ./src/server/authentication-service.ts
89
+
90
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/deflate.js
91
+ A Node.js API is used (DecompressionStream at line: 25) which is not supported in the Edge Runtime.
92
+ Learn more: https://nextjs.org/docs/api-reference/edge-runtime
93
+
94
+ Import trace for requested module:
95
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/deflate.js
96
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js
97
+ ../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/index.js
98
+ ./src/server/authentication-service.ts
99
+
80
100
  Collecting page data ...
81
- Generating static pages (0/6) ...
101
+ Generating static pages (0/8) ...
102
+
82
103
  Generating static pages (2/8)
83
104
  Search not found. Add a new component for this section or remove it from the CMS
105
+ Warning: Dynamic Content not found for the page: home. Refer to the Dynamic Content documentation at https://developers.vtex.com/docs/guides/faststore/dynamic-content-overview for mapping the page and the corresponding data-fetching function.
84
106
  Search not found. Add a new component for this section or remove it from the CMS
107
+
85
108
  Generating static pages (4/8)
86
109
  Search not found. Add a new component for this section or remove it from the CMS
87
-
88
110
  Generating static pages (1/6)
89
-
90
111
  Generating static pages (2/6)
91
- Warning: Dynamic Content not found for the page: home. Refer to the Dynamic Content documentation at https://developers.vtex.com/docs/guides/faststore/dynamic-content-overview for mapping the page and the corresponding data-fetching function.
92
112
  Search not found. Add a new component for this section or remove it from the CMS
113
+
93
114
  Generating static pages (6/8)
94
115
  Search not found. Add a new component for this section or remove it from the CMS
95
-
96
116
  Generating static pages (4/6)
97
117
  Search not found. Add a new component for this section or remove it from the CMS
98
-
99
118
  ✓ Generating static pages (6/6)
119
+
100
120
  ✓ Generating static pages (8/8)
101
121
  Finalizing page optimization ...
102
122
  Collecting build traces ...
103
123
 
@@ -101,14 +121,17 @@ Route (pages) Size First Load JS
101
121
  ├ ● /[slug]/p 97.3 kB 255 kB
102
122
  ├ ├ css/1c4983f11b861ea0.css 6.34 kB
103
123
  ├ └ css/54cedf4b65e5863f.css 16.7 kB
104
- ├ ○ /404 1.58 kB 159 kB
105
- ├ ● /500 1.58 kB 159 kB
124
+ ├ ○ /404 1.58 kB 160 kB
125
+ ├ ● /500 1.58 kB 160 kB
126
+ ├ λ /api/fs/auth/login 0 B 109 kB
106
127
  ├ λ /api/fs/logout 0 B 109 kB
107
128
  ├ λ /api/graphql 0 B 109 kB
108
129
  ├ λ /api/health/live 0 B 109 kB
109
130
  ├ λ /api/health/ready 0 B 109 kB
110
131
  ├ λ /api/preview 0 B 109 kB
111
132
  ├ ● /checkout 750 B 159 kB
133
+ ├ ○ /fs-auth-login 2.17 kB 111 kB
134
+ ├ └ css/3bd4edd425711792.css 3.2 kB
112
135
  ├ ● /login 1.7 kB 160 kB
113
136
  ├ λ /pvt/account 247 B 109 kB
114
137
  ├ ● /pvt/account/[...unknown] 287 B 109 kB
@@ -118,7 +141,7 @@ Route (pages) Size First Load JS
118
141
  ├ └ css/ea11d74a2848b093.css 4.89 kB
119
142
  ├ λ /pvt/account/orders 9.89 kB 168 kB
120
143
  ├ └ css/14176a4704ff97b2.css 14.4 kB
121
- ├ λ /pvt/account/orders/[id] 13.6 kB 171 kB
144
+ ├ λ /pvt/account/orders/[id] 13.6 kB 172 kB
122
145
  ├ └ css/82f3cd529b672812.css 14 kB
123
146
  ├ λ /pvt/account/profile 1.99 kB 160 kB
124
147
  ├ └ css/a852afa9be31e946.css 4.58 kB
@@ -129,11 +152,13 @@ Route (pages) Size First Load JS
129
152
  └ ● /s 3.35 kB 176 kB
130
153
  + First Load JS shared by all 118 kB
131
154
  ├ chunks/framework-d514426edf885c68.js 45.4 kB
132
- ├ chunks/main-ec03882c4375091d.js 33.2 kB
155
+ ├ chunks/main-083a618181bb5fbb.js 33.4 kB
133
156
  ├ chunks/pages/_app-35e8dda638ee2672.js 26.6 kB
134
- ├ chunks/webpack-65b1de5f075b8367.js 3.89 kB
157
+ ├ chunks/webpack-ff85816fb58111fa.js 3.9 kB
135
158
  └ css/146a8678babc61bf.css 9.2 kB
136
159
 
160
+ ƒ Middleware 44.8 kB
161
+
137
162
  λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
138
163
  ○ (Static) automatically rendered as static HTML (uses no initial props)
139
164
  ● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
@@ -1,19 +1,20 @@
1
1
 
2
- > @faststore/core@3.99.4 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.6 test /home/runner/work/faststore/faststore/packages/core
3
3
  > jest
4
4
 
5
- PASS test/utils/multipleTemplates.test.ts (11.618 s)
6
- PASS test/utils/clearCookies.test.ts (12.194 s)
7
- PASS test/utils/cookieCacheBusting.test.ts (13.633 s)
8
- PASS test/server/cms/global.test.ts
5
+ PASS test/utils/clearCookies.test.ts (11.099 s)
6
+ PASS test/utils/multipleTemplates.test.ts
7
+ PASS test/utils/cookieCacheBusting.test.ts (14.855 s)
8
+ PASS test/server/authentication-service.test.ts (16.605 s)
9
9
  PASS test/utils/validateSessionRefreshToken.test.ts
10
+ PASS test/server/cms/global.test.ts
10
11
  PASS test/utils/isLocalHost.test.ts
12
+ PASS test/server/cms/index.test.ts
11
13
  PASS test/utils/isLocalHost.browser.test.ts
12
- PASS test/server/cms/index.test.ts (6.005 s)
13
- PASS test/server/index.test.ts (13.099 s)
14
+ PASS test/server/index.test.ts (10.697 s)
14
15
 
15
- Test Suites: 9 passed, 9 total
16
- Tests: 68 passed, 68 total
16
+ Test Suites: 10 passed, 10 total
17
+ Tests: 85 passed, 85 total
17
18
  Snapshots: 0 total
18
- Time: 25.905 s
19
+ Time: 27.703 s
19
20
  Ran all test suites.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [3.100.0](https://github.com/vtex/faststore/compare/v3.99.6...v3.100.0) (2026-06-02)
7
+
8
+ ### Features
9
+
10
+ - Password Protection (v3) ([#3347](https://github.com/vtex/faststore/issues/3347)) ([e215c72](https://github.com/vtex/faststore/commit/e215c72af5bf49783790a8d5ea7316d7c415ec57))
11
+
6
12
  ## [3.99.6](https://github.com/vtex/faststore/compare/v3.99.5...v3.99.6) (2026-05-19)
7
13
 
8
14
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.99.6",
3
+ "version": "3.100.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -88,6 +88,7 @@
88
88
  "idb-keyval": "^5.1.3",
89
89
  "include-media": "^1.4.10",
90
90
  "isomorphic-unfetch": "^3.1.0",
91
+ "jose": "^6.2.3",
91
92
  "lexical": "^0.34.0",
92
93
  "next": "^13.5.9",
93
94
  "next-logger": "^5.0.2",
@@ -126,5 +127,5 @@
126
127
  "ts-jest": "29.1.1",
127
128
  "typescript": "5.3.2"
128
129
  },
129
- "gitHead": "5636f4f2af5ef09cc522c45364d85d445ca5c716"
130
+ "gitHead": "0270a50a08d76544dd1c442635265785b12f793e"
130
131
  }
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Middleware is always active. It runs:
3
+ * 1. Password protection (AuthenticationService) — when applicable (default/custom domains per env).
4
+ * 2. Redirects — only when ENABLE_REDIRECTS_MIDDLEWARE is set at runtime.
5
+ */
6
+
7
+ import { NextResponse } from 'next/server'
8
+ import type { NextRequest } from 'next/server'
9
+ import storeConfig from 'discovery.config'
10
+
11
+ import { AuthenticationService } from './server/authentication-service'
12
+
13
+ type Redirect = {
14
+ from: string
15
+ to: string
16
+ type: 'permanent' | 'temporary'
17
+ }
18
+ interface RedirectsClient {
19
+ get(from: string): Promise<Redirect | null>
20
+ }
21
+
22
+ class DynamoRedirectsClient implements RedirectsClient {
23
+ async get(_from: string): Promise<Redirect | null> {
24
+ // TODO: Implement DynamoDB client. Ensure that the cluster has access to DynamoDB first.
25
+ return null
26
+ }
27
+ }
28
+
29
+ const redirectsClient = new DynamoRedirectsClient()
30
+
31
+ export async function middleware(request: NextRequest) {
32
+ const path = request.nextUrl.pathname
33
+
34
+ let authResult: Awaited<
35
+ ReturnType<AuthenticationService['authenticateRequest']>
36
+ >
37
+
38
+ try {
39
+ const authService = new AuthenticationService()
40
+ authResult = await authService.authenticateRequest(request)
41
+ } catch {
42
+ return NextResponse.error()
43
+ }
44
+
45
+ if (authResult.response.status !== 200) {
46
+ return authResult.response
47
+ }
48
+
49
+ if (process.env.ENABLE_REDIRECTS_MIDDLEWARE === 'true') {
50
+ const redirect = await redirectsClient.get(path)
51
+
52
+ if (redirect) {
53
+ const redirectUrl = new URL(redirect.to, storeConfig.storeUrl)
54
+ const redirectStatusCode = redirect.type === 'permanent' ? 301 : 302
55
+ const response = NextResponse.redirect(redirectUrl, redirectStatusCode)
56
+
57
+ for (const cookie of authResult.response.cookies.getAll()) {
58
+ response.cookies.set(cookie)
59
+ }
60
+
61
+ response.headers.set(
62
+ 'Cache-Control',
63
+ 'public, max-age=300, stale-while-revalidate=31536000'
64
+ )
65
+
66
+ return response
67
+ }
68
+ }
69
+
70
+ return authResult.response
71
+ }
72
+
73
+ export const config = {
74
+ matcher: [
75
+ /*
76
+ * Match all paths including `/`
77
+ * Exclude:
78
+ * - api (e.g. api/fs/auth/login)
79
+ * - _next/static, _next/image
80
+ * - favicon.ico
81
+ * - fs-auth-login (login page)
82
+ * - ~partytown (partytown scripts)
83
+ */
84
+ '/',
85
+ '/((?!api|_next/static|_next/image|favicon.ico|fs-auth-login|~partytown).*)',
86
+ ],
87
+ }
@@ -0,0 +1,123 @@
1
+ import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
2
+
3
+ import storeConfig from 'discovery.config'
4
+
5
+ import type { LoginResponse } from '../../../../utils/loginResponse'
6
+ import {
7
+ sessionUrl,
8
+ passwordProtectionTimeouts,
9
+ } from '../../../../server/password-protection/webops-api'
10
+ import {
11
+ COOKIE_NAME,
12
+ TOKEN_TTL_SECONDS,
13
+ } from 'src/server/authentication-service'
14
+
15
+ interface WebOpsSessionPayload {
16
+ valid: boolean
17
+ token?: string
18
+ }
19
+
20
+ const isSafeReturnToPath = (value: string): boolean => {
21
+ return value.startsWith('/') && !value.startsWith('//')
22
+ }
23
+
24
+ const isWebOpsSessionPayload = (
25
+ data: unknown
26
+ ): data is WebOpsSessionPayload => {
27
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
28
+ return false
29
+ }
30
+
31
+ const payload = data as Record<string, unknown>
32
+
33
+ return (
34
+ typeof payload.valid === 'boolean' &&
35
+ (payload.token === undefined || typeof payload.token === 'string')
36
+ )
37
+ }
38
+
39
+ const handler: NextApiHandler<LoginResponse> = async (
40
+ request: NextApiRequest,
41
+ response: NextApiResponse<LoginResponse>
42
+ ) => {
43
+ if (request.method !== 'POST') {
44
+ response.status(405).end()
45
+ return
46
+ }
47
+
48
+ const storeId = storeConfig.api.storeId
49
+
50
+ try {
51
+ const { password } = request.body ?? {}
52
+
53
+ if (!password || typeof password !== 'string') {
54
+ response.status(400).json({
55
+ success: false,
56
+ error: 'Password is required',
57
+ })
58
+ return
59
+ }
60
+
61
+ const webopsResponse = await fetch(sessionUrl(), {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({ storeId, password }),
65
+ signal: AbortSignal.timeout(passwordProtectionTimeouts.defaultMs),
66
+ })
67
+
68
+ if (!webopsResponse.ok) {
69
+ if (webopsResponse.status === 401 || webopsResponse.status === 403) {
70
+ response.status(401).json({
71
+ success: false,
72
+ error: 'Invalid password',
73
+ })
74
+ return
75
+ }
76
+
77
+ response.status(503).json({
78
+ success: false,
79
+ error: 'Service temporarily unavailable',
80
+ })
81
+ return
82
+ }
83
+
84
+ const data: unknown = await webopsResponse.json()
85
+
86
+ if (!isWebOpsSessionPayload(data)) {
87
+ response.status(500).json({
88
+ success: false,
89
+ error: 'Internal server error',
90
+ })
91
+ return
92
+ }
93
+
94
+ if (data.valid && data.token) {
95
+ const returnTo =
96
+ typeof request.query.returnTo === 'string'
97
+ ? request.query.returnTo
98
+ : '/'
99
+ const sanitizedReturnTo = isSafeReturnToPath(returnTo) ? returnTo : '/'
100
+
101
+ response.setHeader('Set-Cookie', [
102
+ `${COOKIE_NAME}=${data.token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=${TOKEN_TTL_SECONDS}`,
103
+ ])
104
+
105
+ response.status(200).json({
106
+ success: true,
107
+ redirectUrl: sanitizedReturnTo,
108
+ })
109
+ } else {
110
+ response.status(401).json({
111
+ success: false,
112
+ error: 'Invalid password',
113
+ })
114
+ }
115
+ } catch {
116
+ response.status(503).json({
117
+ success: false,
118
+ error: 'Service temporarily unavailable',
119
+ })
120
+ }
121
+ }
122
+
123
+ export default handler
@@ -0,0 +1,41 @@
1
+ @layer components {
2
+ .fsAuthLogin {
3
+ @import "@faststore/ui/src/components/atoms/Button/styles.scss";
4
+ @import "@faststore/ui/src/components/atoms/Input/styles.scss";
5
+ @import "@faststore/ui/src/components/atoms/Loader/styles.scss";
6
+ @import "@faststore/ui/src/components/molecules/InputField/styles.scss";
7
+ }
8
+ }
9
+
10
+ .page {
11
+ display: flex;
12
+ flex-direction: column;
13
+ align-items: center;
14
+ justify-content: center;
15
+ min-height: 100vh;
16
+ padding: var(--fs-spacing-3);
17
+ }
18
+
19
+ .title {
20
+ margin: 0 0 var(--fs-spacing-6) 0;
21
+ font-size: var(--fs-text-size-title-section);
22
+ font-weight: var(--fs-text-weight-bold);
23
+ }
24
+
25
+ .subtitle {
26
+ margin: 0 0 var(--fs-spacing-3) 0;
27
+ font-size: var(--fs-text-size-body);
28
+ }
29
+
30
+ .form {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: var(--fs-spacing-2);
34
+ align-items: stretch;
35
+ width: 100%;
36
+ max-width: 20rem;
37
+
38
+ button {
39
+ align-self: center;
40
+ }
41
+ }
@@ -0,0 +1,90 @@
1
+ import { Button, InputField } from '@faststore/ui'
2
+ import Head from 'next/head'
3
+ import { useRouter } from 'next/router'
4
+ import { type FormEvent, useState } from 'react'
5
+
6
+ import { isLoginResponse } from '../../utils/loginResponse'
7
+ import styles from './fs-auth-login.module.scss'
8
+
9
+ export default function PasswordProtectionLogin() {
10
+ const router = useRouter()
11
+ const returnTo =
12
+ typeof router.query.returnTo === 'string' ? router.query.returnTo : '/'
13
+
14
+ const [password, setPassword] = useState('')
15
+ const [error, setError] = useState('')
16
+ const [loading, setLoading] = useState(false)
17
+
18
+ const handleSubmit = async (e: FormEvent) => {
19
+ e.preventDefault()
20
+ setLoading(true)
21
+ setError('')
22
+
23
+ try {
24
+ const url = new URL('/api/fs/auth/login', globalThis.location.origin)
25
+ url.searchParams.set('returnTo', returnTo)
26
+
27
+ const response = await fetch(url.toString(), {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify({ password }),
31
+ })
32
+
33
+ const data: unknown = await response.json()
34
+
35
+ if (!isLoginResponse(data)) {
36
+ setError('Service temporarily unavailable')
37
+ return
38
+ }
39
+
40
+ if (data.success && data.redirectUrl) {
41
+ globalThis.location.href = data.redirectUrl
42
+ } else {
43
+ setError(data.error ?? 'Invalid password')
44
+ }
45
+ } catch {
46
+ setError('Service temporarily unavailable')
47
+ } finally {
48
+ setLoading(false)
49
+ }
50
+ }
51
+
52
+ return (
53
+ <div className={styles.fsAuthLogin}>
54
+ <Head>
55
+ <meta name="robots" content="noindex,nofollow" />
56
+ </Head>
57
+ <div className={styles.page}>
58
+ <h1 className={styles.title}>This store is password protected</h1>
59
+
60
+ <h2 className={styles.subtitle}>
61
+ Enter the password to access the store
62
+ </h2>
63
+
64
+ <form className={styles.form} onSubmit={handleSubmit}>
65
+ <InputField
66
+ id="fs-auth-password"
67
+ label="Password"
68
+ type="password"
69
+ value={password}
70
+ onChange={(e) => setPassword(e.target.value)}
71
+ placeholder="Enter password"
72
+ autoComplete="current-password"
73
+ required
74
+ disabled={loading}
75
+ error={error || undefined}
76
+ />
77
+ <Button
78
+ type="submit"
79
+ variant="primary"
80
+ size="small"
81
+ disabled={loading}
82
+ loading={loading}
83
+ >
84
+ Login
85
+ </Button>
86
+ </form>
87
+ </div>
88
+ </div>
89
+ )
90
+ }