I am working on a password reset feature for a flutter application. It sends the user an email which sends them to a website which should handle the password reset. However, I am having issues with Supabase session management and I am unable to update the user password.
Flutter function for sending the email.
Future<void> sendPasswordResetEmail(String email, BuildContext context) async {
try {
final response = await Supabase.instance.client.auth.resetPasswordForEmail(
email,
redirectTo: '',
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Password reset email sent successfully', textAlign: TextAlign.center,),
backgroundColor: const Color.fromARGB(255, 52, 157, 44),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to send password reset email: ${e.toString()}', textAlign: TextAlign.center,),
backgroundColor: Colors.red,
),
);
}
}
The Button link that the email takes to
<div style="text-align: center;">
<a
href="{{ .ConfirmationURL }}#token={{ .Token }}&token_hash={{ .TokenHash }}&email={{ .Email }}"
class="button"
>
Reset Password
</a>
</div>
The inital part of the password reset (Assuming the error lies here)
const [passwords, setPasswords] = useState({ password: '', confirmPassword: '' });
const [message, setMessage] = useState({ type: '', text: '' });
const [isLoading, setIsLoading] = useState(false);
const [session, setSession] = useState(null);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [email, setEmail] = useState('');
useEffect(() => {
const searchParams = new URLSearchParams(window.location.search);
const hashParams = new URLSearchParams(window.location.hash.substring(1));
const searchError = searchParams.get('error');
const hashError = hashParams.get('error');
const errorDescription = searchParams.get('error_description') || hashParams.get('error_description');
if (searchError || hashError) {
setMessage({
type: 'error',
text: errorDescription || 'Invalid or expired reset link',
});
return;
}
const token = hashParams.get('token');
const tokenHash = hashParams.get('token_hash');
const hashEmail = hashParams.get('email');
console.log('token:', token);
console.log('token hash:', tokenHash);
console.log('email:', hashEmail);
if (!token || !tokenHash) {
setMessage({
type: 'error',
text: 'Invalid reset link - missing required parameters',
});
return;
}
if (hashEmail) {
setEmail(hashEmail);
}
// Immediately verify the OTP token for recovery
const verifyRecoveryToken = async () => {
const { data, error } = await supabase.auth.verifyOtp({
email: hashEmail,
token: token,
type: 'recovery',
});
if (error) {
console.error('Verification error:', error);
setMessage({
type: 'error',
text: error.message || 'Invalid or expired reset link.',
});
} else {
console.log('Verified session:', data);
setSession(data);
setMessage({
type: 'success',
text: 'Reset link verified. Please set your new password.',
});
}
};
verifyRecoveryToken();
}, []);
The actual sending the request to the server.
const handlePasswordReset = async (e) => {
e.preventDefault();
if (!session) {
setMessage({
type: 'error',
text: 'No active session. Please request a new password reset.',
});
return;
}
setIsLoading(true);
// Validate passwords before proceeding
if (passwords.password !== passwords.confirmPassword) {
setMessage({
type: 'error',
text: 'Passwords do not match.',
});
setIsLoading(false);
return;
}
if (passwords.password.length < 6) {
setMessage({
type: 'error',
text: 'Password must be at least 6 characters long.',
});
setIsLoading(false);
return;
}
try {
// Update the password using the verified session
const { error: updateError } = await supabase.auth.updateUser({
password: passwords.password,
});
if (updateError) throw updateError;
setMessage({
type: 'success',
text: 'Password updated successfully!',
});
// Clear the form
setPasswords({ password: '', confirmPassword: '' });
// Redirect after success (adjust the URL as needed)
setTimeout(() => {
window.location.href = 'invoiceup://';
}, 2000);
} catch (error) {
console.error('Password update error:', error);
setMessage({
type: 'error',
text: error.message || 'Failed to update password. Please try again.',
});
} finally {
setIsLoading(false);
}
};
EDIT 1: I seem to be getting an error when trying to access supabase with my client
{"message":"No API key found in request","hint":"No `apikey` request header or url param was found."}
This is my code for it
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = '';
const supabaseAnonKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// Initialize Supabase client with enhanced auth configuration
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
I also saw someone say it must be due to supabase allowed sites however the https:// is included therefore it should work. I also have allowed all pages of my site to be allowed auth data.
https://*.mysite.co.uk/*
added in. the url settings of my project
I tried to send myself the password reset email. It sent correctly, the link looks something like this
#token=444555&token_hash=pkce_f3fc77a38dc132a94641a1ad0bd7051baa9addea9f85xxxxxxx&email=myemail%40outlook
How the variables are: token: 444555 token hash: pkce_f3fc77a38dc132a94641a1ad0bd7051baa9addea9f857xxxxxxxxxxxx e9192ac6.0eb2f27f.js:1 email: [email protected]
Instead of a successful sign in i got errors:
Failed to load resource: the server responded with a status of 403 ()Understand this errorAI
**
Failed to load resource: the server responded with a status of 403 ()Understand this errorAI Verification error: AuthApiError: Token has expired or is invalid
**