本页还没有译文
本页 39 段还没译成简体中文,下面显示的是英文原文。Use Supabase Auth with Astro
还没翻译译自英文(supabase.com)本页 39 段都还没译
Quickstart
-
Create a new Supabase project
Head over to database.new and create a new Supabase project.
Your new database has a table for storing your users. You can see that this table is currently empty by running some SQL in the SQL Editor.
select * from auth.users; -
Create an Astro app
Create a new Astro app using the
npm createcommand.Explore drop-in UI components for your Supabase app.
UI components built on shadcn/ui that connect to Supabase via a single command.
npm create astro@latest my-app cd my-app -
Install Supabase libraries and Node adapter
Install the
@supabase/supabase-jsclient library,@supabase/ssrfor server-side auth, and the@astrojs/nodeadapter to enable server-side rendering.npm install @supabase/supabase-js @supabase/ssr @astrojs/node -
Configure Astro for SSR
Update your
astro.config.mjsto enable server-side rendering with the Node adapter.import { defineConfig } from "astro/config"; import node from "@astrojs/node"; export default defineConfig({ output: "server", adapter: node({ mode: "standalone", }), }); -
Declare Supabase Environment Variables
Create a
.env.localfile and populate with your Supabase connection variables:Project URL:
https://<project-ref>.supabase.co(find yours in the dashboard)Publishable key:
sb_publishable_...(find yours in the dashboard)PUBLIC_SUPABASE_URL=your-project-url PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_keyGet API details
To interact with data in database tables, you use the client libraries that wrap the auto-generated Data API endpoints, authenticating using the Project URL and key from the project Connect dialog.
Project URL:
https://<project-ref>.supabase.co(find yours in the dashboard)Publishable key:
sb_publishable_...(find yours in the dashboard)Note
See API keys for a full explanation of all key types, their uses, and where to find them.
-
Create a Supabase client helper
Create a utility file to initialize the Supabase client with SSR support:
import { createServerClient, parseCookieHeader } from "@supabase/ssr"; import type { AstroCookies } from "astro"; const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL const supabasePublishableKey = import.meta.env.PUBLIC_SUPABASE_PUBLISHABLE_KEY export function createClient({ request, cookies, }: { request: Request; cookies: AstroCookies; }) { return createServerClient( supabaseUrl, supabasePublishableKey, { cookies: { getAll() { return parseCookieHeader( request.headers.get("Cookie") ?? "" ); }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value, options }) => cookies.set(name, value, options) ); }, }, } ); } -
Create authentication actions
Create a new file at
src/actions/index.tsto define server-side authentication actions for signing up, signing in, and signing out:import { defineAction } from "astro:actions"; import { z } from "astro/zod"; import { createClient } from "../lib/supabase"; export const server = { signUp: defineAction({ accept: "form", input: z.object({ email: z.string().email(), password: z.string().min(6), }), handler: async (input, context) => { try { const supabase = createClient({ request: context.request, cookies: context.cookies, }); const { error } = await supabase.auth.signUp({ email: input.email, password: input.password, options: { emailRedirectTo: "http://localhost:4321/auth/callback", }, }); if (error) { return { success: false, message: error.message, }; } return { success: true, message: "Check your email to confirm your account", }; } catch (err) { return { success: false, message: "Unexpected error", }; } }, }), signIn: defineAction({ accept: "form", input: z.object({ email: z.string().email(), password: z.string(), }), handler: async (input, context) => { try { const supabase = createClient({ request: context.request, cookies: context.cookies, }); const { error } = await supabase.auth.signInWithPassword({ email: input.email, password: input.password, }); if (error) { return { success: false, message: error.message, }; } return { success: true, message: "Signed in successfully", }; } catch (err) { return { success: false, message: "Unexpected error", }; } }, }), signOut: defineAction({ handler: async (_, context) => { try { const supabase = createClient({ request: context.request, cookies: context.cookies, }); await supabase.auth.signOut(); return { success: true, }; } catch (err) { return { success: false, message: "Failed to sign out", }; } }, }), }; -
Customize email template
Before users can confirm their email, update the Supabase email template to send the token hash to your callback URL.
In your Supabase project dashboard:
- Go to Auth > Email Templates
- Select the Confirm signup template
- Change
{{ .ConfirmationURL }}to{{ .SiteURL }}/auth/callback?token_hash={{ .TokenHash }}&type=email. - Change your Site URL to
http://localhost:4321
{{ .SiteURL }}/auth/callback?token_hash={{ .TokenHash }}&type=email -
Create an auth callback page
Create a new file at
src/pages/auth/callback.astroto handle the email confirmation callback. Extract the token from the URL and verify it with Supabase:--- import { createClient } from "../../lib/supabase"; import type { EmailOtpType } from "@supabase/supabase-js"; const supabase = createClient({ request: Astro.request, cookies: Astro.cookies, }); const requestUrl = new URL(Astro.request.url); const token_hash = requestUrl.searchParams.get('token_hash'); const type = requestUrl.searchParams.get('type') as EmailOtpType | null; if (token_hash && type) { const { error } = await supabase.auth.verifyOtp({ token_hash, type, }); if (!error) { return Astro.redirect("/dashboard"); } } return Astro.redirect("/auth/signin"); --- <html> <head> <title>Email Confirmation</title> </head> <body> <p>Confirming your email...</p> </body> </html> -
Create a sign-up page
Create a new file at
src/pages/auth/signup.astrowith a sign-up form. Use a client-side event listener to handle form submission:--- import { createClient } from "../../lib/supabase"; const supabase = createClient({ request: Astro.request, cookies: Astro.cookies, }); const { data } = await supabase.auth.getUser(); if (data?.user) { return Astro.redirect("/dashboard"); } --- <html> <head> <title>Sign Up</title> </head> <body> <h1>Sign Up</h1> <div id="message"></div> <form id="signup-form"> <div> <label for="email">Email</label> <input id="email" type="email" name="email" placeholder="your@email.com" required /> </div> <div> <label for="password">Password</label> <input id="password" type="password" name="password" placeholder="At least 6 characters" required /> </div> <button type="submit" id="signup-btn">Sign Up</button> </form> <p> Already have an account? <a href="/auth/signin">Sign in</a> </p> </body> </html> <script> import { actions } from "astro:actions"; const form = document.querySelector("#signup-form") as HTMLFormElement; const btn = document.getElementById("signup-btn") as HTMLButtonElement; const messageEl = document.getElementById("message") as HTMLDivElement; form?.addEventListener("submit", async (e) => { e.preventDefault(); btn.disabled = true; btn.textContent = "Signing up..."; messageEl.textContent = ""; try { const formData = new FormData(form); const result = await actions.signUp(formData); if (!result.data?.success) { btn.disabled = false; btn.textContent = "Sign Up"; messageEl.textContent = result.data?.message || "Sign up failed"; messageEl.style.color = "red"; return; } messageEl.textContent = result.data.message; messageEl.style.color = "green"; btn.textContent = "Sign Up"; } catch (error) { btn.disabled = false; btn.textContent = "Sign Up"; messageEl.textContent = "An error occurred. Please try again."; messageEl.style.color = "red"; console.error(error); } }); </script> -
Create a sign-in page
Create a new file at
src/pages/auth/signin.astrowith a sign-in form. Use a client-side event listener to handle form submission:--- import { createClient } from "../../lib/supabase"; const supabase = createClient({ request: Astro.request, cookies: Astro.cookies, }); const { data } = await supabase.auth.getUser(); if (data?.user) { return Astro.redirect("/dashboard"); } --- <html> <head> <title>Sign In</title> </head> <body> <h1>Sign In</h1> <div id="message"></div> <form id="signin-form"> <div> <label for="email">Email</label> <input id="email" type="email" name="email" placeholder="your@email.com" required /> </div> <div> <label for="password">Password</label> <input id="password" type="password" name="password" placeholder="Your password" required /> </div> <button type="submit" id="signin-btn">Sign In</button> </form> <p> Don't have an account? <a href="/auth/signup">Sign up</a> </p> </body> </html> <script> import { actions } from "astro:actions"; const form = document.querySelector("#signin-form") as HTMLFormElement; const btn = document.getElementById("signin-btn") as HTMLButtonElement; const messageEl = document.getElementById("message") as HTMLDivElement; form?.addEventListener("submit", async (e) => { e.preventDefault(); btn.disabled = true; btn.textContent = "Signing in..."; messageEl.textContent = ""; try { const formData = new FormData(form); const result = await actions.signIn(formData); if (!result.data?.success) { btn.disabled = false; btn.textContent = "Sign In"; messageEl.textContent = result.data?.message || "Sign in failed"; messageEl.style.color = "red"; return; } // Redirect to dashboard on successful sign in window.location.href = "/dashboard"; } catch (error) { btn.disabled = false; btn.textContent = "Sign In"; messageEl.textContent = "An error occurred. Please try again."; messageEl.style.color = "red"; console.error(error); } }); </script> -
Create a dashboard page
Create a new file at
src/pages/dashboard.astroto display the authenticated user's information. Use a client-side event listener for the sign-out button:--- import { createClient } from "../lib/supabase"; const supabase = createClient({ request: Astro.request, cookies: Astro.cookies, }); const { data } = await supabase.auth.getUser(); const user = data?.user; if (!user) { return Astro.redirect("/auth/signin"); } --- <html> <head> <title>Dashboard</title> </head> <body> <h1>Welcome!</h1> <p>Email: {user.email}</p> <p>User ID: {user.id}</p> <button id="signout-btn">Sign Out</button> </body> </html> <script> import { actions } from "astro:actions"; const btn = document.getElementById("signout-btn") as HTMLButtonElement; btn?.addEventListener("click", async (e) => { e.preventDefault(); btn.disabled = true; btn.textContent = "Signing out..."; try { const result = await actions.signOut(); if (!result.data?.success) { btn.disabled = false; btn.textContent = "Sign Out"; alert("Failed to sign out"); return; } // Redirect to signin page window.location.href = "/auth/signin"; } catch (error) { btn.disabled = false; btn.textContent = "Sign Out"; console.error(error); } }); </script> -
Start the app
Start the development server, then navigate to http://localhost:4321/auth/signup to test the authentication.
npm run dev
Learn more
- Supabase Auth docs for more Supabase authentication methods