はじめに
Auth0のユニバーサルログインを使用したログインを実装する
page.tsx
以下のコードは、Next.js の App Router 構造(app ディレクトリ)で page.tsx ファイルを使用して、LoginComponent をクライアントサイドでのみレンダリングする実装です。/loginに遷移した時に実行されます。
'use client'
import dynamic from 'next/dynamic'
const LoginComponent = dynamic(
() => import('./components/LoginComponent').then((module) => module.default),
{
ssr: false,
}
)
export default function Login() {
return (
<>
<LoginComponent />
</>
)
}1. 'use client' ディレクティブ:
- Next.js 14 では、
'use client'ディレクティブを使用することで、このファイル内のコードがクライアントサイドでのみ実行されるように指示します。これにより、SSR(サーバーサイドレンダリング)ではなく、クライアントサイドでのみ動作します。
2. dynamic インポート:
dynamic関数を使ってLoginComponentを 動的にインポート しています。これは、コンポーネントを遅延ロードして、初期ロード時のパフォーマンスを改善するための Next.js の機能です。ssr: falseによって、サーバーサイドレンダリングが無効化されています。これにより、LoginComponentはサーバー側でレンダリングされず、クライアントサイドでのみレンダリングされます。
3. import('./components/LoginComponent').then((module) => module.default):
- この部分は、
LoginComponentがデフォルトエクスポートであることを前提にしています。LoginComponentがdefault exportされている場合、この方法で正しくインポートできます。 .then((module) => module.default)は、import関数が Promise を返すため、モジュールがロードされた後にdefaultエクスポートを取得します。
4. <LoginComponent />:
- 動的にインポートされた
LoginComponentをこの場所でレンダリングしています。LoginComponentがロードされると、<LoginComponent />がクライアントサイドで表示されます。
5. 戻り値としての LoginComponent のレンダリング:
Loginコンポーネント内でLoginComponentを使用しており、ページが表示された際にLoginComponentがレンダリングされます。この実装により、LoginComponentの読み込みがクライアントサイドで遅延して行われ、初期ロードを軽減します。
この構造は、特に クライアントサイドでのみ動作する認証フロー や、ユーザーインターフェースの動的な要素を含むコンポーネントに適しています。
- パフォーマンス最適化:
LoginComponentの動的インポートにより、ページ全体がロードされる前に必要なコンポーネントだけをロードすることができます。これは、初期ロード時間を短縮し、ユーザー体験を向上させるのに役立ちます。 - サーバーサイドレンダリングの無効化:
ssr: falseによって、このコンポーネントはサーバーサイドではレンダリングされず、クライアントサイドでのみ動作します。
LoginComponent.tsx
以下のコードは、Auth0 の認証機能を用いて、ユーザーがログインしていない場合に Universal Login を表示し、Capacitor を利用したモバイルデバイスでのリダイレクト処理も考慮した実装です。ログイン機能がウェブとモバイルの両方で適切に動作するように設計されています。
'use client'
import { useAuth0 } from '@auth0/auth0-react'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { App as CapApp } from '@capacitor/app'
import { Browser } from '@capacitor/browser'
import { getDeviceInfo } from '@/lib/utils' // デバイス情報を取得するユーティリティ関数
const LoginComponent = () => {
const { loginWithRedirect, isAuthenticated, isLoading, handleRedirectCallback } = useAuth0()
const router = useRouter()
// デバイスのプラットフォームに基づいてリダイレクト処理を変更
const handleLogin = async () => {
const deviceInfo = await getDeviceInfo()
if (deviceInfo.platform === 'web') {
// ウェブブラウザ環境の場合、通常のリダイレクト
loginWithRedirect()
} else {
// モバイルデバイスの場合、Capacitor ブラウザでリダイレクト
loginWithRedirect({
openUrl: async (url) => {
await Browser.open({ url, windowName: '_self' })
},
})
}
}
// モバイルアプリの場合のリダイレクトコールバック処理
useEffect(() => {
const handleMobileRedirect = async () => {
const deviceInfo = await getDeviceInfo()
if (deviceInfo.platform !== 'web') {
// モバイルアプリでのリダイレクト処理を設定
CapApp.addListener('appUrlOpen', async ({ url }) => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
await handleRedirectCallback(url)
router.replace('/home') // ログイン後のダッシュボードページ
await Browser.close() // モバイルブラウザを閉じる
}
})
}
}
// クライアントサイドでのみ実行
if (!isLoading && typeof window !== 'undefined') {
if (isAuthenticated) {
router.replace('/home')
} else {
handleLogin() // ログイン処理を実行
}
handleMobileRedirect() // モバイル用リダイレクト設定
}
}, [isAuthenticated, isLoading, handleRedirectCallback, router])
if (isLoading) {
return <div>Loading...</div> // 認証情報を確認中
}
return null // 認証後はリダイレクトされる
}
export default LoginComponent;1. loginWithRedirect() の使用:
loginWithRedirect()は、Auth0 の Universal Login ページにユーザーをリダイレクトしてログインさせるために使用されます。ユーザーがログインしていない場合、このメソッドが呼び出され、Auth0 のホスティングされたログインページに移動します。
2. ウェブとモバイルのリダイレクト処理の違い:
- ウェブブラウザ環境:
deviceInfo.platform === 'web'の条件で、通常のリダイレクトが行われます。ここでは、loginWithRedirect()がデフォルトの挙動で Auth0 のログインページにリダイレクトします。 - モバイル環境(Capacitor): モバイルデバイスでは、
Capacitor Browserプラグインを使用して、ネイティブのブラウザウィンドウで Auth0 のログインページを開きます。openUrlのオプションを使って、Browser.open()でブラウザを開く処理が行われます。
3. useEffect でのリダイレクト処理:
useEffectフック内で、コンポーネントがクライアントサイドでマウントされた後に認証状態を確認し、必要に応じてログイン処理またはリダイレクトを実行します。isAuthenticatedがtrueであればユーザーはログイン済みなので、/homeページにリダイレクトされます。isAuthenticatedがfalseの場合は、handleLogin()メソッドが実行され、ユーザーがログインしていないために Universal Login ページにリダイレクトされます。
4. モバイルでのリダイレクトコールバック処理:
- モバイルアプリでのログイン完了後、
Capacitor AppのappUrlOpenイベントリスナーを使って、Auth0 のリダイレクト URL(stateやcodeが含まれる)を処理します。 handleRedirectCallback()は、リダイレクトされた URL を処理して、ログインを完了させます。完了後にユーザーを/homeにリダイレクトし、モバイルブラウザウィンドウを閉じます。
5. isLoading の状態管理:
isLoadingは、認証情報がまだロード中かどうかを示します。認証の状態がまだ確認できていない場合は、Loading...テキストが表示され、認証処理が終わるまでユーザーに待機画面を表示します。- 認証状態が確認されると、適切なリダイレクト(
loginWithRedirectまたは/home)が行われます。
参考
- https://qiita.com/taisei_code/items/c2d2c2c0d41d6d808bf4
- NextRouter was not mounted エラーにハマり、import { useRouter } from “next/navigation”; にすると解決