wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

NextAuth.jsでログイン画面をカスタマイズしてみる

NextAuth.jsでログイン画面をカスタマイズしてみたので記事にする

PR

github.com

github.com

実装

NextAuth.jsを使うと以下のような感じで簡単にログインを実装できる。

src/server/auth.ts

export const authOptions: NextAuthOptions = {
  providers: [
    DiscordProvider({
      clientId: env.DISCORD_CLIENT_ID,
      clientSecret: env.DISCORD_CLIENT_SECRET,
    }),
    AppleProvider({
      clientId: env.APPLE_ID,
      clientSecret: env.APPLE_SECRET,
    }),
    GoogleProvider({
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
    }),
  ],

ログインのプロバイダーは以下を参考に設定

next-auth.js.org

以下でsignInを参照すればログイン可能

import { signIn } from "next-auth/react";

<button onClick={() => signIn("credentials", {callbackUrl: "/",})}>
  ログイン
</button>

デフォルトで以下の画像のように表示される

これだけでも十分使用できるが、サービスのロゴや規約のリンクを表示させたい場合はカスタマイズが必要
その場合は以下を参考に実装可能

next-auth.js.org

実際にコード化してみると以下の通り

以下で表示するcomponentのpathを指定

src/server/auth.ts

export const authOptions: NextAuthOptions = {
  pages: {
    signIn: "/auth/signin",  // ← 追加
    error: "/auth/signin",    // ← 追加
  },

以下で実際のUIを作成

src/pages/auth/signin.tsx

import type {
  GetServerSidePropsContext,
  InferGetServerSidePropsType,
} from "next";
import { getProviders, signIn } from "next-auth/react";
import { getServerSession } from "next-auth/next";
import { authOptions } from "~/server/auth";
import { useRouter } from "next/router";
import SignInError from "~/features/auth/components/SignInError";

const authStyle: Record<string, { className: string; color: string }> = {
  Discord: {
    className: "bg-blue-600 text-white border border-blue-500",
    color: "blue",
  },
  GitHub: {
    className: "bg-gray-700 text-white border border-gray-700 ",
    color: "gray",
  },
  Google: {
    className: "bg-white text-black border border-black",
    color: "gray",
  },
  Apple: {
    className: "bg-black text-white border border-black",
    color: "gray",
  },
};

export default function SignIn({
  providers,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  const { error } = useRouter().query;

  return (
    <div>
      <div className="signin-bg fixed top-0 h-screen w-screen" />
      <div className="flex items-center justify-center">
        <div className="container z-10 flex h-screen flex-col items-center justify-center">
          <div className=" rounded-xl border bg-white px-20 py-4">
            <div>
              <div className="pb-10 pt-5 text-center">
                <div className="signin-mini-logo-title">
                  年間スケジュール、まとめるなら
                </div>
                <div className="signin-logo-title text-xl font-bold">
                  OOMAKA
                </div>
              </div>
              {error && (
                <div className="pb-6">
                  <SignInError error={String(error)}></SignInError>
                </div>
              )}
              <div className="flex flex-col items-center pb-10">
                {Object.values(providers ?? {}).map((provider) => {
                  const item = authStyle[String(provider?.name)];

                  return (
                    <div key={provider.name}>
                      <button
                        className={`my-3 w-72 rounded-lg px-4 py-2 font-bold ${String(
                          item?.className
                        )}`}
                        onClick={() => void signIn(provider.id)}
                      >
                        {provider.name} でログイン
                      </button>
                    </div>
                  );
                })}
              </div>
              <div className="pb-5 text-center text-sm text-gray-500">
                利用規約およびプライバシーポリシーに同意の上、
                <br />
                ログインへお進みください。
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

export async function getServerSideProps(context: GetServerSidePropsContext) {
  const session = await getServerSession(context.req, context.res, authOptions);
  if (session) {
    return { redirect: { destination: "/" } };
  }

  const providers = await getProviders();

  return {
    props: { providers: providers ?? [] },
  };
}

props.providersに設定したプロバイダーの設定が入っているので、この情報を使用してログイン画面を作成して最終的に以下のような表示になった

これでNextAuth.jsでログイン画面をカスタマイズ完了。それっぽく出来たので満足