Home > Web Front-end > Vue.js > How to use Supabase Auth method in Vue3

How to use Supabase Auth method in Vue3

WBOY
Release: 2023-05-28 08:39:18
forward
1293 people have browsed it

    引言

    Supabase自称是一款“替代开源Firebase”的工具。我对与Supbase合作已经有一段时间了,我想我将尝试使用他们的认证API来为Vue.js 3应用程序进行认证设置。

    首先,你为什么要使用Supabase Auth?最重要的是,如果你使用Supabase作为你的数据存储,(它有一些非常甜蜜的好处),Supabase Auth是你可以管理对这些数据的访问的唯一方法。其次,虽然Supabase Auth也有许多不同的功能。

    • 没有中间件的用户权限(通过Postgres的行级安全)。

    • 神奇的电子邮件链接

    • 社交提供者的登录

    • 以及所有其他你期望的封装在一个简单的JavaScript SDK中的Auth功能

    综上所述,值得一提的是,Supabase Auth不是一个独立的认证提供商,它只是为了与其他Supabase服务(数据、存储等)一起使用。

    好了,让我们开始在我们的Vue.js 3应用程序中实施Supabase Auth。

    安装Supabase

    我假设你已经有了一个用Vue Router设置的Vue.js 3应用程序,如果没有,你可以下载这个模板代码来进行操作。我还假设你已经有了一个Supabase账户和项目设置。如果你还没有,可以访问supabase.io,该网站会引导你完成第一步。

    然后,为了与Supabase auth以及它的任何其他服务合作,我们需要安装Supabase JavaScript SDK。

    npm install @supabase/supabase-js
    Copy after login

    设置Supabase

    安装完Supabase后,我们需要经过几个步骤来设置它。我将创建一个名为UseSupabase.js 的组合,用来组织这个设置。

    // UseSupabase.js
    import { createClient } from "@supabase/supabase-js";
    // these can come from an environment variable if desired
    // not required however as they are 100% exposed on the client side anyway 
    // and that's ok, Supabase expects this (security is provided by Row Level Security)
    const supabaseUrl = "";
    const supabaseKey = "";
    // setup client
    const supabase = createClient(supabaseUrl, supabaseKey);
    // expose supabase client
    export default function useSupabase() {
      return { supabase };
    }
    Copy after login

    supabaseUrl和supabaseKey可以在Supabase仪表板上的Settings > API

    当你在Supabase界面时,你也可以到Autentication > Settings ,设置你的网站网址,以便Supabase知道如何正确地重定向确认邮件等。它的默认值是localhost:3000,但你可以根据需要进行修改。

    创建一个AuthUser组合

    设置好Supabase SDK后,我们现在可以开始使用它了。首先,我将创建一个AuthUser组合,以抽象出与AuthUser的一些交互,并存留所有我们需要填写的方法。这将在我们希望离开Supabase时大有裨益,同时也有助于将所有的AuthUser功能整合到一个地方。

    import { ref } from "vue";
    // user is set outside of the useAuthUser function
    // so that it will act as global state and always refer to a single user
    const user = ref(null);
    export default function useAuthUser() {
      /**
       * Login with email and password
       */
      const login = async ({ email, password }) => {};
      /**
       * Login with google, github, etc
       */
      const loginWithSocialProvider = (provider) => {};
      /**
       * Logout
       */
      const logout = async () => {};
      /**
       * Check if the user is logged in or not
       */
      const isLoggedIn = () => {};
      /**
       * Register
       */
      const register = async ({ email, password, ...meta }) => {};
      /**
       * Update user email, password, or meta data
       */
      const update = async (data) => {};
      /**
       * Send user an email to reset their password
       * (ie. support "Forgot Password?")
       */
      const sendPasswordRestEmail = async (email) => {};
      return {
        user,
        login,
        loginWithSocialProvider,
        isLoggedIn,
        logout,
        register,
        update,
        sendPasswordRestEmail,
        maybeHandleEmailConfirmation,
      };
    }
    Copy after login

    创建页面

    接下来,让我们来创建典型用户认证流程所需的页面。我使用了Tailwind CSS进行样式设计,不过这些样式可以根据你的需求进行自由调整。

    注册.vue

    我们需要的第一个页面是一个注册页面。它需要包含必要的字段,以便在Supabase中创建一个用户。这些字段是电子邮件和密码。我们也可以在Supabase中为我们的用户添加任意的元数据,这意味着我们也可以让用户的真实姓名成为注册的一部分。

    // Register.vue
    <script>...</script>
    <template>
      <form class="max-w-lg m-auto" @submit.prevent="handleSubmit">
        <h2 class="text-3xl mb-5">Register</h2>
        <label>Name <input v-model="form.name" type="text" /></label>
        <label>Email <input v-model="form.email" type="email" /></label>
        <label>Password <input v-model="form.password" type="password" /></label>
        <button>Register</button>
      </form>
    </template>
    Copy after login

    我们可以使用一个响应式引用来追踪表单数据,并提供一个函数来处理表单的提交,这个函数也可以放在脚本部分中。在表单提交时,我们将从AuthUser组合中调用注册函数(它仍然是空的,但我们将在稍后用supabase的具体调用来填充它),然后重定向到一个页面,指示用户检查他们的电子邮件以确认注册。

    // Register.vue
    <script setup>
    import { ref } from "vue";
    import useAuthUser from "@/composables/UseAuthUser";
    import { useRouter } from "vue-router";
    // Use necessary composables
    const router = useRouter();
    const { register } = useAuthUser();
    // Form reactive ref to keep up with the form data
    const form = ref({
      name: "",
      email: "",
      password: "",
    });
    // function to hand the form submit
    const handleSubmit = async () => {
      try {
            // use the register method from the AuthUser composable
        await register(form.value);
            // and redirect to a EmailConfirmation page the will instruct
            // the user to confirm they&#39;re email address
        router.push({
          name: "EmailConfirmation",
          query: { email: form.value.email },
        });
      } catch (error) {
        alert(error.message);
      }
    };
    </script>
    <template>...</template>
    Copy after login

    最后,我们需要为该页面注册路由。

    // router/index.js
    const routes = [
      //...
        {
        name: "Register",
        path: "/register",
        component: () => import("@/pages/Register.vue"),
      },
    ]
    Copy after login

    如果你使用了模板代码,访问/register ,应该会得到一个看起来像这样的页面。

    EmailConfirmation.vue

    既然我们要重定向到一个EmailConfirmation页面,那么需要确保它存在。它将只是一个简单的模板和一条信息。我们已经将来自注册表单的电子邮件作为一个查询变量提供,因此在这里可以显示它,同时进行重定向。

    <template>
      <div>
        <h2 class="text-3xl">Thanks for registering!</h2>
        <p>
          Please confirm your email to finishing registering:
          {{ $route.query.email }}
        </p>
      </div>
    </template>
    Copy after login

    再一次,我们也需要注册这个路由。

    // router/index.js
    const routes = [
        //...
        {
        name: "EmailConfirmation",
        path: "/email-confirmation",
        component: () => import("@/pages/EmailConfirmation.vue"),
        },
    ]
    Copy after login

    现在访问带有电子邮件查询变量的/email-confirmation 路由将看起来像这样。

    /[email protected]

    登录.vu

    在注册并确认了他们的电子邮件后,用户将不得不登录。让我们接下来创建这个页面。

    该模板将包括一个带有电子邮件和密码字段的表单,以及一个指向忘记密码页面的链接,还有一个处理Github登录的链接。

    <script>...</script>
    <template>
      <div class="max-w-lg m-auto">
        <form @submit.prevent="handleLogin">
          <h2 class="text-3xl mb-5">Login</h2>
          <label>Email <input v-model="form.email" type="email" /></label>
          <label>Password <input v-model="form.password" type="password" /></label>
          <button>Login</button>
          <router-link to="/forgotPassword">Forgot Password?</router-link>
        </form>
        <div class="mt-5">
          <a @click.prevent="handleLogin(&#39;github&#39;)">Github</a>
        </div>
      </div>
    </template>
    Copy after login

    在脚本部分,我们可以创建一个反应式引用来跟上表单的值,以及一个函数来处理表单的提交。

    <script setup>
    import { ref } from "vue";
    import useAuthUser from "@/composables/UseAuthUser";
    import { useRouter } from "vue-router";
    // Use necessary composables
    const router = useRouter();
    const { login, loginWithSocialProvider } = useAuthUser();
    // keep up with form data
    const form = ref({
      email: "",
      password: "",
    });
    // call the proper login method from the AuthUser composable
    // on the submit of the form
    const handleLogin = async (provider) => {
      try {
        provider
          ? await loginWithSocialProvider(provider)
          : await login(form.value);
        router.push({ name: "Me" });
      } catch (error) {
        alert(error.message);
      }
    };
    </script>
    <template>...</template>
    Copy after login

    注册完路由后,你可以访问/login 路由,看到一个漂亮的登录表单。

    // router/index.js
    const routes = [
        //...
        {
        name: "Login",
        path: "/login",
        component: () =&gt; import("@/pages/Login.vue"),
      },
    ]
    Copy after login

    ForgotPassword.vue

    由于我们在上面实现了一个忘记密码的链接,让我们也把这个页面用脚手架搭出来。

    在模板中,我们只需要一个带有单个电子邮件字段的表单,以便收集我们应该发送重置链接的电子邮件。

    <script>...</script>
    <template>
      <form class="max-w-lg m-auto" @submit.prevent="handlePasswordReset()">
        <h2 class="text-3xl mb-5">Forgot Password?</h2>
        <label>Email <input v-model="email" type="email" /></label>
        <button>Send Reset Email</button>
      </form>
    </template>
    Copy after login

    在脚本部分,我们将创建一个电子邮件反应式参考,以跟上表单中的电子邮件输入,并定义一个函数,在表单提交时调用AuthUser可组合的sendPasswordRestEmail 函数。

    <script setup>
    import useAuthUser from "@/composables/UseAuthUser";
    import { ref } from "vue";
    // use necessary composables
    const { sendPasswordRestEmail } = useAuthUser();
    // keep up with email
    const email = ref("");
    // function to call on submit of the form 
    // triggers sending the reset email to the user
    const handlePasswordReset = async () => {
      await sendPasswordRestEmail(email.value);
      alert(`Password reset email sent to: ${email.value}`);
    };
    </script>
    Copy after login

    最后,我们将注册这个路由。

    // router/index.js
    const routes = [
        //...
        {
        name: "ForgotPassword",
        path: "/forgotPassword",
        component: () => import("@/pages/ForgotPassword.vue"),
      },
    ]
    Copy after login

    现在,点击登录页面上的 "忘记密码?"链接将把你带到这里。

    Me.vue

    我们需要的最后一个页面是一个个人资料页面,在用户登录后显示他们的秘密信息。我们将它称为/me

    // router/index.js
    const routes = [
        //...
        {
        name: "Me",
        path: "/me",
        component: () =&gt; import("@/pages/Me.vue"),
      },
    ]
    Copy after login

    我们还将添加路由中间件,让我们的程序知道这应该是一个受保护的路由,只有通过认证的用户才能访问。

    {
      name: "Me",
        meta: {
            requiresAuth: true,
        },
        //...
    },
    Copy after login

    然后,为了让这个中间件真正发挥作用,我们需要这样来实现它。

    const router = createRouter({
      history: createWebHistory(),
      routes,
    });
    router.beforeEach((to) => {
        // here we check it the user is logged in
        // if they aren&#39;t and the route requries auth we redirect to the login page
      const { isLoggedIn } = useAuthUser();
      if (!isLoggedIn() && to.meta.requiresAuth) {
        return { name: "Login" };
      }
    });
    export default router;
    Copy after login

    页面本身将是一个简单的问候用户的信息。

    <script setup>
    import useAuthUser from "@/composables/UseAuthUser";
    const { user } = useAuthUser();
    </script>
    <template>
      <div v-if="user">
            <!--user_metadata is the key supabase nests all arbitrary meta data under-->
        <div>Hello {{ user.user_metadata.name }}</div>
      </div>
    </template>
    Copy after login

    我们现在还不会尝试查看这个页面,因为我们还没有实现登录。

    https://github.com/danielkellyio/supabase-auth-example

    现在,页面和AuthUser组合已经到位,我们可以开始通过调用Supabase SDK来填充AuthUser组合函数的内容。

    我们需要做的第一件事是访问我们的Supabase客户端。要实现这一点,我们可以导入UseSupabase可组合函数,并从中解构Supabase客户端。

    import useSupabase from "@/composables/UseSupabase";
    export default function useAuthUser() {
        const { supabase } = useSupabase();
        //...
    }
    Copy after login

    现在我们可以通过我们的空函数,一个一个地填充它们。

    login()

    为了登录Supabase,我们需要调用supabase.auth.signIn 。我们也可以等待响应,如果有错误就抛出一个新的错误,否则就返回登录的用户。

    const login = async ({ email, password }) => {
        const { user, error } = await supabase.auth.signIn({ email, password });
        if (error) throw error;
        return user;
    };
    Copy after login

    loginWithSocialProvider()

    loginWithSocialProvider也同样简单。只需要将提供者传递给signIn方法。

    const loginWithSocialProvider = async (token) => {
        const { user, error } = await supabase.auth.signIn({ provider });
        if (error) throw error;
        return user;
    };
    Copy after login

    logout()

    要注销当前用户,我们需使用Supabase的signOut方法。由于用户不再可用,所以没有什么可以从注销中返回。

    const logout = async () => {
      const { error } = await supabase.auth.signOut();
      if (error) throw error;
    };
    Copy after login

    isLoggedIn()

    对于isLoggedIn函数,我们只需检查reactive ref user是否有一个值。

    const isLoggedIn = () => {
        return !!user.value;
    };
    Copy after login

    如果你在想,我们在登录用户时从来没有设置过这个值,你是完全正确的,但我们将利用另一个小的supabase方法来帮助我们解决这个问题,就在一分钟之内。

    register()

    register函数与login函数几乎相同,都需要输入电子邮件和密码。然而,它也需要接受其他用户信息(即元数据)。我们将重定向到个人资料页面,并附带查询变量,包含一些有用信息。

    const register = async ({ email, password, ...meta }) => {
      const { user, error } = await supabase.auth.signUp(
        { email, password },
        { 
                //arbitrary meta data is passed as the second argument under a data key
                // to the Supabase signUp method
                data: meta,
                // the to redirect to after the user confirms their email
                // window.location wouldn&#39;t be available if we were rendering server side
                // but since we&#39;re all on the client it will work fine
          redirectTo: `${window.location.origin}/me?fromEmail=registrationConfirmation"`,
            }
      );
      if (error) throw error;
      return user;
    };
    Copy after login

    请注意这个很酷的小技巧,我们把meta:...meta 。这允许我们在调用函数时,在传递给函数的对象中提供同一层次的元数据,但在函数中单独访问它。

    // for example
    register({email: &#39;[email protected]&#39;, password: &#39;password123&#39;, name: &#39;Daniel Kelly&#39;, favoriteFood: &#39;Spaghetti&#39;})
    // meta will be {name: &#39;Daniel Kelly&#39;, favoriteFood: &#39;Spaghetti&#39;}
    Copy after login

    update()

    尽管我们没有提供更新用户接口,但是通过Supabase,我们可以轻松实现这个函数。

    const update = async (data) => {
        const { user, error } = await supabase.auth.update(data);
        if (error) throw error;
        return user;
    };
    Copy after login

    sendPasswordResetEmail()

    最后一个要实现的函数是sendPasswordResetEmail 。再一次,supabase有一个简单的解决方案。

    const sendPasswordRestEmail = async (email) => {
        const { user, error } = await supabase.auth.api.resetPasswordForEmail(email);
        if (error) throw error;
        return user;
    };
    Copy after login

    观察Auth状态的变化

    我们已经接近开始使用接口的阶段,但还需完成一个关键步骤。我们需要知道用户何时登录或注销,并相应地更新AuthUser组合中的反应式ref。

    有时候,在登录和注销方法中实现这个任务可能是可行的,这可能是你首先想到的解决方案。然而,如果用户因为会话过期而注销了呢?或者如果用户在Supabase那边被更新或删除了呢?在这两种情况下,我们的登录和注销方法都不会被调用。

    为了解决这个问题,Supabase提供了一个叫做onAuthStateChange 的函数。

    我们可以在我们的supabase组合中调用这个函数,让它监听auth状态的所有变化,然后相应地设置我们的用户reactive ref。

    // UseSupabase.js
    import { createClient } from "@supabase/supabase-js";
    import useAuthUser from "@/composables/UseAuthUser";
    // config
    const supabaseUrl = "";
    const supabaseKey = "";
    // setup client
    const supabase = createClient(supabaseUrl, supabaseKey);
    // ⬇ setup auth state listener ⬇
    supabase.auth.onAuthStateChange((event, session) => {
        // the "event" is a string indicating what trigger the state change (ie. SIGN_IN, SIGN_OUT, etc)
        // the session contains info about the current session most importanly the user dat
      const { user } = useAuthUser();
        // if the user exists in the session we&#39;re logged in
        // and we can set our user reactive ref
      user.value = session?.user || null;
    });
    // expose supabase client
    export default function useSupabase() {
      return { supabase };
    }
    Copy after login

    我选择在UseSupabase.js中的函数调用之外做这件事,这样它就只被调用一次,并与其他Supabase设置代码组织在一起。

    测试东西

    现在到了关键时刻。我们应该有大部分的工作。(尽管你马上就会看到,我们还需要再做一些调整)。在你的浏览器中导航到注册页面并注册。

    之后,你应该成功地被重定向到EmailConfirmation页面,你的电子邮件地址显示在信息中。

    另外,如果你检查你的收件箱,你会像预期那样收到电子邮件。

    顺便提一下,如果你想定制这封电子邮件的样子,你可以在Supabase仪表板上的Authentication > Templates

    另外,如果你在Authentication > Users ,你就可以看到你的新注册用户的状态是:Waiting for Verication!

    很好,现在去点击电子邮件中的那个链接吧。哦,天哪!我们被重新定向到了一个新的网站。我们被重定向到了登录页面......这不对。请注意,确实在标题右上方的链接上写有“注销”

    如果我们点击到me 页面,它将让我们访问它,并正确显示我们在注册表格中提供的名字。

    问题是,在我们点击页面的那一瞬间,我们的中间件正在运行,我们还没有完全登录,因此authStateChange还没有发生,还没有设置我们的用户反应式。

    让我们在我们的中间件中做一个例外,如果查询变量fromEmail存在,我们就继续让导航通过,因为我们知道,从确认邮件中来,用户将立即登录。

    router.beforeEach((to) => {
      const { isLoggedIn } = useAuthUser();
      if (
        !isLoggedIn() &&
        to.meta.requiresAuth &&
        !Object.keys(to.query).includes("fromEmail")
      ) {
        return { name: "Login" };
      }
    });
    Copy after login

    还要注意,这不是一个安全问题。如果有人在没有登录的情况下随意在查询字符串中包括fromEmail ,由于用户不存在,反正什么都不会显示出来,也不会从Supabase收到关于用户的信息。

    注销

    现在除了注销链接外,一切都应该正常了。通过为注销路由定义一个路由守卫并直接添加路由保护,我们可以使其正常工作。

    {
        name: "Logout",
        path: "/logout",
        beforeEnter: async () => {
          const { logout } = useAuthUser();
          await logout();
          return { name: "Home" };
        },
      },
    Copy after login

    注销后,如果你愿意,你可以尝试用不同的电子邮件再次注册,以确认我们上面的修复对直接导航到个人资料页面起作用。同样,当注销后,检查一下登录页面。你应该也能用现有的用户成功登录!

    The above is the detailed content of How to use Supabase Auth method in Vue3. For more information, please follow other related articles on the PHP Chinese website!

    Related labels:
    source:yisu.com
    Statement of this Website
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
    Popular Tutorials
    More>
    Latest Downloads
    More>
    Web Effects
    Website Source Code
    Website Materials
    Front End Template