我正在开发一个带有 Supabase 后端的 SvelteKit 应用程序。我刚刚完成输入验证,现在我尝试显示无效凭据的错误。我正在努力寻找一种方法来做到这一点。
这里是 src/routes/login/+page.svelte:
<script>
import { superForm } from "sveltekit-superforms";
export let data;
let showAuthError = false;
const { form, errors, enhance } = superForm(data.form);
let selectedLoginMethod = "email"
</script>
<main class="flex justify-center items-center h-screen">
<div class="w-full max-w-md">
<h1 class="text-center text-3xl font-bold mb-4">Login</h1>
<div class="button-container justify-center items-center flex mb-4">
<button
type="button"
class={`btn btn-primary ${selectedLoginMethod === "email" ? "active" : ""}`}
on:click={() => (selectedLoginMethod = "email")}
>
Email
</button>
<button
type="button"
class={`btn btn-primary ${selectedLoginMethod === "emailCode" ? "active" : ""}`}
on:click={() => (selectedLoginMethod = "emailCode")}
>
Email code
</button>
<button
type="button"
class={`btn btn-primary ${selectedLoginMethod === "google" ? "active" : ""}`}
on:click={() => (selectedLoginMethod = "google")}
>
Google
</button>
</div>
{#if selectedLoginMethod === "email"}
<form method="POST" class="space-y-4" use:enhance>
{#if $errors.email}
<div role="alert" class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{$errors.email}</span>
</div>
{/if}
{#if $errors.password}
<div role="alert" class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{$errors.password}</span>
</div>
{/if}
{#if showAuthError}
<div role="alert" class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>Invalid Credentials</span>
</div>
{/if}
<input type="hidden" name="loginMethod" bind:value={selectedLoginMethod} />
<input type="email" placeholder="[email protected]" name="email" aria-invalid={$errors.email ? 'true' : undefined} bind:value={$form.email} class="input input-bordered input-primary w-full max-w-xl" />
<input type="password" placeholder="Pa$$word123!" name="password" aria-invalid={$errors.password ? 'true' : undefined} bind:value={$form.password} class="input input-bordered input-primary w-full max-w-xl" />
<a class="block mb-2 text-secondary hover:text-primary" href="/signup">New to the platform? Join now</a>
<button type="submit" class="btn btn-primary grow">
Sign in
</button>
</form>
{:else if selectedLoginMethod === "emailCode"}
<form method="POST" class="space-y-4" use:enhance>
{#if $errors.email}
<div role="alert" class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{$errors.email}</span>
</div>
{/if}
<input type="hidden" name="loginMethod" bind:value={selectedLoginMethod} />
<input type="email" placeholder="[email protected]" name="email" bind:value={$form.email} required class="input input-bordered input-primary w-full max-w-xl" />
<button type="submit" class="btn btn-primary grow">
{selectedLoginMethod === "magicLink" ? "Send link" : "Send code"}
</button>
</form>
{:else if selectedLoginMethod === "google"}
<form method="POST" action="/login" class="space-y-4" use:enhance>
<input type="hidden" name="loginMethod" value="google" />
<button type="submit" class="btn btn-primary grow">
Sign in with Google
</button>
</form>
{/if}
</div>
</main>
<style>
.button-container {
display: flex;
gap: 1rem;
}
</style>
这里是 src/routes/login/+page.server.js:
import { superValidate } from "sveltekit-superforms";
import { joi } from "sveltekit-superforms/adapters";
import Joi from "joi"
import { fail, redirect } from "@sveltejs/kit";
import { AuthApiError } from "@supabase/supabase-js";
const defaultLoginSchema = Joi.object({
email: Joi.string()
.required()
.email({ minDomainSegments: 2 })
.messages({
"string.email": "Enter a valid email address",
"string.empty": "Email is required"
}),
password: Joi.string()
.required()
.messages({"string.empty": "Password is required"}),
loginMethod: Joi.string()
.default('email')
});
const passwordlessLoginSchema = Joi.object({
email: Joi.string()
.required()
.email({ minDomainSegments: 2 })
.messages({
"string.email": "Enter a valid email address",
"string.empty": "Email is required"
}),
loginMethod: Joi.string()
.default('emailCode')
});
/** @type {import('./$types').PageServerLoad} */
export const load = (async () => {
const form = await superValidate(joi(defaultLoginSchema));
return { form };
});
export const actions = {
async default({ request, locals }) {
const formData = await request.formData();
const selectedLoginMethod = formData.get("loginMethod");
if (selectedLoginMethod === "email") {
const form = await superValidate(formData, joi(defaultLoginSchema))
if (!form.valid) {
console.error("Validaton Error:", form);
return fail(400, { form });
}
const { data, error } = await locals.supabase.auth.signInWithPassword({
email: form.data.email,
password: form.data.password,
});
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
console.error("Invalid credentials", error)
return fail(400, {
error: "Invalid credentials",
email: form.data.email,
invalid: true,
message: error.message,
form: form,
authApiError: true
});
}
return fail(500, {
message: "Server error. Try again later.",
form: form,
});
}
console.log("Sign in with email & password successful: ", data)
redirect(307, '/home');
} else if (selectedLoginMethod === 'emailCode') {
const form = await superValidate(formData, joi(passwordlessLoginSchema))
if (!form.valid) {
console.error("Validaton Error:", form);
return fail(400, { form });
}
console.log("formdata:", form);
const { data, error } = await locals.supabase.auth.signInWithOtp({
email: form.data.email,
options: {
shouldCreateUser: false,
}
});
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
return fail(400, {
error: "Invalid credentials",
email: form.data.email,
invalid: true,
message: err.message,
form: form,
apiAuthError: true
});
}
return fail(500, {
message: "Server error. Try again later.",
form: form,
});
}
console.log(`One-Time-Password was sent to ${form.data.email}: `, data);
redirect(307, `/verify-otp?email=${email}`);
} else if (selectedLoginMethod === 'google') {
// console.log("SESSION (Before):", request.locals.session);
const { data, error } = await locals.supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: 'http://localhost:5173/home'
}
});
// console.log("SESSION (After Update):", request.locals.session);
if (error) {
console.error("Google Sign-in Response (Error):", error);
return fail(500, {
message: "Error authenticating with Google.",
});
}
redirect(307, data.url)
}
},
};
我尝试将数据获取到客户端,但没有成功。当存在无效凭据时,这会记录在控制台中:
Invalid credentials AuthApiError: Invalid login credentials
at handleError (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:43:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async _handleRequest (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:81:9)
at async _request (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:65:18)
at async SupabaseAuthClient.signInWithPassword (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@supabase/gotrue-js/dist/main/GoTrueClient.js:300:23)
at async default (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/src/routes/login/+page.server.js:63:37)
at async Module.handle_action_json_request (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@sveltejs/kit/src/runtime/server/page/actions.js:48:18)
at async resolve (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@sveltejs/kit/src/runtime/server/respond.js:457:18)
at async Module.respond (/Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@sveltejs/kit/src/runtime/server/respond.js:330:20)
at async file:///Users/leonardmarshall-afzal/Projects/platform--supabase/frontend/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js:524:22 {
__isAuthError: true,
status: 400
}
这是浏览器开发者工具的网络选项卡中的响应:
{
"type": "failure",
"status": 400,
"data": "[{\"error\":1,\"email\":2,\"invalid\":3,\"message\":4,\"form\":5,\"authApiError\":3},\"Invalid credentials\",\"[email protected]\",true,\"Invalid login credentials\",{\"id\":6,\"valid\":3,\"posted\":3,\"errors\":7,\"data\":8},\"dz8vqv\",{},{\"email\":2,\"password\":9,\"loginMethod\":10},\"Password123!\",\"email\"]"
}
如何访问 +page.svelte 中的此信息,以便显示错误消息,通知用户问题。
我刚刚遇到了同样的问题,并在这里找到了你的帖子。
我使用了 SuperForms 的
message
助手(以下是文档:https://superforms.rocks/concepts/messages#status-messages)
在 +page.server.ts 中,您要查找错误,我已将
return fail
替换为 return message
:
//+page.server.ts
...
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
return message(form, 'Invalid email or password');
}
}
...
还在+page.svelte中将
message
添加到superForm
并在{#if}
语句中调用它:
//+page.svelte
<script>
...
const { form, errors, enhance, message } = superForm(data.form);
...
</script>
<form>
...
{#if $message}
<small>{$message}</small>
{/if}
</form>
我是第一次开发认证网站,如果有经验的朋友有更好的方法请指正。
我希望这有帮助。保重!