Setting Up Authentication in Astro Using Prisma and Planetscale

I've been wanting to add authentication to my personal website for a while now to see how it works in Astro. Since Prisma and PlanetScale have been asking for comments on my blog
, I've decided to store my account information in PlanetScale. Since it's just for my own account, and I'm not storing any other sensitive information in my database, I've decided to store the credentials in plain text for now. I changed my Prisma schema to make this possible:


 

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// schema.prisma

model Account {
  id Int @id @default(autoincrement())
  username String @unique
  password String
}
</code></span></span>

After updating the model in code, the run npx prisma db pushpropagates the changes to PlanetScale, so the schema is updated in the actual database.

I use an existing package @astro-authto handle all authentication on my website.
To do this, I need to add 2 environment variables to my application: ( ASTROAUTH_URLthe URL where my website is hosted) and ASTROAUTH_SECRET(an optional key).

Since I store the credentials in PlanetScale, I need to CredentialProviderenable login with username and password using .
There are many other providers available on @astro-auth, check out the packages if you are interested.
The code required to set it up @astro-authlooks like this:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// /pages/api/auth/[...astroauth].ts</span>

<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-name-color)">AstroAuth</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@astro-auth/core</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">CredentialProvider</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@astro-auth/providers</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">prisma</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">../../../lib/prisma</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>

<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">all</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">AstroAuth</span><span style="color:var(--syntax-text-color)">({</span>
  <span style="color:var(--syntax-name-color)">authProviders</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span>
    <span style="color:var(--syntax-name-color)">CredentialProvider</span><span style="color:var(--syntax-text-color)">({</span>
      <span style="color:var(--syntax-name-color)">authorize</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-name-color)">properties</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">account</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">prisma</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">account</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">findFirst</span><span style="color:var(--syntax-text-color)">({</span>
          <span style="color:var(--syntax-name-color)">where</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
            <span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">,</span>
            <span style="color:var(--syntax-name-color)">AND</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
              <span style="color:var(--syntax-name-color)">password</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">password</span><span style="color:var(--syntax-text-color)">,</span>
            <span style="color:var(--syntax-text-color)">},</span>
          <span style="color:var(--syntax-text-color)">},</span>
          <span style="color:var(--syntax-name-color)">select</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
            <span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">,</span>
          <span style="color:var(--syntax-text-color)">},</span>
        <span style="color:var(--syntax-text-color)">});</span>
        <span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">account</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
          <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">;</span>
        <span style="color:var(--syntax-text-color)">}</span>
        <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">;</span>
      <span style="color:var(--syntax-text-color)">},</span>
    <span style="color:var(--syntax-text-color)">}),</span>
  <span style="color:var(--syntax-text-color)">],</span>
<span style="color:var(--syntax-text-color)">});</span>
</code></span></span>

Creating a landing page is very easy.
I just created a form, called the signIn()method on submit @astro-auth, and BOOM: logged in!
Code for login page:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// /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>
</code></span></span>

After submitting the form, the user is logged in and redirected to the home page. Securing a page with authentication is easy, just use a function from
to check for a logged in user. Here's an example of a page I use this check for:getUser()@astro-auth

 

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// /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>
</code></span></span>

If the user is not logged in, the user will be redirected to the home page with a 307 status code.
I also have an API route to delete comments on my blog posts, which I want to isolate so only authenticated users can use this API. It is also possible
to use getUser()the function from for this, but this time we will pass instead of an object. Example using this code:@astro-authrequestAstro

 

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// /pages/api/comments.ts</span>

<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">del</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">APIRoute</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">request</span> <span style="color:var(--syntax-text-color)">})</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
  <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">user</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">getUser</span><span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">server</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">request</span> <span style="color:var(--syntax-text-color)">});</span>
  <span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">user</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">body</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">json</span><span style="color:var(--syntax-text-color)">();</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">deleteComment</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">prisma</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">comment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">delete</span><span style="color:var(--syntax-text-color)">({</span>
      <span style="color:var(--syntax-name-color)">where</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
        <span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">body</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">,</span>
      <span style="color:var(--syntax-text-color)">},</span>
    <span style="color:var(--syntax-text-color)">});</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Response</span><span style="color:var(--syntax-text-color)">(</span>
      <span style="color:var(--syntax-name-color)">JSON</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">stringify</span><span style="color:var(--syntax-text-color)">({</span>
        <span style="color:var(--syntax-name-color)">message</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">`Comment with id </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">deleteComment</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)"> deleted`</span><span style="color:var(--syntax-text-color)">,</span>
      <span style="color:var(--syntax-text-color)">}),</span>
      <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">status</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">200</span> <span style="color:var(--syntax-text-color)">}</span>
    <span style="color:var(--syntax-text-color)">);</span>
  <span style="color:var(--syntax-text-color)">}</span>
  <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Response</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">status</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">403</span> <span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">};</span>
</code></span></span>

So when the user is not authenticated, a 403 response will be returned.

Hope this helps you! The source code can, as always, be found on my Github
.

Guess you like

Origin blog.csdn.net/jascl/article/details/131304357