fix auth: register redirect, password reset flow, GHL contact ID, session handling
- Fix register redirect bug where successful signup returned 400 instead of redirecting to /account/setup (use isRedirect() to re-throw redirects) - Add error display to register page and fix Svelte 5 syntax (on:click) - Add await to Google sign-in on login page and register page - Fix logout to await signOut and redirect to / - Implement full password reset flow: - Add sendResetPassword callback to auth.ts config - Create forgot-password page with email submission - Create reset-password page with one-time token validation - Guard reset page: redirect to / if no valid token in URL - Add ghlContactId field to Profile model for future Go High Level integration - Add Prisma migration for ghl_contact_id column - Update register test to expect redirect instead of broken 400
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
import { auth } from '$lib/auth';
|
||||
import { prisma } from '$lib/db';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const token = url.searchParams.get('token');
|
||||
|
||||
if (!token) {
|
||||
throw redirect(302, '/');
|
||||
}
|
||||
|
||||
const verification = await prisma.verification.findFirst({
|
||||
where: {
|
||||
value: token,
|
||||
expiresAt: { gt: new Date() }
|
||||
}
|
||||
});
|
||||
|
||||
if (!verification) {
|
||||
throw redirect(302, '/');
|
||||
}
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request, url }) => {
|
||||
const token = url.searchParams.get('token');
|
||||
|
||||
if (!token) {
|
||||
return fail(400, { error: 'Invalid or missing reset token.' });
|
||||
}
|
||||
|
||||
const data = await request.formData();
|
||||
const password = data.get('password') as string;
|
||||
const password2 = data.get('password2') as string;
|
||||
|
||||
if (!password || password.length < 8) {
|
||||
return fail(400, { error: 'Password must be at least 8 characters.' });
|
||||
}
|
||||
|
||||
if (password !== password2) {
|
||||
return fail(400, { error: 'Passwords do not match.' });
|
||||
}
|
||||
|
||||
try {
|
||||
await auth.api.resetPassword({
|
||||
body: {
|
||||
newPassword: password,
|
||||
token
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to reset password.';
|
||||
return fail(400, { error: message });
|
||||
}
|
||||
|
||||
throw redirect(303, '/login');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
let { form }: PageProps = $props();
|
||||
</script>
|
||||
|
||||
<div class="mx-auto mt-12 max-w-md">
|
||||
<h1 class="mb-6 text-2xl font-semibold text-gray-900">Reset Password</h1>
|
||||
|
||||
{#if form?.error}
|
||||
<div class="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-red-700">
|
||||
{form.error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form method="POST" class="flex flex-col gap-4">
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="New Password"
|
||||
required
|
||||
minlength="8"
|
||||
class="rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password2"
|
||||
placeholder="Confirm New Password"
|
||||
required
|
||||
minlength="8"
|
||||
class="rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700"
|
||||
>
|
||||
Reset Password
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user