使用Prisma和Planetscale在Astro中设置身份验证
#javascript #网络开发人员 #prisma #astro

我一直想在我的个人网站上添加身份验证一段时间以查看其在Astro中的工作方式。
随着Prisma和Planetscale已经竞选comments on my blogs,我决定将我的帐户信息存储在Planetscale中。
因为它只是用于我自己的帐户,而且我没有在数据库中存储任何其他敏感信息,所以我决定暂时将凭据存储。
我更改了Prisma模式以使其成为可能:

// schema.prisma

model Account {
  id Int @id @default(autoincrement())
  username String @unique
  password String
}

一旦模型在代码中更新,运行npx prisma db push就会传播到行星尺度的更改,因此在实际数据库中更新了架构。

我使用了一个名为@astro-auth的现有软件包来处理我的网站上的所有身份验证。
为此,我需要在我的应用程序中添加2个环境变量:ASTROAUTH_URL(我的网站托管了URL)和ASTROAUTH_SECRET(一个自我选择的秘密键)。

因为我将凭据存储在Planetscale中,所以我需要使用CredentialProvider启用用户名和密码登录。
@astro-auth上还有许多其他提供商,如果您有兴趣,请查看包裹。
使用@astro-auth进行设置所需的代码看起来像这样:

// /pages/api/auth/[...astroauth].ts

import AstroAuth from '@astro-auth/core';
import { CredentialProvider } from '@astro-auth/providers';
import { prisma } from '../../../lib/prisma';

export const all = AstroAuth({
  authProviders: [
    CredentialProvider({
      authorize: async properties => {
        const account = await prisma?.account.findFirst({
          where: {
            username: properties.username,
            AND: {
              password: properties.password,
            },
          },
          select: {
            id: true,
          },
        });
        if (account?.id) {
          return properties.username;
        }
        return null;
      },
    }),
  ],
});

创建登录页面非常简单。
我刚刚创建了一个表单,从提交时调用signIn()方法,然后登录boom:登录!
登录页面的代码:

// /pages/login.astro

<html>
  <head>
    <title>Login</title>
    <script>
      import { signIn } from '@astro-auth/client';

      document.addEventListener('DOMContentLoaded', () => {
        document.querySelector('form')?.addEventListener('submit', async e => {
          e.preventDefault();
          const form = e.target;
          if (form) {
            const formData = new FormData(form as HTMLFormElement);
            const data = Object.fromEntries(formData);
            await signIn({
              provider: 'credential',
              login: data,
            });
            window.location.href = '/';
          }
        });
      });
    </script>
  </head>
  <body>
    <form>
      <label for="username">Name</label>
      <input type="text" name="username" />

      <label for="password">Password</label>
      <input type="password" name="password" />

      <input type="submit" value="Submit" />
    </form>
  </body>
</html>

提交表格后,用户登录并重定向到主页。
使用身份验证保护页面很容易,只需检查@astro-authgetUser()函数登录的用户。
这是我使用此检查的页面的示例:

// /pages/comment-overview.astro

---
import { getUser } from '@astro-auth/core';
import Layout from '../layouts/Layout.astro';
import { prisma } from '../lib/prisma';
import CommentsOverviewWrapper from '../components/CommentOverviewWrapper';
const user = getUser({ client: Astro });
if (!user) {
  return Astro.redirect('/', 307);
}
const commentsWithPost = await prisma?.comment.findMany({
  include: {
    post: {
      select: {
        url: true,
      },
    },
  },
});
---

<Layout
  description="Overview of comments"
  title="Thomas Ledoux | Comment overview"
>
  <CommentsOverviewWrapper commentsWithPost={commentsWithPost} client:load />
</Layout>

如果未登录用户,则用户将使用307状态代码重定向到主页。
我也有一条API路由来删除博客文章的评论,我想围栏,因此只有身份验证的用户才能使用此API。
也可以为此使用getUser()函数,但是这次我们将通过request而不是Astro对象。
使用此代码的示例:

// /pages/api/comments.ts

export const del: APIRoute = async ({ request }) => {
  const user = getUser({ server: request });
  if (user) {
    const body = await request.json();
    const deleteComment = await prisma?.comment.delete({
      where: {
        id: body.id,
      },
    });
    return new Response(
      JSON.stringify({
        message: `Comment with id ${deleteComment?.id} deleted`,
      }),
      { status: 200 }
    );
  }
  return new Response(null, { status: 403 });
};

因此,当未对用户进行身份验证时,将返回403响应。

希望这很有帮助!
源代码可以像往常一样在my Github上找到。