Request a password reset token
When a Customer indicates that they've forgotten their password, your application initiates the reset process by requesting a unique, temporary token from Composable Commerce. This token is then sent to the Customer's registered email address.
- Endpoint:
POST /{projectKey}/customers/password-token- To request a password reset token for a Customer in a Store, use the following endpoint:
POST /{projectKey}/in-store/key={storeKey}/customers/password-token.
- To request a password reset token for a Customer in a Store, use the following endpoint:
- Request: send the Customer email address (
emailfield). Optionally setttlMinutesandinvalidateOlderTokenswhen your flow needs a shorter token lifetime or must invalidate previously issued tokens. For security reasons, the flow doesn't reveal whether the email exists. - Response: returns a
valuethat's the password reset token. - Action: after receiving the token, your backend sends it to the Customer's verified email address within a password reset link. For example,
https://www.example.com/reset-password?token=TOKEN_VALUE.
Example - Generate a password reset token
import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here
import { CustomerToken } from '@commercetools/platform-sdk'; // Import the type for the token response
// Function to request a password reset token
async function requestPasswordResetToken(email: string): Promise<string> {
// Returns the token string
try {
const tokenResponse = await apiRoot
.customers()
.passwordToken()
.post({
body: {
email: email,
ttlMinutes: 60, // Token valid for 60 minutes (1 hour)
invalidateOlderTokens: true, // Invalidate all password tokens previously issued for this Customer
},
})
.execute();
const customerToken: CustomerToken = tokenResponse.body;
console.log(
'Password reset token generated (but not sent to console for security):'
);
// For demonstration, we'll log, but in production, NEVER log the token directly.
if (process.env.NODE_ENV !== 'production') {
console.log('Token Value:', customerToken.value); // Access the value from the typed object
}
return customerToken.value;
} catch (error: any) {
// Type 'any' for caught error for now, as stricter typing for HTTP errors can be complex
console.error('Error requesting password reset token:', error);
// For security, always return a generic success message to the user,
// regardless of whether the email exists or not.
console.log(
'If your email exists in our system, a password reset link has been sent.'
);
throw error;
}
}
// Simulate Alice requesting a password reset
(async () => {
const aliceEmail = 'alice.smith@example.com';
console.log(`\n--- Initiating Password Reset Request for ${aliceEmail} ---`);
try {
const resetToken = await requestPasswordResetToken(aliceEmail);
const resetLink = `https://www.example.com/reset-password?token=${resetToken}`;
const emailData = {
toName: 'Alice',
toEmail: aliceEmail,
subject: 'Password Reset for your Zen Electron Account',
body: `
Dear Alice,
You have requested a password reset for your Zen Electron account.
Please click on the following link to reset your password:
${resetLink}
This link will expire in 1 hour.
If you did not request a password reset, please ignore this email.
`.trim(),
};
// Note to learner: Implement actual email sending logic inside sendPasswordResetEmail()
sendPasswordResetEmail(emailData);
} catch (err) {
console.error('Failed to initiate password reset for Alice.');
}
})();
Reset the password
After the Customer clicks the password reset link from their email, they are navigated to a page where they can enter a new password. Your application then sends this new password along with the token to Composable Commerce.
- Endpoint:
POST /{projectKey}/customers/password/reset- To reset the password for a Customer in a Store, use the following endpoint:
POST /{projectKey}/in-store/key={storeKey}/customers/password/reset.
- To reset the password for a Customer in a Store, use the following endpoint:
- Request: send the
tokenValuefrom the email link and thenewPasswordthat must enfore your backend's strong password policy (for example, minimum length and character requirements). - Optional:
currentPassword: for a logged-in password change, usePOST /{projectKey}/customers/password/changeinstead of the reset flow. This endpoint requires theid,version,currentPassword, andnewPassworddetails.- Outcome: if the token is valid and not expired, the password is updated. Otherwise, Composable Commerce returns an error.
- Security: always enforce strong password policies before sending the new password to Composable Commerce, and require a fresh sign-in after the reset.
Example - Reset a password
tokenValue and newPassword details from the frontend form.import { apiRoot } from '../../client'; // Assuming apiRoot is initialized and exported here
import { Customer } from '@commercetools/platform-sdk'; // Import the Customer type for the response
// Assuming `tokenValue` is received from the URL parameter and `newPassword` from a form
const receivedResetTokenValue = 'GENERATED_RESET_TOKEN_FROM_EMAIL'; // Replace with actual token
const aliceNewPassword = 'VerySecureNewPassword!2025'; // Ensure it meets strong policy
// Function to reset customer password
async function resetCustomerPassword(
token: string,
newPass: string
): Promise<Customer> {
try {
const customerResponse = await apiRoot
.customers()
.passwordReset()
.post({
body: {
tokenValue: token,
newPassword: newPass,
},
})
.execute();
const customer: Customer = customerResponse.body;
console.log('Password successfully reset for customer:', customer.email);
// After successful password reset, you might want to immediately log the user in
// or redirect them to the login page.
return customer;
} catch (error: any) {
// Type 'any' for caught error for now, as stricter typing for HTTP errors can be complex
console.error('Error resetting password:', error);
// Provide a generic error message to the user, e.g., "The password reset link is invalid or has expired."
throw error;
}
}
// Execute password reset
(async () => {
try {
console.log("\n--- Attempting to reset Alice's password ---");
const updatedCustomer = await resetCustomerPassword(
receivedResetTokenValue,
aliceNewPassword
);
// After successful reset, you might auto-login Alice or prompt her to log in
console.log(
`Password reset for Alice successful! Her account is ready. Customer ID: ${updatedCustomer.id}`
);
// Note: Consider having the customer log in with the new password before creating an authenticated session.
// This adds an extra layer of verification, ensuring the user actually knows the new password.
} catch (err) {
console.error("Failed to reset Alice's password.");
}
})();
Entire password reset flow
The following diagram illustrates the complete password reset workflow:
Key takeaways
- The password reset flow is a two-step process. First, request a token and then use that token to reset the password.
- Use the
POST /{projectKey}/customers/password-tokenendpoint to generate a secure, temporary token. - Always set a short time-to-live (TTL) on password reset tokens to enhance security.
- Use the
POST /{projectKey}/customers/password/resetendpoint with the token and new password to complete the reset. - Enforce strong password policies on your backend before sending the new password to the API.
- For security reasons, don't provide feedback on whether an email address exists when a Customer requests a password reset.
You've now learned how to implement a secure password reset flow. Next, we'll explore how to handle the transition from an anonymous to an authenticated Customer session, ensuring a seamless experience.