หลังจากคำแนะนำล่าสุดของเราในบล็อก Upstash ก็ติดอันดับจดหมายข่าว Bytes ฉันคิดว่าเราจะจัดปาร์ตี้ SvelteKit ต่อไป
ในฐานะแฟนตัวยงของ Svelte ฉันเห็นผู้คนกระโดดขึ้นเครื่องมากขึ้นทุกวัน และมันทำให้ฉันตื่นเต้นอย่างไม่น่าเชื่อสำหรับอนาคต
เครื่องมืออย่างหนึ่งที่ยังคงมองไม่เห็นคือลูเซีย
ในคู่มือนี้ ฉันจะแสดงให้คุณเห็นถึงวิธีการเริ่มต้นและใช้งานการตรวจสอบสิทธิ์กับ Lucia เราจะใช้ PlanetScale สำหรับความต้องการฐานข้อมูลของเรา และใช้ Upstash Redis เพื่อจัดการเซสชัน
ด้านล่างนี้คือภาพหน้าจอของเป้าหมายสุดท้ายของเราสำหรับคำแนะนำนี้ คุณสามารถค้นหาพื้นที่เก็บข้อมูลตัวอย่างได้ที่นี่

คู่มือนี้จะอยู่ใน SvelteKit แต่เนื่องจาก Lucia รองรับเฟรมเวิร์กใดๆ ก็ตาม คู่มือนี้ส่วนใหญ่จึงสามารถนำไปใช้กับเฟรมเวิร์กยอดนิยมใดๆ ที่มีอยู่ได้อย่างง่ายดาย
ลูเซียคืออะไร
พูดง่ายๆ ก็คือ Lucia เป็นไลบรารีสำหรับ TypeScript ที่ทำให้การจัดการผู้ใช้และเซสชันเป็นเรื่องง่าย เดิมที ไลบรารีนี้ถูกสร้างขึ้นสำหรับ SvelteKit แต่มีการพัฒนาอย่างต่อเนื่อง และตอนนี้มีความหลากหลายเพียงพอที่จะเล่นได้ดีกับเฟรมเวิร์กใดๆ ก็ตาม
สิ่งที่ยอดเยี่ยมมากเกี่ยวกับ Lucia คือการที่มันจัดเตรียมทุกสิ่งที่คุณต้องการในการจัดการความซับซ้อนของการรับรองความถูกต้องโดยไม่กระทบต่อประสบการณ์ของผู้ใช้ คิดว่า Lucia เป็นชุดพื้นฐาน — ขึ้นอยู่กับคุณว่าคุณต้องการจัดโครงสร้างโค้ดและจัดการกับประสบการณ์ผู้ใช้อย่างไร Lucia มีส่วนสำคัญบางประการที่สำคัญที่ต้องทำความเข้าใจ:
มิดเดิลแวร์ อนุญาตให้ Lucia อ่านคำขอและการตอบกลับสำหรับเฟรมเวิร์กและรันไทม์ที่แตกต่างกัน
ต่อไปนี้คือตัวอย่างวิธีกำหนดค่ามิดเดิลแวร์:
import { lucia } from "lucia";
import { node } from "lucia/middleware";
// import { nextjs } from "lucia/middleware";
// import { h3 } from "lucia/middleware";
export const auth = lucia({
env: "DEV", // "PROD" if deployed to HTTPS
middleware: node(),
}); อะแดปเตอร์ฐานข้อมูล อนุญาตให้ Lucia จัดเก็บและเรียกข้อมูลผู้ใช้และเซสชัน ด้วยการจัดเตรียมอะแดปเตอร์ Lucia รู้วิธีสืบค้นประเภทเหล่านี้ อะแดปเตอร์มี 2 ประเภท; อะแดปเตอร์ทั่วไปและอะแดปเตอร์เซสชัน ในคำแนะนำเฉพาะนี้ เราจะใช้ฐานข้อมูล mySQL ที่โฮสต์บน PlanetScale เพื่อจัดเก็บผู้ใช้ของเรา และอินสแตนซ์ Redis ที่โฮสต์บน Upstash เพื่อจัดการเซสชัน
ต่อไปนี้คือตัวอย่างวิธีกำหนดค่าอะแดปเตอร์ฐานข้อมูล:
import { prisma } from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
import { lucia } from "lucia";
const client = new PrismaClient();
const auth = lucia({
env: "DEV",
adapter: prisma(client),
}); ด้วยข้อมูลเบื้องหลังนั้น เรามาเริ่มกันเลยดีกว่า
ข้อกำหนดเบื้องต้น
หากต้องการเริ่มต้นใช้งานแอปและปฏิบัติตามสิ่งที่คุณต้องการ:
- ความเข้าใจพื้นฐานของ SvelteKit การจัดการแบบฟอร์มและการกำหนดเส้นทางจะพิจารณาเป็นพิเศษ
- ความคุ้นเคยพื้นฐานกับ Drizzle ORM
- บัญชีและฐานข้อมูลบน PlanetScale
- เข้าถึงอินสแตนซ์ Redis เช่น Upstash Redis
เริ่มต้นใช้งาน
เพื่อประสิทธิภาพ เราจะไม่สร้างแอปพลิเคชันทั้งหมดตั้งแต่ต้น
คุณสามารถโคลน 00 แทนได้ ไดเร็กทอรีจาก repo ตัวอย่าง Upstash ที่จะปฏิบัติตาม
หลังจากดาวน์โหลดพื้นที่เก็บข้อมูลแล้ว ให้ไปที่แอปพลิเคชันโดยใช้ 19 สั่งและติดตั้งการพึ่งพาผ่านตัวจัดการแพ็คเกจที่คุณต้องการและตั้งค่า 23 ตัวแปรโดยการทำซ้ำ 32 .
การทำความเข้าใจส่วนสำคัญ
ต่อไปนี้คือบทสรุปโดยย่อของส่วนสำคัญทั้งหมด
47รหัส> - นี่คือที่ที่เรากำหนดค่า Lucia51รหัส> - Drizzle ช่วยให้เราสร้างสคีมา mySQL ได้อย่างง่ายดาย ซึ่งเราสามารถพุชไปยัง PlanetScale ได้อย่างสะดวกโดยใช้ Drizzle Kit65รหัส> - ส่งออก Upstash Client ที่เราใช้ในการกำหนดค่าอะแดปเตอร์ Lucia เพื่อจัดการผู้ใช้77รหัส> - ส่งออก Upstash Client ที่เราใช้ในการกำหนดค่าอะแดปเตอร์ Lucia เพื่อจัดการเซสชัน
การแยกย่อยโค้ด
การกำหนดค่าลูเซีย
สิ่งแรกที่เราต้องทำคือกำหนดค่าลูเซีย เราสามารถทำได้โดยสร้างไฟล์ใหม่ใน 86 .
import { planetscale } from "@lucia-auth/adapter-mysql";
import { dev } from "$app/environment";
import { lucia } from "lucia";
import { sveltekit } from "lucia/middleware";
import { ps } from "../planetscale";
export enum PROVIDER_ID {
EMAIL = "email",
}
export const auth = lucia({
adapter: {
user: planetscale(ps, {
user: "users",
key: "keys",
/**
* Sessions are handled by Upstash Redis.
*/
session: null,
}),
},
middleware: sveltekit(),
env: dev ? "DEV" : "PROD",
getUserAttributes: (data) => {
return {
userId: data.id,
email: data.email,
};
},
});
export type Auth = typeof auth; ต่อไปนี้เป็นข้อมูลสรุปโดยย่อของสิ่งที่เกิดขึ้นที่นี่:
เรานำเข้า 98 ฟังก์ชั่นจาก 109 แพ็คเกจเพื่อตั้งค่าการกำหนดค่าสำหรับ Lucia
สิ่งแรกที่เราทำคือกำหนดค่า 112 ทรัพย์สิน นี่คือที่ที่เราบอก Lucia ถึงวิธีจัดการกับผู้ใช้และเซสชัน
เราตั้งค่าคุณสมบัติเซสชันเป็น 126 เพราะเราต้องการใช้ Redis เพื่อจัดการเซสชัน หากคุณจะใช้สตริง 136 ที่นี่แทน Lucia จะใช้อะแดปเตอร์เดียวกันสำหรับทั้งผู้ใช้และเซสชัน (สตริงเหล่านี้สอดคล้องกับตารางในฐานข้อมูลของคุณ)
ไม่ต้องกังวลเกี่ยวกับอะแดปเตอร์เซสชันในตอนนี้ เราจะมาพูดถึงเรื่องนั้นในภายหลัง
ใน 148 คุณสมบัติที่เราสามารถแจ้งให้ Lucia ทราบว่าเรากำลังใช้ SvelteKit สิ่งนี้จะทำให้ Lucia สามารถอ่านออบเจ็กต์คำขอและการตอบกลับได้
จดบันทึก 150 ที่ส่งออก ประเภท นี่คือประเภท 169 ของเรา วัตถุ เราจำเป็นต้องใช้สิ่งนี้เพื่อตั้งค่า SvelteKit ในพื้นที่
รับการอนุมานประเภทที่ยอดเยี่ยมกับ Lucia
Lucia เขียนด้วย TypeScript ดังนั้นคุณจึงสามารถอนุมานประเภทที่ยอดเยี่ยมได้ทันที มาทำให้แน่ใจว่า SvelteKit รู้เกี่ยวกับ 174 ประเภทที่เราเพิ่งสร้างขึ้น
เปิด 187 ของคุณ ไฟล์และเพิ่มสิ่งต่อไปนี้:
import type { Auth as LuciaAuth } from "$lib/server/auth";
import type { AuthRequest, Session, User } from "lucia";
declare global {
namespace App {
// interface Error {}
interface Locals {
auth: AuthRequest;
session: Session | null;
}
interface PageData {
user?: User;
}
// interface Platform {}
}
}
/// <reference types="lucia" />
declare global {
namespace Lucia {
type Auth = LuciaAuth;
type DatabaseUserAttributes = {
email: string;
};
type DatabaseSessionAttributes = {};
}
}
export {};
โดยการเพิ่ม 191 พิมพ์เป็น 205 เนมสเปซ ตอนนี้เราสามารถเข้าถึง 219 แล้ว วัตถุจาก 227 วัตถุในเส้นทาง SvelteKit ของเรา
แต่ทุกสิ่งที่นำเข้าจาก Lucia ก็จะมีประเภทที่ถูกต้องเช่นกัน
ตอนนี้เรามีประเภทเหล่านี้แล้ว เราก็สามารถตั้งค่า 235 ได้ . นี่คือที่ที่เราจะผูก 241 และ 253รหัส> คัดค้านคำขอปัจจุบัน
ซึ่งจะทำให้เข้าถึงได้ง่ายบนเซิร์ฟเวอร์ผ่าน 263 .
import type { Handle } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";
import { auth } from "$lib/server";
const auth_handle: Handle = async ({ event, resolve }) => {
event.locals.auth = auth.handleRequest(event);
event.locals.session = await event.locals.auth.validate();
return resolve(event);
};
export const handle = sequence(auth_handle);
เราจะนำเข้า 273 ด้วย ซึ่งเป็นฟังก์ชันตัวช่วยที่ช่วยให้เราสามารถรัน hooks หลายอันตามลำดับได้ ซึ่งจะมีประโยชน์ในภายหลังเมื่อเราพยายามปกป้องเส้นทางของเรา
การสร้างโมเดลผู้ใช้
ตอนนี้เราได้กำหนดค่า Lucia แล้ว เราก็สามารถสร้างโมเดลผู้ใช้ของเราได้
เราจะใช้ Drizzle ORM เนื่องจากตอนนี้อากาศร้อนและกำลังเกิดขึ้น

และมีมของพวกเขาก็ตรงประเด็น เพียงแค่ดูอันนี้ ป>
ก่อนดำเนินการต่อ คุณต้องสร้างฐานข้อมูลบน PlanetScale ก่อน และตั้งค่าไฟล์กำหนดค่า Drizzle สิ่งนี้จะช่วยให้ Drizzle CLI เชื่อมต่อกับฐานข้อมูลของคุณ
ละอองฝน.config.tsimport dotenv from "dotenv";
import type { Config } from "drizzle-kit";
dotenv.config();
const username = process.env.DATABASE_USERNAME;
const password = process.env.DATABASE_PASSWORD;
const host = process.env.DATABASE_HOST;
const db = process.env.DATABASE_NAME;
const connectionString = `mysql://${username}:${password}@${host}/${db}?ssl={"rejectUnauthorized":true}`;
export default {
schema: "./src/lib/server/drizzle/schema/index.ts",
driver: "mysql2",
dbCredentials: {
connectionString: connectionString,
},
} satisfies Config; เนื่องจากเราใช้อะแดปเตอร์ mySQL Lucia จึงคาดว่าโมเดลผู้ใช้ของเราจะมีโครงสร้างเฉพาะ คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในเอกสาร
วางโค้ดต่อไปนี้ใน 284 .
import { relations } from "drizzle-orm";
import {
bigint,
datetime,
index,
int,
mysqlEnum,
mysqlTable,
timestamp,
unique,
varchar,
} from "drizzle-orm/mysql-core";
export const users = mysqlTable(
"users",
{
id: varchar("id", { length: 255 }).primaryKey(),
createdAt: timestamp("createdAt").defaultNow().onUpdateNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
email: varchar("email", { length: 191 }).notNull(),
},
(table) => {
return {
idIdx: index("users_id_idx").on(table.id),
userIdKey: unique("users_id_key").on(table.id),
};
},
);
export const keys = mysqlTable(
"keys",
{
id: varchar("id", { length: 255 }).primaryKey(),
hashedPassword: varchar("hashed_password", { length: 255 }),
userId: varchar("user_id", { length: 255 }).notNull(),
},
(table) => {
return {
userIdIdx: index("keys_user_id_idx").on(table.userId),
keyIdKey: unique("keys_id_key").on(table.id),
};
},
);
และเรียกใช้ 291 เพื่อส่งสคีมาไปที่ PlanetScale
เอาล่ะ! ตอนนี้คุณมีโมเดลผู้ใช้ที่ Lucia สามารถใช้จัดการผู้ใช้ได้
การตั้งค่าการจัดการเซสชัน
ตอนนี้เรามีโมเดลผู้ใช้แล้ว เราก็สามารถตั้งค่าการจัดการเซสชันได้
เราจะใช้ Upstash Redis เพื่อจัดการเซสชัน คุณสามารถลงทะเบียนเพื่อรับบัญชีฟรีได้ที่นี่

เมื่อคุณอยู่ในแดชบอร์ดแล้ว สิ่งที่คุณต้องทำคือสร้างฐานข้อมูลใหม่และคัดลอกตัวแปรสภาพแวดล้อม

ตอนนี้เพิ่มตัวแปรเหล่านี้ใน 300 ของคุณ ไฟล์. และเพิ่มสิ่งต่อไปนี้ใน 311 .
import { Redis } from "@upstash/redis";
import {
UPSTASH_REDIS_REST_TOKEN,
UPSTASH_REDIS_REST_URL,
} from "$env/static/private";
export const upstashClient = new Redis({
url: UPSTASH_REDIS_REST_URL,
token: UPSTASH_REDIS_REST_TOKEN,
});
โปรดจำไว้ว่าเมื่อเรากำหนดค่า Lucia เราได้ตั้งค่า 327 เป็น 339 . เนื่องจากเราต้องการใช้ Redis เพื่อจัดการเซสชัน นี่คือลักษณะการกำหนดค่าของเราตอนนี้:
import { planetscale } from "@lucia-auth/adapter-mysql";
import { upstash } from "@lucia-auth/adapter-session-redis";
import { dev } from "$app/environment";
import { lucia } from "lucia";
import { sveltekit } from "lucia/middleware";
import { ps } from "../planetscale";
import { upstashClient } from "../upstash";
export enum PROVIDER_ID {
EMAIL = "email",
}
export const auth = lucia({
adapter: {
user: planetscale(ps, {
user: "users",
key: "keys",
session: null,
}),
// Instruct Lucia to use Upstash Redis for sessions
session: upstash(upstashClient),
},
middleware: sveltekit(),
env: dev ? "DEV" : "PROD",
getUserAttributes: (data) => {
return {
userId: data.id,
email: data.email,
};
},
});
export type Auth = typeof auth; โชคดีสำหรับเราที่ Lucia มีอะแดปเตอร์ Upstash Redis อยู่ในกล่อง ดังนั้นสิ่งที่เราต้องทำคือนำเข้าและส่งต่อไปยังไคลเอ็นต์ Upstash
ตอนนี้มันง่ายมาก!
การสร้างเส้นทาง
ตอนนี้เราได้กำหนดค่า Lucia แล้ว เราก็สามารถสร้างเส้นทางของเราได้
สร้างโฟลเดอร์ต่อไปนี้ใน 349 . ไม่ต้องกังวลกับไฟล์ใดๆ ในตอนนี้ เราจะพูดถึงไฟล์เหล่านั้นในอีกสักครู่
352รหัส>363รหัส>374รหัส>
382รหัส>
เคล็ดลับ: การมี "คุณสมบัติ" บางอย่างภายใต้ชื่อโฟลเดอร์หรือกลุ่มเดียวกันทำให้ง่ายต่อการจัดการเมื่อทำการเปลี่ยนเส้นทางและปกป้องเส้นทาง
การสร้างหน้าลงชื่อเข้าใช้
ในที่สุด เราก็สามารถทำงานส่วนหน้าได้! มาเริ่มกันที่หน้าลงชื่อเข้าใช้
สร้างไฟล์ใหม่ใน 397 . ฉันจะละเว้นการออกแบบในตอนนี้และมุ่งเน้นไปที่ฟังก์ชันการทำงาน — แต่คุณสามารถดูโค้ดแบบเต็มได้ในที่เก็บตัวอย่าง
<script lang="ts">
import { enhance } from '$app/forms';
import { Button, Input, Label, PasswordInput } from '$lib/components/common';
import type { ActionData } from './$types';
export let form: ActionData;
let loading = false;
let email = '';
let password = '';
</script>
<form
method="POST"
use:enhance={() => {
loading = true;
return async ({ update }) => {
loading = false;
update();
};
}}
>
{#if form && form.error}
<div class="p-2 mb-4 text-sm text-center text-red-900 bg-red-200 rounded-sm">
Error: {form.error}
</div>
{/if}
<div class="grid gap-2.5">
<div class="grid gap-1">
<Label for="email">Email</Label>
<Input
bind:value={email}
name="email"
placeholder="Email"
type="email"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
required={true}
/>
</div>
<div class="grid gap-1">
<Label for="password">Password</Label>
<PasswordInput name="password" placeholder="Password" bind:value={password} />
</div>
<div class="mt-6 col-span-full">
<Button type="submit" class="w-full" disabled={loading}>
{#if loading}
Loading...
{:else}
Sign in
{/if}
</Button>
</div>
</div>
</form>
400รหัส> การดำเนินการจะปรับปรุงแบบฟอร์มอย่างต่อเนื่อง และช่วยให้เราแสดงสถานะการโหลดเมื่อมีการส่งแบบฟอร์ม คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการปรับปรุงได้ที่นี่
โค้ดที่เหลือค่อนข้างอธิบายได้ในตัว
การจัดการคำขอลงชื่อเข้าใช้
SvelteKit ทำให้การจัดการคำขอ POST เป็นเรื่องง่ายอย่างเหลือเชื่อ สิ่งที่เราต้องทำคือสร้างไฟล์ 418 ในไดเรกทอรีเดียวกันกับ 425 ไฟล์และส่งออก 432 วัตถุที่มีอย่างน้อย 442 ทรัพย์สิน
import { fail, redirect } from "@sveltejs/kit";
import { auth, PROVIDER_ID } from "$lib/server";
import { LuciaError } from "lucia";
import type { Actions, PageServerLoad } from "./$types";
export const actions = {
/* our actions here */
}; มาสำรวจองค์ประกอบสำคัญในส่วนนี้ของไฟล์กันดีกว่า:
เราได้นำเข้า 456 แล้ว แจงนับและ 462 จาก 473 ซึ่งเราสร้างไว้ก่อนหน้านี้ 485รหัส> นี้ object มีวิธีการทั้งหมดที่เราต้องการในการจัดการผู้ใช้และเซสชัน
ตอนนี้เรามาดูที่ 497 วัตถุ เราสามารถรับข้อมูลแบบฟอร์มจากออบเจ็กต์คำขอ และดูแลทำความสะอาดขั้นพื้นฐาน
actions = {
default: async ({ request, locals }) => {
const formData = await request.formData();
const email = formData.get("email") as string;
const password = formData.get("password") as string;
const fields = [email, password];
if (fields.some(field => !field)) {
return fail(400, {
error: "All fields are required"
});
} ต่อไปเราจะลองลงชื่อเข้าใช้ผู้ใช้ หากไม่มีผู้ใช้อยู่หรือรหัสผ่านไม่ถูกต้อง Lucia จะแจ้งข้อผิดพลาด เราได้เตรียมพร้อมสำหรับเหตุการณ์นี้โดยการตรวจจับข้อผิดพลาดและส่งคืนการตอบกลับ 400 รายการพร้อมกับข้อความแสดงข้อผิดพลาด
try {
const user = await auth.useKey(PROVIDER_ID.EMAIL, email.toLowerCase(), password);
const session = await auth.createSession({
userId: user.userId,
attributes: {}
});
locals.auth.setSession(session);
} catch (err) {
if (
err instanceof LuciaError &&
(err.message === 'AUTH_INVALID_KEY_ID' || err.message === 'AUTH_INVALID_PASSWORD')
) {
return fail(400, {
error: 'Incorrect username of password'
});
}
return fail(400, {
error: 'An unknown error occurred'
});
} หากมีผู้ใช้อยู่และรหัสผ่านถูกต้อง เราจะสร้างเซสชันใหม่
และสุดท้าย เราจะเปลี่ยนเส้นทางผู้ใช้ไปยังแดชบอร์ด
return redirect('/app'); ดังที่คุณคงทราบได้ว่า Lucia ขจัดความซับซ้อนในการรับรองความถูกต้องออกไปมากมาย สิ่งที่เราต้องทำคือเรียกวิธีการที่ถูกต้อง แล้วลูเซียจะจัดการส่วนที่เหลือ

ไม่จำเป็นต้องแฮชรหัสผ่าน สร้างเซสชัน หรือจัดการคุกกี้ ลูเซียทำทุกอย่างเพื่อเรา และปลอดภัยทุกประเภท!
การสร้างหน้าลงทะเบียน
สร้างไฟล์ใหม่ใน 501 .
หน้าลงทะเบียนจะคล้ายกับหน้าลงชื่อเข้าใช้มาก ดังนั้นจึงไม่มีอะไรจะอธิบายมากนัก ข้อแตกต่างเพียงอย่างเดียวคือเราขอให้ยืนยันรหัสผ่าน
src/เส้นทาง/auth/signup/+page.svelte<script lang="ts">
import { enhance } from '$app/forms';
import { Button, Input, Label, PasswordInput } from '$lib/components/common';
import type { ActionData } from './$types';
export let form: ActionData;
let loading = false;
let email = '';
let password = '';
let passwordConfirmation = '';
</script>
<form
method="POST"
use:enhance={() => {
loading = true;
return async ({ update }) => {
loading = false;
update();
};
}}
>
{#if form && form.error}
<div class="p-2 mb-4 text-sm text-center text-red-900 bg-red-200 rounded-sm">
Error: {form.error}
</div>
{/if}
<div class="grid gap-2.5">
<div class="grid gap-1">
<Label for="email">Email</Label>
<Input
bind:value={email}
name="email"
placeholder="Email"
type="email"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
required={true}
/>
</div>
<div class="grid gap-1">
<Label for="password">Password</Label>
<PasswordInput name="password" placeholder="Password" bind:value={password} />
</div>
<div class="grid gap-1">
<Label for="passwordConfirmation">Confirm password</Label>
<PasswordInput
name="passwordConfirmation"
placeholder="Repeat password"
bind:value={passwordConfirmation}
/>
</div>
<div class="mt-6 col-span-full">
<Button type="submit" class="w-full" disabled={loading}>
{#if loading}
Loading...
{:else}
Sign in
{/if}
</Button>
</div>
</div>
</form> การจัดการคำขอลงทะเบียน
สร้างไฟล์ใหม่ใน 514 .
สำหรับการสมัครสมาชิก เราจะต้องใช้รูปแบบที่คล้ายกันกับแบบฟอร์มลงทะเบียนของเรา แต่จะมีความแตกต่างเล็กน้อยตั้งแต่การดึงข้อมูลจากแบบฟอร์ม การตรวจสอบความถูกต้องของฟิลด์จนถึงจุดสร้างผู้ใช้ และการจัดการผู้ใช้ที่มีอยู่แล้ว
การนำเข้าจะเหมือนกับหน้าลงชื่อเข้าใช้ ดังนั้นเราจะข้ามส่วนนั้นไป
เราจะเริ่มต้นด้วยการรับข้อมูลแบบฟอร์มจากออบเจ็กต์คำขอ และดำเนินการดูแลทำความสะอาดขั้นพื้นฐาน เราจะตรวจสอบด้วยว่ารหัสผ่านตรงกันหรือไม่
const formData = await request.formData();
const email = formData.get('email') as string;
const password = formData.get('password') as string;
const passwordConfirmation = formData.get('passwordConfirmation') as string;
const fields = [email, password, passwordConfirmation];
if (fields.some((field) => !field)) {
return fail(400, {
error: 'All fields are required'
});
}
if (password !== passwordConfirmation) {
return fail(400, {
error: 'Passwords do not match'
});
} ต่อไป เราจะพยายามค้นหาผู้ใช้ทางอีเมลโดยใช้ Drizzle ORM หากมีผู้ใช้อยู่ เราจะตอบกลับ 400 รายการพร้อมกับข้อความแสดงข้อผิดพลาด
try {
const user = await db.query.users.findFirst({
where: eq(schema.users.email, email.toLowerCase()),
});
if (user) {
return fail(400, {
error: "User with this email already exists"
});
} หากไม่มีผู้ใช้ เราจะสร้างผู้ใช้ใหม่โดยใช้ Lucia
const newUser = await auth.createUser({
key: {
providerId: PROVIDER_ID.EMAIL,
providerUserId: email.toLowerCase(),
password: password,
},
attributes: {
email,
},
}); และสุดท้าย เราจะสร้างเซสชันใหม่และเปลี่ยนเส้นทางผู้ใช้ไปยังแดชบอร์ด
const session = await auth.createSession({
userId: newUser.userId,
attributes: {},
});
locals.auth.setSession(session); บีบมะนาวพีซี่ง่ายๆ!
โบนัส:การสร้างหน้าออกจากระบบ
ในระหว่างนี้ เรามาสร้างจุดสิ้นสุดเพื่อออกจากระบบผู้ใช้กันดีกว่า สร้างไฟล์ใหม่ใน 521 .
และเพิ่มโค้ดต่อไปนี้:
src/routes/auth/signout/+server.tsimport { auth } from "$lib/server";
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ locals }) => {
const session = await locals.auth.validate();
if (!session) {
return new Response(null, {
status: 400,
});
}
// Invalidate session or alternatively, you can delete all sessions: await auth.invalidateAllUserSessions(session.userId);
await auth.invalidateSession(session.sessionId);
// Remove the cookie.
locals.auth.setSession(null);
return new Response(null, {
status: 200,
});
}; เป็นอีกครั้งที่ลูเซียสนับสนุนเราด้วยการทำให้เซสชันโมฆะเป็นเรื่องง่ายอย่างเหลือเชื่อ สิ่งที่เราต้องทำตอนนี้คือเรียกตำแหน่งข้อมูลนี้เมื่อผู้ใช้คลิกปุ่มออกจากระบบ
src/routes/app/+page.svelte<script lang="ts">
import { goto } from '$app/navigation';
import { Button } from '$lib/components/common';
async function handleSignOut() {
const response = await fetch('/auth/signout', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
goto('/auth/signin', {
replaceState: true,
invalidateAll: true
});
}
}
</script>
<Button on:click={handleSignOut}>Sign out</Button> แค่สรุป
เราสร้างการตรวจสอบสิทธิ์แบบปลอดภัยกับ Lucia, PlanetScale และ Upstash Redis สำเร็จแล้ว และเราเพิ่งเริ่มต้นสิ่งที่ Lucia สามารถทำได้เท่านั้น
อีกครั้ง คุณสามารถค้นหา repo สำหรับคำแนะนำนี้ได้ที่นี่ หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Lucia ฉันขอแนะนำให้ตรวจสอบเอกสาร
ก่อนที่คุณจะไปต่อ คุณควรออกไปเที่ยวในชุมชน Upstash Discord — เรากำลังมีช่วงเวลาดีๆ อยู่ และหากคุณต้องการเนื้อหา SvelteKit เพิ่มเติม คุณสามารถค้นหาได้จากบล็อกของฉันที่นี่