Closed5

Next.jsとSupabaseの導入からGoogleログインまで

▲

Next.jsの環境構築

以下のコマンドでNext.jsの環境構築をする。
途中いろいろ聞かれるが、こだわりがなければデフォルトの選択肢で。

npx create-next-app@latest

環境構築が終わったら以下のコマンドで画面を確認

npm run dev

必要なライブラリをインストール

@supabase/ssr@supabase/supabase-js をインストールする

npm install @supabase/ssr @supabase/supabase-js
▲

Supabaseの設定

Supabaseでプロジェクトを作り、Project APIに書かれているProject URLとAPI Keyを取得する

Next.jsのプロジェクトルートに .env を作成し、Project URLとAPI Keyを書く

.env
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=xxxxxxxxxxxxxxxxx

GCP

プロジェクト作成

Google Cloud Platformにアクセスし、プロジェクトを作成する。

OAuth同意画面

「APIとサービス」 > 「OAuth同意画面」

必要な情報を入力。
「対象」は今回は「外部」を選択。
他の情報も入力したら登録する

OAuth クライアント ID の作成

必要な情報を入力。
「承認済みのリダイレクト URI」はsupabaseから取得する
「Authentication」> 「Sign in / Providers」> 「Google」を開き「Callback URL (for OAuth)」をコピーし、GCPの「承認済みのリダイレクト URI」に入力しOAuth クライアントを作成

Supabaseに情報を保存

OAuth クライアントを作成すると、「クライアント ID」と「クライアント シークレット」が発行されるので、それらをSupabaseの「Authentication」> 「Sign in / Providers」> 「Google」「Client IDs」と「Client Secret (for OAuth)」にそれぞれ保存する

▲

Supabaseクライアント

以下のファイルを作成しコードを書く

src/middleware.ts
import { type NextRequest } from "next/server";
import { updateSession } from "@/utils/supabase/middleware";

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: [
    /*
     * Match all request paths except:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * - images - .svg, .png, .jpg, .jpeg, .gif, .webp
     * Feel free to modify this pattern to include more paths.
     */
    "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
};
src/utils/supabaseClient.ts
import { createBrowserClient } from "@supabase/ssr";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const createClient = () =>
  createBrowserClient(supabaseUrl, supabaseAnonKey);
src/utils/supabaseServer.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

export const createClient = async () => {
  const cookieStore = await cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) => {
              cookieStore.set(name, value, options);
            });
          } catch (error) {
            console.log(error);
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  );
};
src/utils/supabase/middleware.ts
import { createServerClient } from "@supabase/ssr";
import { type NextRequest, NextResponse } from "next/server";

export const updateSession = async (request: NextRequest) => {
  // This `try/catch` block is only here for the interactive tutorial.
  // Feel free to remove once you have Supabase connected.
  try {
    // Create an unmodified response
    let response = NextResponse.next({
      request: {
        headers: request.headers,
      },
    });

    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          getAll() {
            return request.cookies.getAll();
          },
          setAll(cookiesToSet) {
            cookiesToSet.forEach(({ name, value }) =>
              request.cookies.set(name, value)
            );
            response = NextResponse.next({
              request,
            });
            cookiesToSet.forEach(({ name, value, options }) =>
              response.cookies.set(name, value, options)
            );
          },
        },
      }
    );

    // This will refresh session if expired - required for Server Components
    // https://supabase.com/docs/guides/auth/server-side/nextjs
    const user = await supabase.auth.getUser();

    // protected routes
    if (request.nextUrl.pathname.startsWith("/protected") && user.error) {
      return NextResponse.redirect(new URL("/sign-in", request.url));
    }

    if (request.nextUrl.pathname === "/" && !user.error) {
      return NextResponse.redirect(new URL("/dashboard", request.url));
    }

    return response;
    // eslint-disable-next-line
  } catch (e) {
    // If you are here, a Supabase client could not be created!
    // This is likely because you have not set up environment variables.
    // Check out http://localhost:3000 for Next Steps.
    return NextResponse.next({
      request: {
        headers: request.headers,
      },
    });
  }
};
▲

ログイン機能の実装

以下の様に修正する

src/app/page.tsx
"use client";
import { createClient } from "@/utils/supabaseClient";

export default function Home() {
  const handleGoogleSignIn = async () => {
    try {
      const supabase = await createClient();
      const { error } = await supabase.auth.signInWithOAuth({
        provider: "google",
        options: {
          redirectTo: `${location.origin}/auth/callback`,
        },
      });

      if (error) {
        console.error("Google auth error:", error.message);
      }
    } catch (error) {
      console.error("Google sign in error:", error);
    }
  };

  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <h1>welcome</h1>

      <button onClick={handleGoogleSignIn}>google login</button>
    </div>
  );
}

ログイン後のページとしてdashboardページを作る

src/app/dashboard/page.tsx
import { createClient } from "@/utils/supabaseServer";

const Dashboard = async () => {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  return (
    <div>
      ログイン後<div>{user?.email}</div>
    </div>
  );
};

export default Dashboard;

コールバック関数を作る

src/app/auth/callback/route.ts
import { NextResponse } from "next/server";
import { createClient } from "@/utils/supabaseServer";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/dashboard";

  if (code) {
    const supabase = await createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}
▲

ログイン

簡素な画面ですがログインボタンを押すと、googleログインの画面に切り替わりアカウント登録とログインができる
supabaseのユーザーページへ行くと、ログインしたユーザーの情報が追加されている。
また、一度ログインすると、indexページへ行っても /dashboard ページへリダイレクト

このスクラップは4ヶ月前にクローズされました