The way I used to do it before I moved all projects to Passwordless:
Have a route for the password reset and open a component / view something like: { path: '/reset-password/:token', component: ResetPasswordPage }
ResetPasswordPage is your new password form.
Do all client-side validations for your password… too short, too long, at least 1 alpha etc etc
Example of on submit function for your password form. This was done in React:
const handlePasswordSubmit = () => {
const { token } = match?.params // token from your link. Your route was defined as .../:token
// at this stage password was already tested for compliancy and password and retype password are equal.
// instead of console log use your user on-screen notifications system
Accounts.resetPassword(token, pass, err => {
if (err) {
console.log('It looks that this link has already been used and is now expired.')
} else {
console.log('Your password has been changed. Please log in with the new password!')
}
})
history.push('/signin') // send the user to the login screen
}
As the docs states, the method onResetPasswordLink registers a function to call when a reset password link is clicked, and it should be called in top-level code.
So inside of your /client/main.js (or starting point of your client), you can call it.
I have this same issue upon upgrading to Meteor 3.3 from Meteor 2.16. In my client console I get:
Accounts.onResetPasswordLink was called more than once. Only one callback added will be executed.
Accounts.onEmailVerificationLink was called more than once. Only one callback added will be executed.
I set both of the callbacks once in startup/client/accounts.js which gets called by startup/client/index.js.
I finally figured out the issue thanks to Claude. It was from including the accounts-ui package but not using it. Per Claude:
The accounts-ui-unstyled package (pulled in by accounts-ui ) is the culprit — it registers its own onResetPasswordLink and onEmailVerificationLink callbacks at lines 1758 and 1766 of the built bundle. Your app has its own custom login UI and doesn’t use any accounts-ui templates (only one commented-out reference exists). The accounts-ui package is the conflict source.
accounts-ui-unstyled (which accounts-ui pulls in) hardcodes its own onResetPasswordLink and onEmailVerificationLink callbacks unconditionally. Since your app has a fully custom login UI and never uses accounts-ui 's {{> loginButtons}} template, removing accounts-ui from .meteor/packages is the clean fix.