📦

Next.jsのサイトにScratch認証機能を実装する

2024/12/01に公開

認証プロバイダーでは、Google, GitHub, Discord, Apple など有名なサービスが多いですが、一部のサービス(例えば Scratch)は公式な認証機能を提供していません。本記事では、Scratch のアカウント認証を実現するために、サードパーティのライブラリを用いた実装方法を解説します。

サイトを制作

サイトを制作していない場合は、サイトを制作してください。
https://nextjs.org/docs/app/getting-started/installation

開発環境

  • Next.js v15.0.2, App router

インストール

console
npm install @scratch-auth/nextjs

https://www.npmjs.com/package/@scratch-auth/nextjs

環境変数を設定する

暗号化キー SCRATCH_AUTH_SECRET_KEY を設定する必要があります。
このキーはセッションデータの暗号化と認証のために使用されます。

console
openssl rand -hex 32
env.local
SCRATCH_AUTH_SECRET_KEY=***************************************

パッケージ設定ファイル

項目 説明
COOKIE_NAME クッキーに保存される名前
redirect_url セッション認証の処理を実行するエンドポイントを指定する
title サイト名, 外部サイトの認証ページでタイトルとして表示されます。
expiration セッションの有効期限, 日数単位です。
cn CN 関数, パッケージが提供するコンポーネントUIの構築に必要です。
debug デバッグモード, ログをコンソールに出力したりします
scratch-auth.config.ts
import { ScratchAuthConfig } from "@scratch-auth/nextjs/src/types";
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

const config: ScratchAuthConfig = {
  COOKIE_NAME: "scratch-auth-session",
  redirect_url: `http://localhost:3000/api/auth`,
  title: `Scratch Auth`,
  expiration: 30,
  cn: function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
  },
  debug: true,
};

export default config;

セッション検証ページの追加

このページはセッションをデコードし、アカウント情報を検証します。このファイルを scratch-auth.config.ts で設定した redirect_url のパスに追加する必要があります。リダイレクトの間、セッションは privateCode パラメータに設定される。

setScratchAuthSessionprivateCodeを渡すことでパッケージがコードの有効性を確認します。もし成功した場合はクッキーにセッションが保存されます。

このページでは、リダイレクト先が / に固定されていますが、もし変更したい場合は、独自のリダイレクト機能を制作する必要があります。

app/api/auth/page.tsx
"use client";

import React, { useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { setScratchAuthSession } from "@scratch-auth/nextjs";

export default function AuthPage() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const privateCode = searchParams.get("privateCode");

  useEffect(() => {
    async function auth() {
      console.log(privateCode);
      const check = await setScratchAuthSession(privateCode);
      console.log("check", check);
      if (check) {
        console.log("認証に成功");
      } else {
        console.error("認証に失敗");
      }
      router.push("/");
    }
    auth();
  }, [privateCode]);

  return (
    <div className="flex justify-center items-center w-full h-full min-h-[calc(100dvh-64px)]">
      スクラッチアカウントの認証...
    </div>
  );
}

認証ボタンの追加

Scratch Authのビルド済みコンポーネントを使うことで、ログインしたユーザーとログアウトしたユーザーに表示されるコンテンツをコントロールすることができます。まず、ユーザーがログインまたはログアウトするためのヘッダーを作成します。これには以下を使用します:

  • <LogIned>を使用します: このコンポーネントの子コンポーネントは、ユーザがログインしているときのみ表示されます。
  • <LogOuted>: このコンポーネントの子コンポーネントは、ユーザがログアウトしたときのみ表示されます。
  • <UserButton />: ログインしているユーザのアカウントのアバターを表示するためのスタイルがあらかじめ用意されたコンポーネントです。
  • <LogInButton />: ログインページにリンクするスタイルのないコンポーネント。
app/page.tsx
import React from "react";
import {
  LogInButton,
  LogIned,
  LogOuted,
  UserButton,
} from "@scratch-auth/nextjs";

export default function Page() {
  return (
    <div>
      <LogOuted>
        <LogInButton />
      </LogOuted>
      <LogIned>
        <UserButton />
      </LogIned>
    </div>
  );
}

スクラッチアカウントの認証

以下のコマンドでプロジェクトを実行する:

console
npm run dev

http://localhost:3000 ↗ からウェブサイトにアクセスして、認証機能を確認してください。

リダイレクト機能の補足

Scratch Auth を利用してログイン後に希望のページにリダイレクトする機能を実装する方法を解説します。ScratchAuthLogin 関数を使用し、リダイレクト先をクッキーに保存するカスタムログイン関数を作成します。その後、認証ページでリダイレクト先の情報を取得し、指定のページに遷移させます。

カスタムログイン関数

以下のコードでは、ログイン前にリダイレクト先をクッキーに保存します。このクッキーは認証ページで読み取られます。

sah-sessionchange イベントについて:
このイベントは、パッケージが提供するログイン状態を再検証するための仕組みです。例えば、ログイン失敗時にリトライやセッション更新をトリガーする用途があります。

import { setCookie } from "cookies-next/client";
import {
  ScratchAuthLogin,
} from "@scratch-auth/nextjs";
const onScratchAuthLogin = async () => {
  setCookie("scratch-auth-redirect", "/dashboard");
  const success = await ScratchAuthLogin();
  if (!success) eventDispatch(new Event("sah-sessionchange"));
};

カスタムリダイレクト先

カスタムログイン関数で設定した、scratch-auth-redirect をクッキーから取得して、そのURLにリダイレクトするようにする。

src/app/api/auth/page.tsx
"use client";

import React, { useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { setScratchAuthSession } from "@scratch-auth/nextjs";
import { deleteCookie, getCookie } from "cookies-next/client";

export default function AuthPage() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const privateCode = searchParams.get("privateCode");

  useEffect(() => {
    async function auth() {
      console.log(privateCode);
      const check = await setScratchAuthSession(privateCode);
      console.log("check", check);
      if (check) {
        console.log("認証に成功しました");
      } else {
        console.error("認証に失敗しました");
      }
      const redirect = getCookie("scratch-auth-redirect");
      deleteCookie("scratch-auth-redirect");
      router.push(redirect || "/");
    }
    auth();
  }, [privateCode]);

  return (
    <div className="flex justify-center items-center w-full h-full min-h-[calc(100dvh-64px)]">
      Scratchアカウントを認証中...
    </div>
  );
}

Discussion