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. The reset token is then sent to the Customer's registered email address.
- Endpoint: Create password reset token for Customer (
POST /{projectKey}/customers/password-token).- To request a password reset token for a Customer in a Store, use the Create password reset token for Customer in Store endpoint.
- Request: send the Customer email address (
emailfield) in a CustomerCreatePasswordResetToken payload. 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 CustomerToken containing 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, the Customer is 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: Reset password of Customer (
POST /{projectKey}/customers/password/reset).- To reset the password for a Customer in a Store, use Reset password of Customer in Store.
- Request: send the
tokenValuefrom the email link and thenewPasswordin a CustomerResetPassword payload. The new password must enforce your backend's strong password policy (for example, minimum length and character requirements). - Optional: for a logged-in password change, use Change password of Customer instead. The Change password endpoint requires the
id,version,currentPassword, andnewPassworddetails. - Outcome: if the token is valid and not expired, the password is updated, and previously issued access and refresh tokens from the password flow and refresh token flow are invalidated with eventual consistency. 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.