如何在客户端显示服务器端的 Supabase AuthApiError

问题描述 投票:0回答:1

我正在开发一个带有 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 中的此信息,以便显示错误消息,通知用户问题。

javascript error-handling sveltekit supabase
1个回答
0
投票

我刚刚遇到了同样的问题,并在这里找到了你的帖子。

我使用了 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>

我是第一次开发认证网站,如果有经验的朋友有更好的方法请指正。

我希望这有帮助。保重!

© www.soinside.com 2019 - 2024. All rights reserved.