# 概要
@sidebase/nuxt-authは
nuxtJSにおいて様々の認証機構をサポートします。
認証情報の保存先をadaptorによって指定出来るようなので、今回はprismaを使ってgoogle認証を行ってみます。
※nuxtのプロジェクトは作成済みとします。
# prismaのインストール
npm
npm install prisma --save-dev
# prisma 初期化
npx prisma init
# schemaの編集
初期化が完了すると./prisma
フォルダが作成されます。
その中に./prisma/schema.prisma
があるのでそちらを編集します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
/// --- 以下追加 --
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
DB接続先の情報を.envファイルで設定できるのでそちらを編集します。
.env
# This was inserted by `prisma init`:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="mysql://<ユーザー名>:<パスワード>@db:3306/<db名称>?schema=public"
# テーブルの作成
schemaの情報をデータベースに反映させます。
npx prisma migrate dev
# @sidebase/nuxt-authの設定
モジュールをインストールします。
npm i -D @sidebase/nuxt-auth
注意点としてはyarn
でインストールする場合は、追加で以下のモジュールも必要になるようです。
yarn add next-auth@4.21.1
# nuxt.config.tsにModuleの追加
modules
に以下の情報を追加します。
export default defineNuxtConfig({
.
.
.
modules: [
"@sidebase/nuxt-auth",
],
.
.
.
});
# server側に認証用スクリプトを追加
server/api/auth/[...].ts
を追加します。
// file: ~/server/api/auth/[...].ts
import { NuxtAuthHandler } from "#auth";
import GoogleProvider from "next-auth/providers/google";
const config = useRuntimeConfig();
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default NuxtAuthHandler({
pages: {
signIn: "/login", // 用意したログインページに設定
},
secret: config.JWT_SECRET,
adapter: PrismaAdapter(prisma),
providers: [
// @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
GoogleProvider.default({
clientId: config.GOOGLE_CLIENT_ID!,
clientSecret: config.GOOGLE_CLIENT_SECRET!,
}),
],
session: {
strategy: "jwt",
},
callbacks: {
async session({ session, token }) {
return { user: { ...session.user, id: token.sub } };
},
},
});
サーバに送るトークンとしてsession:{ storategy:'jwt' }
JWTを指定しています。
また、GooglePrividerの設定としてGOOGLE_CLIENT_IDおよびGOOGLE_CLIENT_SECRET、JWT_SECRETをuseRuntimeConfig
から取得します。
callbacks:
で認証後に取得するsession情報を任意の情報に変更可能です。
標準ではsession情報にUserテーブルのid情報が無いようでしたのでこちらで追加します。
また、typeエラーを回避するためSessionの型を拡張します。
./types/next-auth.d.ts
declare module "next-auth" {
interface Session {
user: {
id: string | null;
email: string | null;
};
}
}
ID SERCRETの作成方法はこちらを参考にしてください。
JWT Secretはこちらから作成できます。
# runtimeConfigおよび.envの追加
./nuxt.config.ts
にruntimeConfigの情報を追加します。
export default defineNuxtConfig({
.
.
.
runtimeConfig: {
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
JWT_SECRET: process.env.JWT_SECRET
},
.
.
.
});
.env
に上記の情報を追加します。
GOOGLE_CLIENT_ID = <Googleの管理画面で作成したIDを指定>
GOOGLE_CLIENT_SECRET = <Googleの管理画面で作成したSECRETを指定>
JWT_SECRET = <作成したJWTSecretを指定>
# ログイン画面の作成
pages/login.vue
<template>
<div>
<button @click="signIn('google')">Googleでログインする</button>
</div>
</template>
<script setup lang="ts">
import { mdiGoogle } from "@mdi/js";
definePageMeta({
middleware: ["profile"],
});
const { signIn } = useAuth();
</script>
# profile middlewareについて
ログイン後にログイン画面に遷移した際にprofile画面に遷移するためのpluginを設定します。
※別の方法でもログイン後の遷移は可能な様ですが、上手くいかずのmiddlewareを作成しました。
frontnuxt/middleware/profile.ts
export default defineNuxtRouteMiddleware(() => {
const { status } = useAuth();
if (status.value === "authenticated") {
return navigateTo("/profile");
}
});
ログイン後に遷移するprofile.vueを作成します。
pages/profile.vue
<template>
<div>
<div>
<p>Logged In As:{{ data?.user?.email }}</p>
<button @click="() => signOut()">Sign Out</button>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
middleware: 'auth',
});
const { signOut, data } = useAuth();
const list = await getContentsByUserId();
</script>
# サーバ側での認証情報取得について
サーバ側のAPIにも認証後にアクセス可能にしたいケースがあるかと思いますが、
getServerSession
を使用することによってユーザーの情報を取得できます。
ただし、直リンクでpagesにアクセスした場合はsession情報はnullになるようでした。
(router.push('/')等でアクセスした場合は問題なくsession情報は取得出来るようです。)
server/api/profile/index.get.ts
import { getServerSession } from "#auth";
export default defineEventHandler(async (event) => {
const session = await getServerSession(event);
if (!session) {
throw createError({
statusCode: 401,
statusMessage: "not authorized",
});
}
return ...
});
# 所感
少し癖があり、設定だけで使用可能という手軽さではないですが、フルスクラッチで作る手間を考えれば、
大分軽減されるのではないかと思います。