@W-17386338 - Secure SSR Endpoints by Verifying SLAS Callback Requests#2180
Conversation
| * @param {string} options.tenantId - The Tenant ID for the ECOM instance. | ||
| * @returns {Promise<*>} Promise with the JWKS data. | ||
| */ | ||
| async function jwksCaching(req, res, options) { |
There was a problem hiding this comment.
nit: would we consider moving this method, tenantIdRegExp, and shortCodeRegExp into jwt-utils to reduce the code in ssr.js?
…fy-slas-post-callbacks Signed-off-by: Yuna Kim <84923642+yunakim714@users.noreply.github.com>
| headers: { | ||
| 'User-Agent': 'OctoperfMercuryPerfTest' | ||
| } |
There was a problem hiding this comment.
TODO: Remove before merging into feature branch.
@adamraya When testing with staging, this header was required to bypass some Cloudflare restrictions. Was this something you saw when testing with Storefront Preview as well?
There was a problem hiding this comment.
No. At the time we implemented this feature in the private BFF layer, we did not need to add any header.
There was a problem hiding this comment.
Confirmed with Pratik that customer ECOM instances should hit production CDN so there should be no Cloudflare WAFs blocking this API call!
| const appOrigin = getAppOrigin() | ||
| const {app: appConfig} = getConfig() | ||
| const shortCode = appConfig.commerceAPI.parameters.shortCode | ||
| const tenantId = appConfig.commerceAPI.parameters.organizationId.replace(/^f_ecom_/, '') |
There was a problem hiding this comment.
The code assumes the values shortCode and tenantId from the static PWA Kit config instead of extracting and validating them directly from the SLAS token.
Probably the assumption is correct in most cases, but it’s not the safest approach. My understanding is that tokens should be validated based on the data they carry to ensure they match their intended context.
There was a problem hiding this comment.
@adamraya I am using the values from the PWA Kit config because I want to make sure that we are retrieving the JWKs from the tenant that the customer has configured and make sure that the JWT from the callback matches that.
There was a problem hiding this comment.
Would you consider doing both things? i.e.
- Extract the
shortCodeandtenantIdfrom the token. - Validate the values match the PWA Kit config values.
| export const validateSlasCallbackToken = async (token) => { | ||
| try { | ||
| const jwks = createRemoteJWKSet() | ||
| const {payload} = await jwtVerify(token, jwks, {}) |
There was a problem hiding this comment.
Are we only relying on jwtVerify() to validate the token? Should we also check headers, such as kid, alg, or jku?
I'm not an expert in this area, but I recommend reaching out to a SLAS expert who can guide you on what is the CC standard to consider a token valid.
Another question for the SLAS expert is whether it is safe to share these validation logic in a public repo.
There was a problem hiding this comment.
I will ask Blair for his input on this!
There was a problem hiding this comment.
@adamraya I believe it is sufficient to call jwtVerify and have the validation logic in this repo. Unless the PWA team has preferences on where this logic is placed, security wise I believe it is not a risk.
From Blair: "There is no need to validate the header portion of the JWT. The kid is the most important header claim as that is the KeyID of the public key to validate the body signature. Calling jwtVerify should be fine.
I think it would be okay to add the validation of the JWT signature to the repo. After all it is using the Public keys and not exposing the private key or how the JWT was signed."
There was a problem hiding this comment.
I see. I mention this because we usually have an additional layer of validation that checks those claims to ensure the token context is valid, which jwtVerify does not validate. But let's continue with what the experts say.
| headers: { | ||
| 'User-Agent': 'OctoperfMercuryPerfTest' | ||
| } |
There was a problem hiding this comment.
No. At the time we implemented this feature in the private BFF layer, we did not need to add any header.
| const payload = decodeJwt(token) | ||
| const subClaim = payload[CLAIM.ISSUER] | ||
| const tokens = subClaim.split(DELIMITER.ISSUER) | ||
| const tenantId = tokens[2] |
There was a problem hiding this comment.
Extracting the tenantId from the JWT here by reading the iss claim
| if (tenantId !== configTenantId) { | ||
| throw new Error(`The tenant ID in your PWA Kit configuration ("${configTenantId}") does not match the tenant ID in the SLAS callback token ("${tenantId}").`) | ||
| } |
There was a problem hiding this comment.
If the configured tenantId and the JWT tenantId do not match, an error is thrown.
The short code is not provided as part of the JWT, so we can only rely on the configured value in the config file.
b70a97c
into
feature-passwordless-social-login

Description
To ensure the security of the POST endpoints written in
ssr.js, we need to validate that incoming requests originate from SLAS. This will involve validating thex-slas-callback-token header, which is a JWT provided by SLAS during passwordless login and reset callbacks. Unauthorized requests should be rejected with a 401 Unauthorized response.Types of Changes
Changes
jwt-utilsmodule that will take thex-slas-callback-token headerand verify it against the jwks fetched for that SLAS instancessr, fetch the JWKS for the SLAS instance with cachingjosedependencyHow to Test-Drive This PR
npx @salesforce/pwa-kit-dev@latest tail-logs --project wasatch-mrt --environment reset-password-poc --cloud-origin https://cloud-staging.mrt-staging.com --credentialsFile <pathToFileHere>/jwksand/reset-password-callbackChecklists
General
Accessibility Compliance
You must check off all items in one of the follow two lists:
or...
Localization