Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

Upstash 블로그의 마지막 가이드가 Bytes 뉴스레터에 게재된 후 , 나는 우리가 SvelteKit 파티를 계속할 것이라고 생각했습니다.

Svelte의 열렬한 팬으로서 저는 매일 점점 더 많은 사람들이 탑승하는 모습을 보고 있으며 이는 미래에 대해 믿을 수 없을 만큼 기대됩니다.

여전히 주목받고 있는 도구 중 하나는 루시아입니다.

이 가이드에서는 Lucia로 인증을 시작하고 실행하는 방법을 보여 드리겠습니다. 데이터베이스 요구 사항에는 PlanetScale을 사용하고 세션 처리에는 Upstash Redis를 사용할 것입니다.

아래는 이 가이드의 최종 목표에 대한 스크린샷입니다. 여기에서 예제 저장소를 찾을 수 있습니다.

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

이 가이드는 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는 이러한 유형을 쿼리하는 방법을 알고 있습니다. 어댑터에는 두 가지 유형이 있습니다. 일반 어댑터 ​​및 세션 어댑터. 이 특정 가이드에서는 PlanetScale에 호스팅된 mySQL 데이터베이스를 사용하여 사용자를 저장하고 Upstash에 호스팅된 Redis 인스턴스를 사용하여 세션을 처리할 것입니다.

데이터베이스 어댑터를 구성하는 방법의 예는 다음과 같습니다.

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의 계정 및 데이터베이스.
  • Upstash Redis와 같은 Redis 인스턴스에 대한 액세스

시작하기

효율성을 위해 처음부터 전체 애플리케이션을 생성하지는 않을 것입니다.

대신 sveltekit-lucia-redis를 복제할 수 있습니다. 따라가려면 Upstash 예제 저장소의 디렉토리를 참조하세요.

저장소를 다운로드한 후 cd를 사용하여 애플리케이션으로 이동합니다. 명령을 실행하고 선호하는 패키지 관리자를 통해 종속성을 설치하고 .env를 설정합니다. .env.example을 복제하여 변수 .

핵심 부분 이해

다음은 중요한 모든 부분을 간략하게 요약한 것입니다.

  • src/lib/server/auth/index.ts - 여기가 Lucia를 구성하는 곳입니다.
  • src/lib/server/drizzle - Drizzle은 Drizzle Kit를 사용하여 PlanetScale에 편리하게 푸시할 수 있는 mySQL 스키마를 쉽게 생성하는 데 도움이 됩니다.
  • src/lib/server/planetscale - 사용자 관리를 위해 Lucia 어댑터 구성에서 사용하는 Upstash 클라이언트를 내보냅니다.
  • src/lib/server/upstash - 세션을 관리하기 위해 Lucia 어댑터 구성에서 사용하는 Upstash 클라이언트를 내보냅니다.

코드 분석

루시아 구성

가장 먼저 해야 할 일은 Lucia를 구성하는 것입니다. src/lib/server/auth/index.ts에 새 파일을 생성하면 됩니다. .

src/lib/server/auth/index.ts
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;

여기서 무슨 일이 일어나고 있는지 간략히 요약하면 다음과 같습니다.

lucia을 가져옵니다. lucia의 함수 Lucia에 대한 구성을 설정하는 패키지입니다.

가장 먼저 할 일은 adapter을 구성하는 것입니다. 재산. 여기서는 Lucia에게 사용자와 세션을 처리하는 방법을 알려주는 곳입니다.

세션 속성을 null로 설정했습니다. Redis를 사용하여 세션을 처리하려고 하기 때문입니다. 'session' 문자열을 사용하려는 경우 대신 Lucia는 사용자와 세션 모두에 동일한 어댑터를 사용합니다(이 문자열은 데이터베이스의 테이블에 해당합니다).

지금은 세션 어댑터에 대해 걱정하지 마세요. 이에 대해서는 나중에 다루겠습니다.

middleware에서 속성을 통해 Lucia에게 우리가 SvelteKit을 사용하고 있음을 알릴 수 있습니다. 이렇게 하면 Lucia가 요청 및 응답 개체를 읽을 수 있습니다.

내보낸 Auth를 기록해 두세요. 유형. 이것이 auth의 유형입니다. 개체. SvelteKit 로컬을 설정하려면 이 정보가 필요합니다.

Lucia로 훌륭한 유형 추론하기

Lucia는 TypeScript로 작성되었으므로 즉시 뛰어난 유형 추론을 얻을 수 있습니다. SvelteKit이 Auth에 대해 알고 있는지 확인합시다. 방금 만든 유형입니다.

app.d.ts를 열어보세요 파일을 만들고 다음을 추가하세요:

src/app.d.ts
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 {};

Auth을 추가하여 Lucia을 입력하세요. 네임스페이스를 사용하면 이제 auth에 액세스할 수 있습니다. locals의 객체 SvelteKit 경로의 객체입니다.

하지만 Lucia에서 가져온 모든 항목도 이제 올바른 유형을 갖게 됩니다.

이제 이러한 유형이 있으므로 hooks.server.ts을 설정할 수 있습니다. . 여기에 AuthRequest을 바인딩할 곳이 있습니다. 및 Session 현재 요청에 반대합니다.

이렇게 하면 locals를 통해 서버에서 쉽게 액세스할 수 있습니다. .

src/hooks.server.ts
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);

sequence도 가져옵니다. 이는 여러 후크를 순차적으로 실행할 수 있게 해주는 도우미 함수입니다. 이는 나중에 경로를 보호하려고 할 때 유용할 것입니다.

사용자 모델 생성

이제 Lucia가 구성되었으므로 사용자 모델을 만들 수 있습니다.

우리는 Drizzle ORM을 사용할 것입니다. 왜냐하면 이 모든 것이 현재 활발히 진행되고 있기 때문입니다.

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

그들의 밈이 딱 맞습니다. 이것 좀 보세요.

계속하기 전에 PlanetScale에 데이터베이스를 생성해야 합니다. 그리고 Drizzle 구성 파일을 설정하십시오. 이는 Drizzle CLI가 데이터베이스에 연결하는 데 도움이 됩니다.

drizzle.config.ts
import 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는 사용자 모델이 특정 구조를 가질 것으로 기대합니다. 이에 대한 자세한 내용은 문서에서 확인할 수 있습니다.

src/lib/server/drizzle/schema/index.ts에 다음 코드를 입력하세요. .

src/lib/server/drizzle/schema/index.ts
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),
 };
 },
);

그리고 pnpm drizzle-kit push:mysql을 실행하세요. 스키마를 PlanetScale로 푸시합니다.

짜잔! 이제 Lucia가 사용자를 관리하는 데 사용할 수 있는 사용자 모델이 생겼습니다.

세션 관리 설정

이제 사용자 모델이 있으므로 세션 관리를 설정할 수 있습니다.

Upstash Redis를 사용하여 세션을 처리하겠습니다. 여기에서 무료 계정에 가입하실 수 있습니다.

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

대시보드에 들어가면 새 데이터베이스를 만들고 환경 변수를 복사하기만 하면 됩니다.

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

이제 이 변수를 .env에 추가하세요. 파일. 그리고 src/lib/server/upstash/index.ts에 다음을 추가하세요. .

src/lib/server/upstash/index.ts
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를 구성할 때 session를 설정했습니다. null로 . 이는 Redis를 사용하여 세션을 처리하려고 하기 때문입니다. 현재 구성은 다음과 같습니다:

src/lib/server/auth/index.ts
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가 구성되었으므로 경로를 생성할 수 있습니다.

src/routes에 다음 폴더를 만듭니다. . 지금은 파일에 대해 걱정하지 마세요. 잠시 후에 파일을 살펴보겠습니다.

  • src/routes/auth
    • src/routes/auth/signin
    • src/routes/auth/signup
  • src/routes/app

도움말: 동일한 폴더 이름이나 그룹 아래에 특정 "기능"이 있으면 리디렉션을 수행하고 경로를 보호할 때 관리하기가 쉽습니다.

로그인 페이지 만들기

마지막으로 우리는 프론트 엔드 작업을 할 수 있습니다! 로그인 페이지부터 시작해 보겠습니다.

src/routes/auth/signin/+page.svelte에 새 파일을 만듭니다. . 지금은 스타일 지정을 생략하고 기능에 중점을 두겠습니다. 하지만 예제 저장소에서 전체 코드를 찾을 수 있습니다.

src/routes/auth/signin/+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 = '';
</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>

use:enhance 작업을 통해 양식이 점진적으로 향상되고 양식이 제출될 때 로드 상태를 표시할 수 있습니다. 여기에서 강화에 대한 자세한 내용을 읽어보실 수 있습니다.

나머지 코드는 설명이 매우 간단합니다.

로그인 요청 처리

SvelteKit을 사용하면 POST 요청을 매우 쉽게 처리할 수 있습니다. 우리가 해야 할 일은 +page.server.ts 파일을 만드는 것뿐입니다. +page.svelte과 같은 디렉토리에 파일을 만들고 actions 내보내기 최소한 default가 있는 객체 속성입니다.

src/routes/auth/signin/+page.server.ts
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 */
};

파일의 이 부분에 있는 핵심 요소를 살펴보겠습니다.

PROVIDER_ID을 가져왔습니다. 열거형 및 auth src/lib/server/auth에서 , 이전에 생성한 것입니다. 이 auth 개체에는 사용자와 세션을 관리하는 데 필요한 모든 방법이 포함되어 있습니다.

이제 actions를 살펴보겠습니다. 개체. 요청 개체에서 양식 데이터를 가져오고 기본적인 관리 작업을 수행할 수 있습니다.

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는 인증의 복잡성을 많이 추상화합니다. 우리가 해야 할 일은 올바른 메소드를 호출하는 것뿐입니다. 나머지는 Lucia가 처리할 것입니다.

Lucia, PlanetScale 및 Upstash Redis를 사용하는 SvelteKit의 안전하고 유형이 안전한 인증

비밀번호를 해시하거나 세션을 생성하거나 쿠키를 관리할 필요가 없습니다. 루시아는 우리를 위해 모든 일을 합니다. 그리고 모든 것이 유형에 안전합니다!

가입 페이지 만들기

src/routes/auth/signup/+page.svelte에 새 파일을 만듭니다. .

가입 페이지는 로그인 페이지와 매우 유사하므로 여기서는 별로 설명할 것이 없습니다. 유일한 차이점은 비밀번호 확인을 요청한다는 것입니다.

src/routes/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>

가입 요청 처리

src/routes/auth/signup/+page.server.ts에 새 파일을 만듭니다. .

가입을 위해서는 가입 양식과 유사한 패턴을 사용해야 하지만 양식에서 데이터 검색, 사용자 생성 및 기존 사용자 처리 지점까지의 필드 유효성 검사부터 시작하여 거의 차이가 없습니다.

가져오기는 로그인 페이지와 동일하므로 해당 부분을 건너뛰겠습니다.

요청 개체에서 양식 데이터를 가져오는 것부터 시작하고 몇 가지 기본적인 관리 작업을 수행합니다. 비밀번호가 일치하는지 확인하겠습니다.

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);

쉽게 피시 레몬 스퀴지!

보너스:로그아웃 페이지 만들기

그 동안 사용자를 로그아웃시키는 엔드포인트를 만들어 보겠습니다. src/routes/auth/signout/+server.ts에 새 파일을 만듭니다. .

그리고 다음 코드를 추가하세요:

src/routes/auth/signout/+server.ts
import { 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,
 });
};

다시 한 번 Lucia는 세션을 매우 쉽게 무효화할 수 있도록 지원해 줍니다. 이제 우리가 해야 할 일은 사용자가 로그아웃 버튼을 클릭할 때 이 엔드포인트를 호출하는 것뿐입니다.

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에 대해 더 자세히 알아보고 싶다면 문서를 확인하는 것이 좋습니다.

계속 진행하기 전에 Upstash Discord 커뮤니티에 들러 즐거운 시간을 보내시기 바랍니다. 또한 더 많은 SvelteKit 콘텐츠를 원하시면 여기 제 블로그에서 찾으실 수 있습니다.