Basic Auth Implementation in Retail React App Take 2️⃣
Folks often want to protect their in-progress or non-production storefront from prying eyes.
HTTP basic authentication (same mechanism as storefront protection in B2CE) is one way to do this.
This PR shows how you might approach implementing basic auth.
The big challenge is that both HTTP Basic Auth and SCAPI/OCAPI want to use the Authorization header.
In this attempt, I move the API calls to route through the App Server (ssr.js) rather than proxies, and introduce a "mock" header X-Authorization that I use for all API auth. Ib App Server, I swap the header back before forwarding it to the API origin.
This works great, but we're currently blocked by a bug in aws-serverless-express around duplicate query param handling: https://github.com/vendia/serverless-express/issues/214
We use it here: https://github.com/SalesforceCommerceCloud/pwa-kit/blob/effd675712279f86cc0586d33ac613ecc49152f0/packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js#L774
This has since been fixed in that more recent releases of that library.
Blocked by: https://github.com/SalesforceCommerceCloud/pwa-kit/issues/733
A demo of this code is deployed here: https://basic-auth-production.mobify-storefront.com/
Username: storefront
Password: password
Note because of the blocking bug, product search doesn't work correctly.
Hi @johnboxall this looks like a great progress, thanks.
Nice write up @johnboxall !
Are we looking at any kinds of permanent solutions for this? Off of the top of my head I can think of 2 places were we might want to do that, that is not the PWA-Kit*.
- Crazy idea, but how hard would it be able to get SCAPI to support an alternative header for this type of scenario? You used
x-authorizatonanother example would beapplication-authoriziationthat would be suited for the single-page app landscape. Then we won't need this proxy header swap dance. - Alternatively, but more work, would be using a custom authentication scheme at the runtime level. I imagine the solution manifesting itself as a password protection option set via runtime manager. Once enabled and a password set for a given environment, we trigger a flow in that will prompt the end user for a password and set a cookie that can be used safely with the Authorization header used by the scapi api. (We'd obviously have to wait on environment variables to implement something like that tho).
@bendvc
Crazy idea, but how hard would it be able to get SCAPI to support an alternative header for this type of scenario?
I think this is pretty great idea. Let's ask!
Alternatively, but more work, would be using a custom authentication scheme at the runtime level. I imagine the solution manifesting itself as a password protection option set via runtime manager.
The challenge here is that as long as SCAPI/OCAPI/other APIs we're proxying on the same domain want to use the Authorization header, there will be fight. If we solved that problem, then yes, a platform level switch to enable basic auth could work.
I think ideally we'd have some kinda toggle that would require oauth based access, something like https://oauth2-proxy.github.io/oauth2-proxy/ – of which you could say you want auth through Account Manager.
@bendvc
- Idea is great, and fits the bill imho, only question would be if Lambda would support/enable end-to-end headers like that (I'm thinking about Ingress)
- Again, good thinking, what we did in our implementation is to check whether in env.js object basicAuth exists, and in case it does, trigger auth. Now if we'd like to make that runnable from runtime a flag like 'BASIC_AUTH_DISABLED' could be replaced using simple shell, js, pretty much any script tbf, to search for keyword.
in env.js
... basicAuth: { username: 'superadmin', pass: 'VARIABLE_REPLACABLE_AT_RUNTIME_CI-CD' << this is where it can be replaced with something like 'BASIC_AUTH_DISABLED' }
in ssr.js
if (app.basicAuth.password === constants.BASIC_AUTH_DISABLED) { return next() }
Once https://github.com/SalesforceCommerceCloud/commerce-sdk-isomorphic/pull/115 is merged, we should be able to simplify this approach significantly.
Native browser fetch has the ability to disable sending credentials using the credentials: omit option – and we'll no longer have the problem of the Authorization header fight.