import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' import { FileTree } from '@astrojs/starlight/components';

Supabase — это альтернатива Firebase с открытым исходным кодом. Она предоставляет базу данных Postgres, аутентификацию, edge-функции, подписки в реальном времени и хранилище.

Инициализация Supabase в Astro

Необходимые условия

  • Проект Supabase. Если у вас его нет, вы можете бесплатно зарегистрироваться на сайте supabase.com и создать новый проект.
  • Проект Astro с включенным серверным рендерингом (SSR).
  • Учётные данные Supabase для вашего проекта. Вы можете найти их на вкладке Settings > API вашего проекта Supabase.
    • SUPABASE_URL: URL-адрес вашего проекта Supabase.
    • SUPABASE_ANON_KEY: Анонимный ключ для вашего проекта Supabase.

Добавление учётных данных Supabase

Чтобы добавить учётные данные Supabase в проект Astro, добавьте следующее в файл .env:

ini
SUPABASE_URL=ВАШ_SUPABASE_URLSUPABASE_ANON_KEY=ВАШ_SUPABASE_ANON_КЛЮЧ

Теперь эти переменные окружения доступны в вашем проекте.

Если вы хотите иметь IntelliSense для ваших переменных окружения, отредактируйте или создайте env.d.ts в вашем каталоге src/ и добавьте следующее:

ts
interface ImportMetaEnv {  readonly SUPABASE_URL: string  readonly SUPABASE_ANON_KEY: string}interface ImportMeta {  readonly env: ImportMetaEnv}

:::tip Подробнее о переменных окружения и файлах .env в Astro. :::

Теперь ваш проект должен включать эти файлы:

Установка зависимостей

Чтобы подключиться к Supabase, вам необходимо установить @supabase/supabase-js в ваш проект.

Далее создайте папку с именем lib в каталоге src/. В нее вы добавите клиент Supabase.

В supabase.ts добавьте следующее для инициализации клиента Supabase:

ts
import { createClient } from "@supabase/supabase-js";export const supabase = createClient(  import.meta.env.SUPABASE_URL,  import.meta.env.SUPABASE_ANON_KEY,);

Теперь ваш проект должен включать эти файлы:

Добавление аутентификации с помощью Supabase

Supabase обеспечивает аутентификацию из коробки. Она поддерживает аутентификацию по электронной почте/паролю и аутентификацию по протоколу OAuth со многими провайдерами, включая GitHub, Google и некоторые другие.

Необходимые условия

  • Проект Astro, инициализированный с Supabase.
  • Проект Supabase с включенной аутентификацией по электронной почте/паролю. Включить эту функцию можно на вкладке Authentication > Providers в настройках проекта.

Создание конечных точек сервера аутентификации

Чтобы добавить аутентификацию в ваш проект, вам нужно создать несколько конечных точек сервера. Эти конечные точки будут использоваться для регистрации, входа и выхода пользователей.

  • POST /api/auth/register: для регистрации нового пользователя.
  • POST /api/auth/signin: для авторизации пользователя.
  • GET /api/auth/signout: для выхода пользователя из системы.

Создайте эти конечные точки в директории src/pages/api/auth вашего проекта. Теперь ваш проект должен включать эти новые файлы:

register.ts создаёт нового пользователя в Supabase. Он принимает запрос POST с указанием электронной почты и пароля. Затем он использует Supabase SDK для создания нового пользователя.

ts
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";export const POST: APIRoute = async ({ request, redirect }) => {  const formData = await request.formData();  const email = formData.get("email")?.toString();  const password = formData.get("password")?.toString();  if (!email || !password) {    return new Response("Требуется ввести email и пароль", { status: 400 });  }  const { error } = await supabase.auth.signUp({    email,    password,  });  if (error) {    return new Response(error.message, { status: 500 });  }  return redirect("/signin");};

signin.ts авторизирует пользователя. Он принимает запрос POST с указанием электронной почты и пароля. Затем он использует Supabase SDK для регистрации пользователя.

ts
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";export const POST: APIRoute = async ({ request, cookies, redirect }) => {  const formData = await request.formData();  const email = formData.get("email")?.toString();  const password = formData.get("password")?.toString();  if (!email || !password) {    return new Response("Требуется ввести email и пароль", { status: 400 });  }  const { data, error } = await supabase.auth.signInWithPassword({    email,    password,  });  if (error) {    return new Response(error.message, { status: 500 });  }  const { access_token, refresh_token } = data.session;  cookies.set("sb-access-token", access_token, {    path: "/",  });  cookies.set("sb-refresh-token", refresh_token, {    path: "/",  });  return redirect("/dashboard");};

В signout.ts происходит выход пользователя из системы. Он принимает запрос GET и удаляет токены доступа и обновления пользователя.

ts
import type { APIRoute } from "astro";export const GET: APIRoute = async ({ cookies, redirect }) => {  cookies.delete("sb-access-token", { path: "/" });  cookies.delete("sb-refresh-token", { path: "/" });  return redirect("/signin");};

Создание страниц авторизации

Теперь, когда вы создали конечные точки сервера, создайте страницы, которые будут их использовать.

  • src/pages/register: содержит форму для регистрации нового пользователя.
  • src/pages/signin: содержит форму для авторизации пользователя.
  • src/pages/dashboard: содержит страницу, доступную только для авторизованных пользователей.

Создайте эти страницы в каталоге src/pages. Теперь ваш проект должен включать эти новые файлы:

register.astro содержит форму для регистрации нового пользователя. Она принимает электронную почту и пароль и отправляет POST-запрос на /api/auth/register.

astro
---import Layout from "../layouts/Layout.astro";---<Layout title="Регистрация">  <h1>Регистрация</h1>  <p>У вас уже есть аккаунт? <a href="/signin">Войти</a></p>  <form action="/api/auth/register" method="post">    <label for="email">Email</label>    <input type="email" name="email" id="email" />    <label for="password">Пароль</label>    <input type="password" name="password" id="password" />    <button type="submit">Войти</button>  </form></Layout>

В signin.astro содержится форма для авторизации пользователя. Она принимает электронную почту и пароль и отправляет POST запрос на /api/auth/signin. Она также проверяет наличие токенов доступа и обновления. Если они присутствуют, то происходит перенаправление на приборную панель.

astro
---import Layout from "../layouts/Layout.astro";const { cookies, redirect } = Astro;const accessToken = cookies.get("sb-access-token");const refreshToken = cookies.get("sb-refresh-token");if (accessToken && refreshToken) {  return redirect("/dashboard");}---<Layout title="Авторизация">  <h1>Авторизация</h1>  <p>Вы здесь впервые? <a href="/register">Создание учётной записи</a></p>  <form action="/api/auth/signin" method="post">    <label for="email">Email</label>    <input type="email" name="email" id="email" />    <label for="password">Пароль</label>    <input type="password" name="password" id="password" />    <button type="submit">Войти</button>  </form></Layout>

dashboard.astro содержит страницу, доступную только для авторизованных пользователей. Она проверяет наличие токенов доступа и обновления. Если их нет, она перенаправляет на страницу авторизации.

astro
---import Layout from "../layouts/Layout.astro";import { supabase } from "../lib/supabase";const { cookies, redirect } = Astro;const accessToken = cookies.get("sb-access-token");const refreshToken = cookies.get("sb-refresh-token");if (!accessToken || !refreshToken) {  return redirect("/signin");}const { data, error } = await supabase.auth.setSession({  refresh_token: refreshToken.value,  access_token: accessToken.value,});if (error) {  cookies.delete("sb-access-token", {    path: "/",  });  cookies.delete("sb-refresh-token", {    path: "/",  });  return redirect("/signin");}const email = data.user?.email;---<Layout title="dashboard">  <h1>Добро пожаловать, {email}</h1>  <p>Мы рады видеть вас здесь</p>  <form action="/api/auth/signout">    <button type="submit">Выйти</button>  </form></Layout>

Добавление аутентификации OAuth

Чтобы добавить OAuth-аутентификацию в ваш проект, вам нужно отредактировать клиент Supabase, чтобы включить поток аутентификации с помощью pkce. Подробнее о потоках аутентификации вы можете прочитать в документации Supabase.

ts
import { createClient } from "@supabase/supabase-js";export const supabase = createClient(  import.meta.env.SUPABASE_URL,  import.meta.env.SUPABASE_ANON_KEY,  {    auth: {      flowType: "pkce",    },  },);

Далее в приборной панели Supabase включите провайдер OAuth, который вы хотите использовать. Список поддерживаемых провайдеров можно найти на вкладке Authentication > Providers вашего проекта Supabase.

В следующем примере в качестве провайдера OAuth используется GitHub. Чтобы подключить свой проект к GitHub, выполните действия, описанные в документации Supabase.

Затем создайте новую конечную точку сервера для обработки обратного вызова OAuth в src/pages/api/auth/callback.ts. Эта конечная точка будет использоваться для обмена кода OAuth на токен доступа и обновления.

ts
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";export const GET: APIRoute = async ({ url, cookies, redirect }) => {  const authCode = url.searchParams.get("code");  if (!authCode) {    return new Response("No code provided", { status: 400 });  }  const { data, error } = await supabase.auth.exchangeCodeForSession(authCode);  if (error) {    return new Response(error.message, { status: 500 });  }  const { access_token, refresh_token } = data.session;  cookies.set("sb-access-token", access_token, {    path: "/",  });  cookies.set("sb-refresh-token", refresh_token, {    path: "/",  });  return redirect("/dashboard");};

Далее отредактируйте страницу входа и добавьте новую кнопку для входа с провайдером OAuth. Эта кнопка должна отправлять POST-запрос на /api/auth/signin с provider, установленным на имя OAuth-провайдера.

astro
---import Layout from "../layouts/Layout.astro";const { cookies, redirect } = Astro;const accessToken = cookies.get("sb-access-token");const refreshToken = cookies.get("sb-refresh-token");if (accessToken && refreshToken) {  return redirect("/dashboard");}---<Layout title="Авторизация">  <h1>Авторизация</h1>  <p>Вы здесь впервые? <a href="/register">Создать учётную запись</a></p>  <form action="/api/auth/signin" method="post">    <label for="email">Email</label>    <input type="email" name="email" id="email" />    <label for="password">Пароль</label>    <input type="password" name="password" id="password" />    <button type="submit">Авторизоваться</button>    <button value="github" name="provider" type="submit">Войти в систему через GitHub</button>  </form></Layout>

Наконец, отредактируйте конечную точку сервера входа для работы с провайдером OAuth. Если присутствует provider, он будет перенаправлять на провайдера OAuth. В противном случае он будет регистрировать пользователя с помощью электронной почты и пароля.

ts
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";import type { Provider } from "@supabase/supabase-js";export const POST: APIRoute = async ({ request, cookies, redirect }) => {  const formData = await request.formData();  const email = formData.get("email")?.toString();  const password = formData.get("password")?.toString();  const provider = formData.get("provider")?.toString();  const validProviders = ["google", "github", "discord"];  if (provider && validProviders.includes(provider)) {    const { data, error } = await supabase.auth.signInWithOAuth({      provider: provider as Provider,      options: {        redirectTo: "http://localhost:4321/api/auth/callback"      },    });    if (error) {      return new Response(error.message, { status: 500 });    }    return redirect(data.url);  }  if (!email || !password) {    return new Response("Необходимо указать электронную почту и пароль", { status: 400 });  }  const { data, error } = await supabase.auth.signInWithPassword({    email,    password,  });  if (error) {    return new Response(error.message, { status: 500 });  }  const { access_token, refresh_token } = data.session;  cookies.set("sb-access-token", access_token, {    path: "/",  });  cookies.set("sb-refresh-token", refresh_token, {    path: "/",  });  return redirect("/dashboard");};

После создания конечной точки обратного вызова OAuth и редактирования страницы входа и конечной точки сервера ваш проект должен иметь следующую структуру файлов:

Ресурсы сообщества