ユーザー登録の実装 (Svelte & Lucia)

このコードは、SvelteKitのサーバーサイド機能を活用して、ユーザー認証の処理を行うものです。Luciaを使用した認証システムを組み込み、ユーザー登録とリダイレクト処理を実装しています。

+page.server.ts: ユーザー登録とリダイレクト

load 関数: セッションの検証とリダイレクト

export const load = (async ({ locals }) => {
  const session = await locals.auth.validate();

  if (!session) return {};

  redirect(303, "/");
}) satisfies PageServerLoad;

解説

  1. locals.auth.validate() を用いて、現在のセッションが有効かどうかを検証します。
  2. セッションがない場合 {} を返し、ページのロードを許可します。
  3. セッションが有効なら、redirect(303, "/") でトップページにリダイレクトします。
  4. satisfies PageServerLoad; を使用して型の安全性を確保しています。

actions オブジェクト: ユーザー登録の処理

export const actions = {
  register: async ({ request }) => {
    const { username, password, name } = Object.fromEntries(
      await request.formData()
    ) as {
      username: string;
      password: string;
      name: string;
    };

    try {
      const user = await auth.createUser({
        key: {
          providerId: "username",
          providerUserId: username,
          password,
        },
        attributes: {
          name,
          username,
        },
      });
      console.log("user created", user);
    } catch (error) {
      console.error(error);
      return fail(400, { message: "That username already exists." });
    }

    redirect(303, "/login");
  },
} satisfies Actions;

解説

  1. request.formData() を使ってフォームデータを取得し、username, password, name を取得します。
  2. auth.createUser() を用いて新しいユーザーを作成します。
    • providerId: "username" はユーザー名を識別子として使用することを意味します。
    • providerUserId: username でユーザー名をセット。
    • password を保存(おそらく内部的にハッシュ化されます)。
    • attributesnameusername をセット。
  3. ユーザーの作成に失敗した場合(すでに存在するユーザー名など)、fail(400, { message: "That username already exists." }) を返してエラーメッセージを表示します。
  4. 成功した場合、redirect(303, "/login") でログインページへリダイレクトします。

+page.svelte: ページ側の実装

スクリプト部分

<script lang="ts">
  import { enhance } from "$app/forms";
  import type { ActionData } from "./$types";

  export let form: ActionData;
</script>

解説

  • enhance$app/forms からインポートし、フォーム送信を最適化。
  • form はサーバーから返される ActionData 型のエラーメッセージを格納。

フォーム部分(HTML)

<form class="bg-white shadow-md rounded-lg max-w-sm mx-auto mt-20 p-6" use:enhance action="?/register" method="post">
  <div class="mb-4">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="name">Name:</label>
    <input type="text" id="name" name="name" required />
  </div>
  
  <div class="mb-4">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="username">Username:</label>
    <input type="text" id="username" name="username" required />
  </div>

  <div class="mb-6">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="password">Password:</label>
    <input type="password" id="password" name="password" required />
  </div>

  <button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg transition-colors">
    REGISTER
  </button>

  {#if form?.message}
    <div class="mt-4 p-3 bg-red-100 text-red-700 rounded-lg">{form.message}</div>
  {/if}

  <p class="mt-4 text-center text-gray-600">
    Already have an account?
    <a href="/login" class="text-blue-500 hover:text-blue-700 font-bold">Login here.</a>
  </p>
</form>

解説

  • use:enhance により、フォーム送信が最適化される。
  • action="?/register"actions.register にフォームを送信。
  • method="post" によりPOSTリクエストで送信。
  • name, username, password の3つの入力フィールドを持つ。
  • required 属性で入力必須。
  • {#if form?.message} でエラーメッセージを動的に表示。
  • 既にアカウントを持っているユーザー向けのログインリンクを設置。

まとめ

  • load 関数でセッションを確認し、ログイン済みならリダイレクト。
  • actions.register でユーザー登録を処理し、エラー時には適切なメッセージを返す。
  • ページ側のフォームでは use:enhance を活用し、動的なエラーメッセージを表示。
  • redirect(303, "/login") で登録完了後にログインページへ遷移。