@salesforce/pwa-kit-create-app 3.9.0-preview.1 → 3.9.0-preview.2

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.
@@ -17,6 +17,33 @@ module.exports = {
17
17
  // This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false
18
18
  // showDefaults: true
19
19
  },
20
+ login: {
21
+ passwordless: {
22
+ // Enables or disables passwordless login for the site. Defaults to: false
23
+ enabled: false,
24
+ // The callback URI, which can be an absolute URL (including third-party URIs) or a relative path set up by the developer.
25
+ // Required in 'callback' mode; if missing, passwordless login defaults to 'sms' mode, which requires Marketing Cloud configuration.
26
+ // If the env var `PASSWORDLESS_LOGIN_CALLBACK_URI` is set, it will override the config value.
27
+ callbackURI:
28
+ process.env.PASSWORDLESS_LOGIN_CALLBACK_URI || '/passwordless-login-callback'
29
+ },
30
+ social: {
31
+ // Enables or disables social login for the site. Defaults to: false
32
+ enabled: false,
33
+ // The third-party identity providers supported by your app. The PWA Kit supports Google and Apple by default.
34
+ // Additional IDPs will also need to be added to the IDP_CONFIG in the SocialLogin component.
35
+ idps: ['google', 'apple'],
36
+ // The redirect URI used after a successful social login authentication.
37
+ // This should be a relative path set up by the developer.
38
+ // If the env var `SOCIAL_LOGIN_REDIRECT_URI` is set, it will override the config value.
39
+ redirectURI: process.env.SOCIAL_LOGIN_REDIRECT_URI || '/social-callback'
40
+ },
41
+ resetPassword: {
42
+ // The callback URI, which can be an absolute URL (including third-party URIs) or a relative path set up by the developer.
43
+ // If the env var `RESET_PASSWORD_CALLBACK_URI` is set, it will override the config value.
44
+ callbackURI: process.env.RESET_PASSWORD_CALLBACK_URI || '/reset-password-callback'
45
+ }
46
+ },
20
47
  // The default site for your app. This value will be used when a siteRef could not be determined from the url
21
48
  defaultSite: '{{answers.project.commerce.siteId}}',
22
49
  // Provide aliases for your sites. These will be used in place of your site id when generating paths throughout the application.
@@ -13,6 +13,19 @@ import {defaultPwaKitSecurityHeaders} from '@salesforce/pwa-kit-runtime/utils/mi
13
13
  import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
14
14
  import helmet from 'helmet'
15
15
 
16
+ import express from 'express'
17
+ import {emailLink} from '@salesforce/retail-react-app/app/utils/marketing-cloud/marketing-cloud-email-link'
18
+ import {
19
+ PASSWORDLESS_LOGIN_LANDING_PATH,
20
+ RESET_PASSWORD_LANDING_PATH
21
+ } from '@salesforce/retail-react-app/app/constants'
22
+ import {
23
+ validateSlasCallbackToken,
24
+ jwksCaching
25
+ } from '@salesforce/retail-react-app/app/utils/jwt-utils'
26
+
27
+ const config = getConfig()
28
+
16
29
  const options = {
17
30
  // The build directory (an absolute path)
18
31
  buildDir: path.resolve(process.cwd(), 'build'),
@@ -21,7 +34,7 @@ const options = {
21
34
  defaultCacheTimeSeconds: 600,
22
35
 
23
36
  // The contents of the config file for the current environment
24
- mobify: getConfig(),
37
+ mobify: config,
25
38
 
26
39
  // The port that the local dev server listens on
27
40
  port: 3000,
@@ -49,7 +62,40 @@ const options = {
49
62
 
50
63
  const runtime = getRuntime()
51
64
 
65
+ const resetPasswordCallback =
66
+ config.app.login?.resetPassword?.callbackURI || '/reset-password-callback'
67
+ const passwordlessLoginCallback =
68
+ config.app.login?.passwordless?.callbackURI || '/passwordless-login-callback'
69
+
70
+ // Reusable function to handle sending a magic link email.
71
+ // By default, this implementation uses Marketing Cloud.
72
+ async function sendMagicLinkEmail(req, res, landingPath, emailTemplate, redirectUrl) {
73
+ // Extract the base URL from the request
74
+ const base = req.protocol + '://' + req.get('host')
75
+
76
+ // Extract the email_id and token from the request body
77
+ const {email_id, token} = req.body
78
+
79
+ // Construct the magic link URL
80
+ let magicLink = `${base}${landingPath}?token=${encodeURIComponent(token)}`
81
+ if (landingPath === RESET_PASSWORD_LANDING_PATH) {
82
+ // Add email query parameter for reset password flow
83
+ magicLink += `&email=${encodeURIComponent(email_id)}`
84
+ }
85
+ if (landingPath === PASSWORDLESS_LOGIN_LANDING_PATH && redirectUrl) {
86
+ magicLink += `&redirect_url=${encodeURIComponent(redirectUrl)}`
87
+ }
88
+
89
+ // Call the emailLink function to send an email with the magic link using Marketing Cloud
90
+ const emailLinkResponse = await emailLink(email_id, emailTemplate, magicLink)
91
+
92
+ // Send the response
93
+ res.send(emailLinkResponse)
94
+ }
95
+
52
96
  const {handler} = runtime.createHandler(options, (app) => {
97
+ app.use(express.json()) // To parse JSON payloads
98
+ app.use(express.urlencoded({extended: true}))
53
99
  // Set default HTTP security headers required by PWA Kit
54
100
  app.use(defaultPwaKitSecurityHeaders)
55
101
  // Set custom HTTP security headers
@@ -82,6 +128,45 @@ const {handler} = runtime.createHandler(options, (app) => {
82
128
  res.set('Cache-Control', `max-age=31536000`)
83
129
  res.send()
84
130
  })
131
+
132
+ app.get('/:shortCode/:tenantId/oauth2/jwks', (req, res) => {
133
+ jwksCaching(req, res, {shortCode: req.params.shortCode, tenantId: req.params.tenantId})
134
+ })
135
+
136
+ // Handles the passwordless login callback route. SLAS makes a POST request to this
137
+ // endpoint sending the email address and passwordless token. Then this endpoint calls
138
+ // the sendMagicLinkEmail function to send an email with the passwordless login magic link.
139
+ // https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-passwordless-login.html#receive-the-callback
140
+ app.post(passwordlessLoginCallback, (req, res) => {
141
+ const slasCallbackToken = req.headers['x-slas-callback-token']
142
+ const redirectUrl = req.query.redirectUrl
143
+ validateSlasCallbackToken(slasCallbackToken).then(() => {
144
+ sendMagicLinkEmail(
145
+ req,
146
+ res,
147
+ PASSWORDLESS_LOGIN_LANDING_PATH,
148
+ process.env.MARKETING_CLOUD_PASSWORDLESS_LOGIN_TEMPLATE,
149
+ redirectUrl
150
+ )
151
+ })
152
+ })
153
+
154
+ // Handles the reset password callback route. SLAS makes a POST request to this
155
+ // endpoint sending the email address and reset password token. Then this endpoint calls
156
+ // the sendMagicLinkEmail function to send an email with the reset password magic link.
157
+ // https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-password-reset.html#slas-password-reset-flow
158
+ app.post(resetPasswordCallback, (req, res) => {
159
+ const slasCallbackToken = req.headers['x-slas-callback-token']
160
+ validateSlasCallbackToken(slasCallbackToken).then(() => {
161
+ sendMagicLinkEmail(
162
+ req,
163
+ res,
164
+ RESET_PASSWORD_LANDING_PATH,
165
+ process.env.MARKETING_CLOUD_RESET_PASSWORD_TEMPLATE
166
+ )
167
+ })
168
+ })
169
+
85
170
  app.get('/robots.txt', runtime.serveStaticFile('static/robots.txt'))
86
171
  app.get('/favicon.ico', runtime.serveStaticFile('static/ico/favicon.ico'))
87
172
 
@@ -13,6 +13,19 @@ import {defaultPwaKitSecurityHeaders} from '@salesforce/pwa-kit-runtime/utils/mi
13
13
  import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
14
14
  import helmet from 'helmet'
15
15
 
16
+ import express from 'express'
17
+ import {emailLink} from '@salesforce/retail-react-app/app/utils/marketing-cloud/marketing-cloud-email-link'
18
+ import {
19
+ PASSWORDLESS_LOGIN_LANDING_PATH,
20
+ RESET_PASSWORD_LANDING_PATH
21
+ } from '@salesforce/retail-react-app/app/constants'
22
+ import {
23
+ validateSlasCallbackToken,
24
+ jwksCaching
25
+ } from '@salesforce/retail-react-app/app/utils/jwt-utils'
26
+
27
+ const config = getConfig()
28
+
16
29
  const options = {
17
30
  // The build directory (an absolute path)
18
31
  buildDir: path.resolve(process.cwd(), 'build'),
@@ -21,7 +34,7 @@ const options = {
21
34
  defaultCacheTimeSeconds: 600,
22
35
 
23
36
  // The contents of the config file for the current environment
24
- mobify: getConfig(),
37
+ mobify: config,
25
38
 
26
39
  // The port that the local dev server listens on
27
40
  port: 3000,
@@ -49,7 +62,40 @@ const options = {
49
62
 
50
63
  const runtime = getRuntime()
51
64
 
65
+ const resetPasswordCallback =
66
+ config.app.login?.resetPassword?.callbackURI || '/reset-password-callback'
67
+ const passwordlessLoginCallback =
68
+ config.app.login?.passwordless?.callbackURI || '/passwordless-login-callback'
69
+
70
+ // Reusable function to handle sending a magic link email.
71
+ // By default, this implementation uses Marketing Cloud.
72
+ async function sendMagicLinkEmail(req, res, landingPath, emailTemplate, redirectUrl) {
73
+ // Extract the base URL from the request
74
+ const base = req.protocol + '://' + req.get('host')
75
+
76
+ // Extract the email_id and token from the request body
77
+ const {email_id, token} = req.body
78
+
79
+ // Construct the magic link URL
80
+ let magicLink = `${base}${landingPath}?token=${encodeURIComponent(token)}`
81
+ if (landingPath === RESET_PASSWORD_LANDING_PATH) {
82
+ // Add email query parameter for reset password flow
83
+ magicLink += `&email=${encodeURIComponent(email_id)}`
84
+ }
85
+ if (landingPath === PASSWORDLESS_LOGIN_LANDING_PATH && redirectUrl) {
86
+ magicLink += `&redirect_url=${encodeURIComponent(redirectUrl)}`
87
+ }
88
+
89
+ // Call the emailLink function to send an email with the magic link using Marketing Cloud
90
+ const emailLinkResponse = await emailLink(email_id, emailTemplate, magicLink)
91
+
92
+ // Send the response
93
+ res.send(emailLinkResponse)
94
+ }
95
+
52
96
  const {handler} = runtime.createHandler(options, (app) => {
97
+ app.use(express.json()) // To parse JSON payloads
98
+ app.use(express.urlencoded({extended: true}))
53
99
  // Set default HTTP security headers required by PWA Kit
54
100
  app.use(defaultPwaKitSecurityHeaders)
55
101
  // Set custom HTTP security headers
@@ -82,6 +128,45 @@ const {handler} = runtime.createHandler(options, (app) => {
82
128
  res.set('Cache-Control', `max-age=31536000`)
83
129
  res.send()
84
130
  })
131
+
132
+ app.get('/:shortCode/:tenantId/oauth2/jwks', (req, res) => {
133
+ jwksCaching(req, res, {shortCode: req.params.shortCode, tenantId: req.params.tenantId})
134
+ })
135
+
136
+ // Handles the passwordless login callback route. SLAS makes a POST request to this
137
+ // endpoint sending the email address and passwordless token. Then this endpoint calls
138
+ // the sendMagicLinkEmail function to send an email with the passwordless login magic link.
139
+ // https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-passwordless-login.html#receive-the-callback
140
+ app.post(passwordlessLoginCallback, (req, res) => {
141
+ const slasCallbackToken = req.headers['x-slas-callback-token']
142
+ const redirectUrl = req.query.redirectUrl
143
+ validateSlasCallbackToken(slasCallbackToken).then(() => {
144
+ sendMagicLinkEmail(
145
+ req,
146
+ res,
147
+ PASSWORDLESS_LOGIN_LANDING_PATH,
148
+ process.env.MARKETING_CLOUD_PASSWORDLESS_LOGIN_TEMPLATE,
149
+ redirectUrl
150
+ )
151
+ })
152
+ })
153
+
154
+ // Handles the reset password callback route. SLAS makes a POST request to this
155
+ // endpoint sending the email address and reset password token. Then this endpoint calls
156
+ // the sendMagicLinkEmail function to send an email with the reset password magic link.
157
+ // https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-password-reset.html#slas-password-reset-flow
158
+ app.post(resetPasswordCallback, (req, res) => {
159
+ const slasCallbackToken = req.headers['x-slas-callback-token']
160
+ validateSlasCallbackToken(slasCallbackToken).then(() => {
161
+ sendMagicLinkEmail(
162
+ req,
163
+ res,
164
+ RESET_PASSWORD_LANDING_PATH,
165
+ process.env.MARKETING_CLOUD_RESET_PASSWORD_TEMPLATE
166
+ )
167
+ })
168
+ })
169
+
85
170
  app.get('/robots.txt', runtime.serveStaticFile('static/robots.txt'))
86
171
  app.get('/favicon.ico', runtime.serveStaticFile('static/ico/favicon.ico'))
87
172
 
@@ -35,7 +35,7 @@ module.exports = {
35
35
  // The third-party identity providers supported by your app. The PWA Kit supports Google and Apple by default.
36
36
  // Additional IDPs will also need to be added to the IDP_CONFIG in the SocialLogin component.
37
37
  idps: ['google', 'apple'],
38
- // The redirect URI used after a successful social login authentication.
38
+ // The redirect URI used after a successful social login authentication.
39
39
  // This should be a relative path set up by the developer.
40
40
  // If the env var `SOCIAL_LOGIN_REDIRECT_URI` is set, it will override the config value.
41
41
  redirectURI: process.env.SOCIAL_LOGIN_REDIRECT_URI || '/social-callback'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/pwa-kit-create-app",
3
- "version": "3.9.0-preview.1",
3
+ "version": "3.9.0-preview.2",
4
4
  "description": "Salesforce's project generator tool",
5
5
  "homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/pwa-kit-create-app#readme",
6
6
  "bugs": {
@@ -38,13 +38,13 @@
38
38
  "tar": "^6.2.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@salesforce/pwa-kit-dev": "3.9.0-preview.1",
42
- "internal-lib-build": "3.9.0-preview.1",
41
+ "@salesforce/pwa-kit-dev": "3.9.0-preview.2",
42
+ "internal-lib-build": "3.9.0-preview.2",
43
43
  "verdaccio": "^5.22.1"
44
44
  },
45
45
  "engines": {
46
46
  "node": "^16.11.0 || ^18.0.0 || ^20.0.0 || ^22.0.0",
47
47
  "npm": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
48
48
  },
49
- "gitHead": "a4afa2da67a0c99f51ac1dfd9f63cd414f342dec"
49
+ "gitHead": "6965aa2fc067c784660c4bb816e699789ea386c5"
50
50
  }
Binary file
Binary file
Binary file
Binary file