March 28th, 2022
October 6, 2021
BigCommerce does a lot of things right in the headless space, but not everything.
Unfortunately, password reset with BigCommerce doesn't work out of the box for headless. So I had to work out how to implement a secure password reset feature, leveraging the BigCommerce platform as much as possible.
In this article series, I'll show you how you can implement the same feature.
There's a lot to this solution and the thought process behind it. As a result, this has been broken up into multiple parts. This part goes into the background and solution overview. If you're just here for the code, check out the next part of the series here.
Of course, BigCommerce has a built-in password reset feature. How else would you manage customer accounts in their storefront? The problem is, it wasn't built for headless, not even as an afterthought. It's not strange; headless wasn't a thing when they made this feature. But it does present a problem. It's possible to trigger the password reset email via the API, but there are three issues with that email template:
I'll go into more detail about these issues and what they mean later on. But for now, those are the three main problems with the built-in password reset feature from a headless perspective.
Let's take a quick look at some of the questions I asked myself and BigCommerce when I rolled my own headless password reset feature.
Yeah, you could, but you can't verify the token. Remember, it's secure and signed for a very good reason.
Password reset is a critical functionality. It is not something you want to get wrong and expose a way for malicious attackers to gain access to your customers' accounts. The damage that could do to your or your client's brand could be irreparable.
Firstly, you need to verify that the customer is the owner of the email. You do that by sending out a password reset email. It sounds simple enough.
But you need to verify that the link they clicked came from that email. You can't just send the email as a URL param; anyone could do that. No, you need to encode all the essential information in a token that only your servers can decode.
Further, the token has to have a limited lifetime and should only be possible to use once to prevent replay attacks where the attacker gains access to the URL with the reset token and tries to repeat the request.
So that leads to the following requirements:
Ok, you're getting somewhere now: you've set the requirements, you've thought about the security of the token, and you're ready to start digging in. Almost ready.
First, let's come up with a solution. There are a few questions hidden in those requirements. Those are:
Let's take a look at the details.
Encoding and decoding tokens is a solved problem. Whichever language and web platform you use, someone has already solved this issue. You don't want to reinvent the wheel on an essential security feature like this. I used the
jsonwebtoken package from npm. I'm sure you'll know of an equivalent in your language.
Limiting the lifetime of a token is built into the
jsonwebtoken library. That was easy.
Well, BigCommerce has a password reset transactional email template. So maybe you can use that? But, as it turns out, no, you can't. As far as I can tell, there's no other way to trigger that email than to make a PUT request to the Customers v3 endpoint to update the customer and set the flag
authentication.force_password_reset to true. Or, as you'll see in the next post, just set a
new_password since a strange default seems to trigger that password reset email as long as the
authentication field is present.
The email sent out by the endpoint mentioned above will point to the domain you host the checkout on (commonly
subdomain.yourdomain.com) and contain a token that only BigCommerce's servers can decode, making it rather useless to you.
You will have to trigger that email from some other service. I ended up using SendGrid; they have reasonable pricing, and it's pretty straightforward to set up templates and send emails using their platform.
You need to create a one time token (OTT) that you store somewhere and delete after use. The token doesn't need to contain anything sensitive; it just needs to be unique. For example, I used a SHA256 hash of the email and the current time.
You should generate that token when you send the password reset email. Then delete it once the customer resets their password. It's a one time token, so it should be generated when it's needed. Otherwise, it shouldn't be present.
You can store the token in a customer attribute. There's some setup required for that, as you'll see in the next post. But it works a treat and saves you from keeping a separate database with OTT token records.
Now that we have a solution planned out, it's time to start coding. In the next post, I'll show you how I solved this problem with Next.js. It should be reasonably translatable to any js framework.