fix: hide reset token, add password confirmation field

Token is now read from URL silently. User enters password twice
with client-side match validation. Shows error for missing/invalid tokens.

v1.23.1
This commit is contained in:
mARTin 2026-03-10 16:43:27 +01:00
parent 59cfcc7be4
commit 627f4c0005
2 changed files with 61 additions and 47 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.23.0",
"version": "1.23.1",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js",
"scripts": {

View File

@ -5,33 +5,41 @@ import React, { useState, useEffect } from 'react';
const ResetPasswordPage = () => {
const [token, setToken] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [message, setMessage] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [showSuccess, setShowSuccess] = useState(false);
const [hasToken, setHasToken] = useState(false);
// Get token from URL query parameter
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const tokenParam = urlParams.get('token');
if (tokenParam) {
setToken(tokenParam);
setHasToken(true);
}
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setMessage(null);
setError(null);
if (newPassword.length < 6) {
setError('Password must be at least 6 characters');
return;
}
if (newPassword !== confirmPassword) {
setError('Passwords do not match');
return;
}
setIsLoading(true);
try {
// Simulate API call
const response = await fetch('/api/auth/reset-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, newPassword }),
});
@ -41,13 +49,9 @@ const ResetPasswordPage = () => {
throw new Error(result.error || 'Failed to reset password');
}
setMessage('Password reset successfully!');
setShowSuccess(true);
setToken('');
setNewPassword('');
} catch (err) {
setError(err instanceof Error ? err.message : 'An unknown error occurred');
console.error('Reset password error:', err);
} finally {
setIsLoading(false);
}
@ -60,7 +64,25 @@ const ResetPasswordPage = () => {
<div className="text-center">
<h2 className="text-2xl font-bold text-gray-900">Password Reset Successful!</h2>
<p className="mt-2 text-gray-600">
Your password has been reset successfully. You can now <a href="/auth/login" className="font-medium text-indigo-600 hover:text-indigo-500">sign in</a> with your new password.
Your password has been reset successfully. You can now{' '}
<a href="/auth/login" className="font-medium text-indigo-600 hover:text-indigo-500">sign in</a>{' '}
with your new password.
</p>
</div>
</div>
</div>
);
}
if (!hasToken) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
<div className="text-center">
<h2 className="text-2xl font-bold text-gray-900">Invalid Reset Link</h2>
<p className="mt-2 text-gray-600">
This reset link is invalid or has expired. Please{' '}
<a href="/auth/forgot-password" className="font-medium text-indigo-600 hover:text-indigo-500">request a new one</a>.
</p>
</div>
</div>
@ -74,7 +96,7 @@ const ResetPasswordPage = () => {
<div className="w-full max-w-md">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900">Weekly To Do List</h1>
<p className="mt-2 text-gray-600">Reset your password</p>
<p className="mt-2 text-gray-600">Choose a new password</p>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
@ -86,24 +108,7 @@ const ResetPasswordPage = () => {
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="token" className="sr-only">
Reset Token
</label>
<input
id="token"
name="token"
type="text"
required
value={token}
onChange={(e) => setToken(e.target.value)}
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Reset Token"
/>
</div>
<div>
<label htmlFor="new-password" className="sr-only">
New Password
</label>
<label htmlFor="new-password" className="sr-only">New Password</label>
<input
id="new-password"
name="new-password"
@ -111,10 +116,23 @@ const ResetPasswordPage = () => {
required
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="New Password"
/>
</div>
<div>
<label htmlFor="confirm-password" className="sr-only">Confirm Password</label>
<input
id="confirm-password"
name="confirm-password"
type="password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Confirm Password"
/>
</div>
</div>
<div>
@ -123,11 +141,7 @@ const ResetPasswordPage = () => {
disabled={isLoading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
{isLoading ? (
<span>Resetting...</span>
) : (
<span>Reset Password</span>
)}
{isLoading ? 'Resetting...' : 'Reset Password'}
</button>
</div>
</form>