Open7

Next.js × Drizzle × Neon でGoogle認証を実装

おとのおとの

このスクラップでは、Next.js 14アプリケーションにおいて、DrizzleとNeon(PostgreSQL)を使用してGoogle認証を実装する方法をメモっていきます(あくまで個人開発目的)

技術スタック

  • Next.js AppRouter
  • NextAuth.js
  • Drizzle ORM
  • Neon (PostgreSQL)
  • TypeScript
おとのおとの

1. 環境構築

必要なパッケージのインストール

(Next.jsはインストールされている前提)

npm install next-auth @auth/drizzle-adapter drizzle-orm @neondatabase/serverless
npm install -D drizzle-kit

環境変数の設定

.envに以下の環境変数を設定します。

.env
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
DATABASE_URL=your_neon_database_url
NEXTAUTH_SECRET=your_nextauth_secret
NEXTAUTH_URL=http://localhost:3000 # 本番環境の場合は本番URLを設定
ALLOWED_EMAILS=allowed@example.com,another@example.com

Google Cloud側の設定

以下参考に進めます。

https://zenn.dev/farstep/books/nextjs-authjs-drizzle/viewer/authjs-setup#google-のクライアント-id-とクライアントシークレットの取得

おとのおとの

2. データベース接続・スキーマの設定

db/index.tsにNeonとDrizzleの接続設定を行います。

db/index.ts
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";

const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);

schema.tsにDrizzleのスキーマを定義します。

schema.ts
import { integer, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: text("id")
    .primaryKey()
    .$defaultFn(() => crypto.randomUUID()),
  name: text("name"),
  email: text("email").notNull(),
  emailVerified: timestamp("emailVerified", { mode: "date" }),
  image: text("image"),
});

export const accounts = pgTable("accounts", {
  id: serial("id").primaryKey(),
  userId: text("user_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  type: text("type").notNull(),
  provider: text("provider").notNull(),
  providerAccountId: text("provider_account_id").notNull(),
  refresh_token: text("refresh_token"),
  access_token: text("access_token"),
  expires_at: integer("expires_at"),
  token_type: text("token_type"),
  scope: text("scope"),
  id_token: text("id_token"),
  session_state: text("session_state"),
});

export const sessions = pgTable("sessions", {
  id: serial("id"),
  sessionToken: text("session_token").notNull().primaryKey(),
  userId: text("user_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  expires: timestamp("expires", { mode: "date" }).notNull(),
});

export const verificationTokens = pgTable("verification_tokens", {
  identifier: text("identifier").notNull(),
  token: text("token").notNull(),
  expires: timestamp("expires", { mode: "date" }).notNull(),
});

おとのおとの

3. 認証設定の実装

auth.tsで認証の設定を行います。

auth.ts
import NextAuth, { AuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/db";

export const authOptions: AuthOptions = {
  adapter: DrizzleAdapter(db),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async signIn({ user }) {
      // 許可されたメールアドレスのみログイン可能(今回のテスト開発に必要だったので実装)
      const allowedEmails = process.env.ALLOWED_EMAILS?.split(",") || [];
      return allowedEmails.includes(user.email ?? "");
    },
    // セッションにユーザーIDを追加
    async session({ session, user }) {
      if (session?.user) {
        session.user.id = user.id;
      }
      return session;
    },
  },
};
おとのおとの

4. APIルートの設定

app/api/auth/[...nextauth]/route.tsでAPIルートを設定します。

app/api/auth/[...nextauth]/route.ts
import { handler as authHandler } from "@/auth";
export { authHandler as GET, authHandler as POST };
おとのおとの

5. データベースのマイグレーション

マイグレーション設定

drizzle.config.tsを作成します。

drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});

package.jsonにscriptsを追加します。

package.json
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
+   "db:generate": "drizzle-kit generate",
+   "db:migrate": "drizzle-kit migrate"
  },

マイグレーション実行

マイグレーションファイルを生成します。

npm run db:generate

マイグレーションを実行します。

npm run db:migrate
おとのおとの

実際にページ側でどのように認証情報を扱っていくかは別途記載予定。