@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.
- package/assets/bootstrap/js/config/default.js.hbs +27 -0
- package/assets/bootstrap/js/overrides/app/ssr.js.hbs +86 -1
- package/assets/templates/@salesforce/retail-react-app/app/ssr.js.hbs +86 -1
- package/assets/templates/@salesforce/retail-react-app/config/default.js.hbs +1 -1
- package/package.json +4 -4
- package/templates/express-minimal.tar.gz +0 -0
- package/templates/mrt-reference-app.tar.gz +0 -0
- package/templates/retail-react-app.tar.gz +0 -0
- package/templates/typescript-minimal.tar.gz +0 -0
|
@@ -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:
|
|
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:
|
|
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.
|
|
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.
|
|
42
|
-
"internal-lib-build": "3.9.0-preview.
|
|
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": "
|
|
49
|
+
"gitHead": "6965aa2fc067c784660c4bb816e699789ea386c5"
|
|
50
50
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|