セッションと認証

認証はウェブアプリで非常に一般的な要件です。このレシピでは、Nuxtアプリで基本的なユーザー登録と認証を実装する方法を示します。

はじめに

このレシピでは、フルスタックのNuxtアプリで認証を設定します。Nuxt Auth Utilsこれは、クライアント側とサーバー側のセッションデータを管理するための便利なユーティリティを提供します。

このモジュールは、セッションデータを保存するために保護された密封型クッキーを使用するため、セッションデータを保存するためのデータベースを設定する必要はありません。

nuxt-auth-utils をインストールする

nuxt CLIを使用して、nuxt-auth-utilsモジュールをインストールします。

ターミナル
npx nuxt module add auth-utils
このコマンドは、nuxt-auth-utilsを依存関係としてインストールし、nuxt.config.tsmodulesセクションに追加します。

nuxt-auth-utilsはセッションデータを保存するために密封型クッキーを使用するため、セッションクッキーはNUXT_SESSION_PASSWORD環境変数からの秘密鍵を使用して暗号化されます。

設定されていない場合、この環境変数は開発モードで実行すると自動的に.envに追加されます。
.env
NUXT_SESSION_PASSWORD=a-random-password-with-at-least-32-characters
デプロイする前に、この環境変数を本番環境に追加する必要があります。

ログインAPIルート

このレシピでは、静的データに基づいてユーザーをサインインさせるための簡単なAPIルートを作成します。

リクエストボディにメールアドレスとパスワードを含むPOSTリクエストを受け付ける/api/login APIルートを作成しましょう。

server/api/login.post.ts
import { z } from 'zod'

const bodySchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
})

export default defineEventHandler(async (event) => {
  const { email, password } = await readValidatedBody(event, bodySchema.parse)

  if (email === '[email protected]' && password === 'iamtheadmin') {
    // set the user session in the cookie
    // this server util is auto-imported by the auth-utils module
    await setUserSession(event, {
      user: {
        name: 'John Doe',
      },
    })
    return {}
  }
  throw createError({
    statusCode: 401,
    message: 'Bad credentials',
  })
})
プロジェクトにzod依存関係をインストールしていることを確認してください (npm i zod)。
nuxt-auth-utilsによって公開されているsetUserSessionサーバーヘルパーについて詳しく読む。

ログインページ

このモジュールは、アプリケーションでユーザーが認証されているかどうかを知るためのVueコンポーザブルを公開します。

<script setup>
const { loggedIn, session, user, clear, fetch } = useUserSession()
</script>

/api/loginルートにログインデータを送信するためのフォームを持つログインページを作成しましょう。

app/pages/login.vue
<script setup lang="ts">
const { loggedIn, user, fetch: refreshSession } = useUserSession()
const credentials = reactive({
  email: '',
  password: '',
})
async function login () {
  try {
    await $fetch('/api/login', {
      method: 'POST',
      body: credentials,
    })

    // Refresh the session on client-side and redirect to the home page
    await refreshSession()
    await navigateTo('/')
  } catch {
    alert('Bad credentials')
  }
}
</script>

<template>
  <form @submit.prevent="login">
    <input
      v-model="credentials.email"
      type="email"
      placeholder="Email"
    >
    <input
      v-model="credentials.password"
      type="password"
      placeholder="Password"
    >
    <button type="submit">
      Login
    </button>
  </form>
</template>

APIルートを保護する

サーバーのルートを保護することは、データを安全に保つ上で非常に重要です。クライアント側のミドルウェアはユーザーにとって役立ちますが、サーバー側の保護がなければ、データは依然としてアクセスされる可能性があります。機密データを含むルートを保護することは不可欠であり、ユーザーがログインしていない場合は401エラーを返す必要があります。

auth-utilsモジュールは、ユーザーがログインしており、アクティブなセッションを持っていることを確認するのに役立つrequireUserSessionユーティリティ関数を提供します。

認証されたユーザーのみがアクセスできる/api/user/statsルートの例を作成しましょう。

server/api/user/stats.get.ts
export default defineEventHandler(async (event) => {
  // make sure the user is logged in
  // This will throw a 401 error if the request doesn't come from a valid user session
  const { user } = await requireUserSession(event)

  // TODO: Fetch some stats based on the user

  return {}
})

アプリのルートを保護する

サーバー側のルートが適切に設定されているため、データは安全ですが、他に何もせずに認証されていないユーザーが/usersページにアクセスしようとすると、おそらく奇妙なデータが表示されるでしょう。そのため、クライアント側のミドルウェアを作成して、クライアント側でルートを保護し、ユーザーをログインページにリダイレクトする必要があります。

nuxt-auth-utilsは、ユーザーがログインしているかを確認し、ログインしていない場合はリダイレクトするために使用する便利なuseUserSessionコンポーザブルを提供します。

/middlewareディレクトリにミドルウェアを作成します。サーバーとは異なり、クライアント側のミドルウェアはすべてのエンドポイントに自動的に適用されるわけではないため、適用したい場所を指定する必要があります。

app/middleware/authenticated.ts
export default defineNuxtRouteMiddleware(() => {
  const { loggedIn } = useUserSession()

  // redirect the user to the login screen if they're not authenticated
  if (!loggedIn.value) {
    return navigateTo('/login')
  }
})

ホームページ

ルートを保護するためのアプリミドルウェアができたので、認証されたユーザー情報を表示するホームページでそれを使用できます。ユーザーが認証されていない場合、ログインページにリダイレクトされます。

definePageMetaを使用して、保護したいルートにミドルウェアを適用します。

app/pages/index.vue
<script setup lang="ts">
definePageMeta({
  middleware: ['authenticated'],
})

const { user, clear: clearSession } = useUserSession()

async function logout () {
  await clearSession()
  await navigateTo('/login')
}
</script>

<template>
  <div>
    <h1>Welcome {{ user.name }}</h1>
    <button @click="logout">
      Logout
    </button>
  </div>
</template>

セッションをクリアし、ユーザーをログインページにリダイレクトするためのログアウトボタンも追加しました。

結論

Nuxtアプリで非常に基本的なユーザー認証とセッション管理を正常にセットアップしました。また、認証されたユーザーのみがアクセスできるように、サーバー側とクライアント側の機密ルートも保護しました。

次のステップとして、次のことができます。

オープンソースのatidoneリポジトリをチェックして、OAuth認証、データベース、CRUD操作を備えたNuxtアプリの完全な例を確認してください。