【Nextjs×Convex×Clerk】ソーシャルアカウントによるログイン認証の実装
はじめに
この記事は、GitHubやGmail、Microsoftアカウントなどのソーシャルアカウントによるログイン実装の手順を説明しています。
以下の動画で採用されていたGitHubアカウントによるログイン認証の実装を参考に、簡単なデモアプリを作成します。
Fullstack Notion Clone: Next.js 13, React, Convex, Tailwind | Full Course 2023
🔰私は絶賛フロントエンド学習中で、ログイン認証やプロバイダまわりでつまづくことが多々あるので、同じような方の参考になればと思います。
また、Convex
はドキュメントがとても丁寧でチュートリアルも充実しているので覗いてみてください。
使用スタック
完成デモ
UIは最小限ですが、GitHub・Gmail・Microsoftアカウントからログインできます。Clerkの設定からログインに使用するソーシャルアカウントは変更可能です。
未ログインユーザのリダイレクトも防ぎます。
セットアップ
create-next-app
npx create-next-app@latest アプリ名
cd アプリ名
必要なパッケージをインストール
npm install convex
npm install @clerk/clerk-react
Convexプロジェクトの作成
ターミナル上で以下のコマンドを実行し、a new project
を選択、任意のプロジェクト名を入力します。
npx convex dev
Clerkアプリケーションの作成
Clerkにログインし、Add application
を選択、サインインの方法を選択します。
作成後、API Keysを.env.localにコピーします。
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_***********
CLERK_SECRET_KEY=sk_test_**********
JWT Templateの作成
New JWT Template
からConvexを選択します。
Apply changes
ボタンを押して、テンプレートを作成します。
configファイルの作成
convex/
にauth.config.js
を作成します。
先ほど作成したJWT Template
より、Issuerをコピーし、domainにペーストします。
export default {
providers: [
{
domain: "https://****-****-****.clerk.accounts.dev",
applicationID: "convex",
},
],
};
以上でセットアップは完了です!
フロントの作成
以下が本アプリのフォルダ構成になります。
/
├── app
| ├── (loginpage)
| | └── page.tsx
| ├── main
| | ├── layout.tsx
| | └── page.tsx
| ├── favicon.ico
| ├── globals.css
| └── layout.tsx
├── components
| ├── provider
| | └── convex-provider.tsx
| └── navigation.tsx
:
ログイン(未認証時の)ページ、CSSは以下になります。ログイン認証とはあまり関係ありません。
loginpage
"use client";
const LogInPage = () => {
return (
<div className="h-full">
<div className="h-full flex justify-center items-center">
Ligin page
</div>
</div>
);
}
export default LogInPage;
globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body,
:root {
height: 100%;
}
Convexプロバイダーの作成
components/provider
を作成、convex-provider.tsx
を作成します。
"use client";
import { ReactNode } from "react";
import { ConvexReactClient } from "convex/react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export const ConvexClientProvider = ({ children }: { children: ReactNode }) => {
return (
<ClerkProvider
publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY!}
>
<ConvexProviderWithClerk useAuth={useAuth} client={convex}>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
);
};
app
直下のlayout.tsx
にプロバイダーを追加し、監視できるようにします。
export default function RootLayout({ children }: { children:React.ReactNode}) => {
return (
<html lang="en">
<body className={inter.className}>
<ConvexClientProvider>
{children}
</ConvexClientProvider>
</body>
</html>
)
}
ログインボタンの作成
components/
にnavigation.tsx
を作成します。
"use client";
import React from 'react';
import { useConvexAuth } from "convex/react";
import { SignInButton, UserButton } from "@clerk/clerk-react";
const Navigation = () => {
const { isAuthenticated, isLoading } = useConvexAuth();
return (
<div className='w-full border-b'>
<div className='m-2 flex justify-end'>
{isLoading && (
<p>Loading...</p>
)}
{!isAuthenticated && !isLoading && (
<>
<SignInButton mode="modal" redirectUrl="/main">
<div className="px-5 py-2.5 relative rounded group overflow-hidden font-medium bg-zinc-50 text-zinc-600 inline-block cursor-pointer">
<span className="absolute top-0 left-0 flex w-full h-0 mb-0 transition-all duration-200 ease-out transform translate-y-0 bg-zinc-600 group-hover:h-full opacity-90"></span>
<span className="relative group-hover:text-white">Login</span>
</div>
</SignInButton>
</>
)}
{isAuthenticated && !isLoading && (
<UserButton
afterSignOutUrl="/"
/>
)}
</div>
</div>
)
}
export default Navigation;
プロバイダの状態はuseConvexAuth
から取得することができます。
const { isAuthenticated, isLoading } = useConvexAuth();
ログインボタンのスタイルは以下のサイトを参照しました。
ログインモーダルに関しては、@clerk/clerk-react
でコンポーネントが提供されているので、それをそのまま使用します。めちゃ簡単^ ^;
NavigationコンポーネントをLayoutに追加
<html lang="en">
<body className={inter.className}>
<ConvexClientProvider>
+ <Navigation/>
{children}
</ConvexClientProvider>
</body>
</html>
不正リダイレクト防止
app/main
直下のlayout.tsxで、useConvexAuth
を使用します。
"use client";
import { useConvexAuth } from "convex/react";
import { ReactNode } from "react";
import { redirect } from "next/navigation";
const MainPageLayout = ({ children }:{ children:ReactNode }) => {
const { isAuthenticated, isLoading } = useConvexAuth();
if(isLoading){
return (
<div className="h-full flex justify-center items-center">
<p>Loading...</p>
</div>
)
}
if (!isAuthenticated) {
return redirect("/"); // app/(loginpage)/page.tsxに遷移
}
return (
<div className="h-full">
<main className="h-full">
{children}
</main>
</div>
)
}
export default MainPageLayout;
ユーザー情報の表示
"use client";
import { useUser } from "@clerk/clerk-react";
import React from "react";
const MainPage = () => {
const { user } = useUser();
return (
<div className='h-full flex justify-center items-center'>
<div className='flex flex-col items-center'>
<p>Welcome! {user?.fullName}</p>
<p>Main Page</p>
</div>
</div>
)
}
export default MainPage
ログインユーザーの情報は@clerk/clerk-react
からuseUser
をインポートすることで取得できます。今回はログインしたユーザーのフルネームを表示しています。
以上で終了です🚀
最後に
Convex・Clerkを使用し、簡単にログイン認証が実装できました。Convex自体もTypeScriptコードからDBスキーマを書けたり、リアルタイムなデーブルの更新を確認できたりと、バックエンドアプリケーションとしても使いやすいと感じたので、しっかりと理解した上で別記事にまとめてみたいなと思います。
また、人生で初めて技術系(?)記事を書きました。内容はもちろんですが、もっと綺麗な構成や書き方を身につけていきたいです、、
アプリのコードは以下のGitHubにあげています。
参考文献
Discussion