-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@W-17386338 - Secure SSR Endpoints by Verifying SLAS Callback Requests #2180
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]>
headers: { | ||
'User-Agent': 'OctoperfMercuryPerfTest' | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you consider doing both things? i.e.
- Extract the
shortCode
andtenantId
from 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will ask Blair for his input on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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-utils
module that will take thex-slas-callback-token header
and verify it against the jwks fetched for that SLAS instancessr
, fetch the JWKS for the SLAS instance with cachingjose
dependencyHow 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>
/jwks
and/reset-password-callback
Checklists
General
Accessibility Compliance
You must check off all items in one of the follow two lists:
or...
Localization