8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クラベスAdvent Calendar 2024

Day 13

【Next.js 15 ☓ Clerk】もう悩まない!認証機能を5分で開発する方法

Posted at

はじめに

今回ですが、Next.js 15とClerkという認証系SaaSを使用して、5分で認証機能を開発していこうと思います。
ハンズオンでも進められる内容になっているので、試してみたい方は事前にClerkにて、アカウント登録を行っておいてください。

今回のソースですが、以下から閲覧可能です。

Clerkとは

Clerkは認証系の機能を提供してくれる認証系のSaaSです。
認証に必要なものは、ほぼすべて提供してくるため、認証の実装を簡単に記述できたり、Clerk内で、認証ユーザーの管理を行えたりと、面倒かつ煩雑な認証機能を丸投げすることができます。

私が使用してみて感じたメリットは以下です。

  • 認証用画面をコンポーネントで提供してくるため、認証画面はコピペで作成できる(細かいスタイリングは必要)
  • ドキュメントに沿うだけで認証機能作れる(ほぼコピペでできます)
  • OAuth(GitHubやGoogle認証など)はClerk側で自動で行ってくれる
    • 自分でGCPやGitHubのsercret keyやcallback_uriの設定や発行が不要
  • ユーザー情報の管理機能まで提供される(Clerkダッシュボード(管理画面)でもできますし、ユーザー自身が行うこともできます)
  • email・passwordの二段階認証のUIまである
    • メール送信ももちろん行われる
  • 公式ドキュメントにはAI検索機能が搭載(これからはこういう公式ドキュメントが増えそう)

上記を自力で実装しようとすると、かなり大変だと思いますが、Clerkを使えば、これらの複雑な認証機能を5分で作れます。

アプリケーションのセットアップ

以下コマンドを実行します。
私はBunを使っていこうと思います。

bunx  create-next-app@latest --typescript --tailwind

各オプションはデフォルトでよいですが、私の場合はESLintを使わずbiomeを使うため、そこだけNoを選択しました。

Clerkのセットアップ

まず、Clerk上でapplicationの作成を行っていきます。
使用したいSign in optionsにをONにするだけで、OAuthの認証機能を使えます。

デフォルトでEmail・Googleの認証がONになっています。
今回、私はGitHubの認証もONにして使っていこうと思います。
clerc-setup.png

こちらの設定が終わったら、Create applicationをクリックします。
すると、以下の画面に遷移するため、手順に従っていきます。

setup-turn.png

ライブラリ追加

画面の手順に表示されるため、コピペでできます。

bun add @clerk/nextjs

環境変数設定

画面の手順に表示されるため、コピペでできます。

.env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=********************************
CLERK_SECRET_KEY=********************************

middlewareの作成

srcディレクトリを使用している場合はsrc配下にmiddleware.tsを作成します。
srcディレクトリを使用していない場合は、rootディレクトリ配下にmiddleware.tsを作成します。

この手順も画面に表示されるため、コピペでできます。

middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

JSON Web Tokens (JWT) を使用しており、Cookie に保存された JWT を検証することで認証を行います。

ちなみに、clerkMiddlewareですが、以下の記事にあるように、JWTを使って検証を行うようです。

ClerkProviderの追加

app/layout.tsxにて、ClerkProviderを追加していきます。
こちらも画面の手順に表示されるため、コピペでできます。

layout.tsx
import type { Metadata } from 'next'
import localFont from 'next/font/local'
import './globals.css'
import { ClerkProvider } from '@clerk/nextjs'
import type { ReactNode } from 'react'

const geistSans = localFont({
  src: './fonts/GeistVF.woff',
  variable: '--font-geist-sans',
  weight: '100 900',
})
const geistMono = localFont({
  src: './fonts/GeistMonoVF.woff',
  variable: '--font-geist-mono',
  weight: '100 900',
})

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

const RootLayout = ({ children }: Readonly<{ children: ReactNode }>) => {
  return (
    <ClerkProvider>
      <html lang="ja">
        <body
          className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        >
          {children}
        </body>
      </html>
    </ClerkProvider>
  )
}

export default RootLayout

ここまでで初回設定画完了です。
これでClerkを使用できる準備が整いました。

Next.jsで認証機能を実装

ここから本格的に、認証機能を実装していきます。
以下を参考にして行っていきます。

sign-upページの作成

app/sign-up/[[...sign-up]]/page.tsxを作成しています。
こちらはOptional Catch-all Segmentsというsegmentで、/sign-up以下のパスパラメータを何でも受け取るというようなsegmentになります。

以下のようにします。
fallbackRedirectUrlに認証後にリダイレクトさせたいパスを渡します。

また、後ほど、RootLayoutの修正を行うめ、少しスタイリングを調整します。

page.tsx
import { SignUp } from '@clerk/nextjs'

const SignUpPage = () => {
  return (
    <div className="w-full mt-4 flex justify-center items-center">
      <SignUp fallbackRedirectUrl={'/'} />
    </div>
  )
}

export default SignUpPage

sign-inページの作成

sign-upと同様にsign-inページについてもapp/sign-in/[[...sign-in]]/page.tsxを作成し、同じようにコンポーネントを作成します。

page.tsx
import { SignIn } from '@clerk/nextjs'

const SignInPage = () => {
  return (
    <div className="w-full mt-4 flex justify-center items-center">
      <SignIn fallbackRedirectUrl={'/'} />
    </div>
  )
}

export default SignInPage

middlewareを修正

以下のように、clerkMiddlewareに処理を追加し、sign-inとsign-up配下のパスへの遷移をpublicにします。

auth.protect()はpublicでないパスに認証なしでアクセスした場合に、sign-inページにリダイレクトさせる処理です

middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])

export default clerkMiddleware(async (auth, request) => {
  // sign-in, sign-up 以外は認証ページにリダイレクト
  if (!isPublicRoute(request)) {
    await auth.protect()
  }
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

環境変数を追加

ドキュメントに従い、以下の環境変数を追加していきます。

.env.local
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

RootLyaoutの修正

以下のドキュメントを参照し、Clerkが提供するコンポーネントを組み込み、ヘッダーを作成していきます。
また、スタイルを少し調整します。

layout.tsx
import type { Metadata } from 'next'
import localFont from 'next/font/local'
import './globals.css'
import {
  ClerkProvider,
  SignInButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@clerk/nextjs'
import type { ReactNode } from 'react'

const geistSans = localFont({
  src: './fonts/GeistVF.woff',
  variable: '--font-geist-sans',
  weight: '100 900',
})
const geistMono = localFont({
  src: './fonts/GeistMonoVF.woff',
  variable: '--font-geist-mono',
  weight: '100 900',
})

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

const RootLayout = ({ children }: Readonly<{ children: ReactNode }>) => {
  return (
    <ClerkProvider>
      <html lang="ja">
        <body
          className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        >
          <header className="fixed top-0 left-0 w-full z-50 h-16 bg-neutral-100 flex items-center">
            <SignedOut>
              <div className="h-fit ml-4 bg-blue-500 w-fit p-2 rounded-md text-white text-sm font-semibold">
                <SignInButton />
              </div>
            </SignedOut>
            <SignedIn>
              <div className="h-fit ml-4 w-fit">
                <UserButton />
              </div>
            </SignedIn>
          </header>
          <main className="min-h-[calc(100dvh-4rem)] max-w-full pt-16">
            {children}
          </main>
        </body>
      </html>
    </ClerkProvider>
  )
}

export default RootLayout

ここまでで、認証に必要なUIと機能の作成が完了しました。
画面を確認してみましょう!

sign-inページ

以下のようなUIがCardのような形式で表示されます。
これらは、今どきよく見られるSignIn画面と思います。
このあたりのUIもトレンドをおさえているので非常に、好感が持てます。

image.png

email・password認証

機能もしっかりしており、アカウントにないメールアドレス入力で弾かれるようになっています。
image.png

また、パスワードリセットもデフォルトであるようです。
試しに、リセットしてみましょう。
image.png

image.png

二段階認証も付いています。

image.png

コード再送信機能も当然のようについています!

image.png

また、この二段階認証のUIですが、自前で実装しようとすると、かなり大変です。
なので、コンポーネントライブラリに任せるのが一般的ですが、それでも大変です。
ただ、Clerkの場合、デフォルトでUXもしっかりしており、テンキーで動かせるので、そこもポイント高いです。
(この間、テンキーどころかカーソル当てても該当のinput要素にフォーカスされないシステムが有り、かなり悪い印象を植え付けられました)

image.png

二段階認証のコードについては、入力したメールアドレス宛に送られてきます。
send-email.png

パスワード再設定画面も完璧ですね!
入力すると、ログインされるみたいで、トップページに遷移します。
この辺のリダイレクトは要件にもよるので、ログインページに再度飛ばすこともできるとは思います。
(ドキュメントで調べてみてください)
image.png

sign-upページ

こちらもほぼ、sign-inページと同じですね。
こちらも試しにemail・passwordの認証を行おうと思います。
image.png

email・password認証

まずは、パスワードのチェックから見ていきましょう。
入力を行い、Submitすると、弾かれました。
文字数だけでなく、パスワードのセキュリティチェックも行ってくれるみたいです。
(これも自力でバリデーションしようとすると結構、面倒です。)
secure-password.png

セキュアなパスワードを入力するとUIがセキュリティ条件をクリアしたことを知らせてくれます。
image.png

パスワードを入力できたら、二段階認証の画面に遷移します。
image.png

あとは、sign-inと同じ流れで、二段階認証の再送もできますし、二段階認証通れば、トップページにリダイレクトされます。

ユーザー情報管理機能の実装

ここでは認証済みユーザーに関連する処理を見ていきます。
まずは、ログインユーザー取得機能を追加していきます!

Server Component

以下を参照し、Server Componentからユーザーを取得してみましょう。
※ 後ほど、Client Componentからも取得する方法を記載するため、Client Componentを作成しています。

import { ClientComponent } from '@/components/client-component'
import { auth, currentUser } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

const Home = async () => {
  const { userId } = await auth()

  if (!userId) {
    redirect('/sign-in')
  }

  const user = await currentUser()

  return (
    <div className="w-full h-full flex flex-col justify-center items-center mt-4 gap-2">
      <p>
        Hello <span className="font-semibold">{user?.fullName}</span>, this is a
        server component
      </p>
      <div>
        <ClientComponent />
      </div>
    </div>
  )
}

export default Home

Client Component

以下、同じドキュメントのclient-sideの項目で、Client Componentでのユーザー取得を行っていきましょう!
(Server Component・Client Componentの両者の方法を紹介するため、今回はどちらも行っています)

以下のようにしますが、基本的には、useUser()のみ使用すればよいと思います。
私は、userIdを表示するために、useAuth()も使っています。

page.tsx
'use client'

import { Loader } from '@/components/loader'
import { useAuth, useUser } from '@clerk/nextjs'
import { useRouter } from 'next/navigation'

export const ClientComponent = () => {
  const router = useRouter()
  const { isLoaded, userId, sessionId } = useAuth()
  const { isLoaded: isUserLoaded, isSignedIn, user } = useUser()

  if (!(isLoaded && isUserLoaded)) {
    return <Loader />
  }

  if (!(isSignedIn && user && userId)) {
    router.push('/sign-in')
  }

  return (
    <div className="flex flex-col gap-2 items-center">
      <p>
        Hello, <span className="font-semibold">{userId}</span> your current
        current active session is{' '}
        <span className="font-semibold">{sessionId}</span>
      </p>
      <p>
        Nice to see you,{' '}
        <span className="font-semibold">
          {user?.firstName} {user?.lastName}
        </span>
        , welcome to the client component!
      </p>
    </div>
  )
}

これで、ユーザー管理機能の実装も完了しました。
それでは、認証後のページを見ていきましょう。

以下のようになっており、認証後はヘッダーにアイコンが表示されているのがわかります。
Google認証を使用したため、Googleアカウントのアイコン画像が表示されています。
これがGitHubの認証を使用すれば、GitHubアカウントのアイコン画像が表示されますし、未認証ではAvatar Componentのように表示されます。

clerc-account-info.png

ユーザーボタン

ヘッダーのユーザーボタンをクリックすると以下のようなUIが表示されます。
これもClerk側が用意しており、こちらでは何も実装していないです。

clerc-user-button.png

それでは、Manage accountをクリックしてみましょう!
以下のようにユーザー編集UIが表示されました。

clerc-account-manage.png

GitHub認証の場合は以下のようになります。
clerc-account-manage-with-github.png

これで今回実装した、認証の内容は以上です。

最後にClerkのダッシュボードを見てみましょう!
以下のように管理画面でユーザー一覧が表示され、更新・削除などの管理機能を使えます。
clerk-manage-user.png

おわりに

認証系SaaSのClerkどうでしょうか?
他にも認証系のことはほぼ網羅的に機能として提供されているので、かなりおすすめです!

料金プランもProプランで月額25ドルなので、これほどまでの認証系の機能を提供してくれているので、かなりお手軽かなと思います。

また、WebHookなどの設定もできるようなので、以下の入門動画を参考にしてみてください!

参考文献

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?