はじめに
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”; にすると解決