diff --git a/apps/frontend/app/(public)/auth/login/page.tsx b/apps/frontend/app/(public)/auth/login/page.tsx index 3cf8977..5186ea5 100644 --- a/apps/frontend/app/(public)/auth/login/page.tsx +++ b/apps/frontend/app/(public)/auth/login/page.tsx @@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { useLoginForm } from '@/features/public/auth/login/hooks'; +import { appendCallbackUrl, normalizeCallbackUrl } from '@/lib/auth/callback-url'; export default function LoginPage() { return ( @@ -19,7 +20,8 @@ export default function LoginPage() { function LoginPageContent() { const router = useRouter(); const searchParams = useSearchParams(); - const callbackUrl = searchParams.get('callbackUrl') ?? '/dashboard'; + const callbackUrl = normalizeCallbackUrl(searchParams.get('callbackUrl')); + const registerHref = appendCallbackUrl('/auth/register', callbackUrl); const { form, error, validators } = useLoginForm(() => { router.push(callbackUrl); }); @@ -90,7 +92,7 @@ function LoginPageContent() {

アカウントをお持ちでない方は{' '} - + 新規登録

diff --git a/apps/frontend/app/(public)/auth/register/page.tsx b/apps/frontend/app/(public)/auth/register/page.tsx index 1691374..b03c923 100644 --- a/apps/frontend/app/(public)/auth/register/page.tsx +++ b/apps/frontend/app/(public)/auth/register/page.tsx @@ -1,19 +1,36 @@ 'use client'; import Link from 'next/link'; -import { useRouter } from 'next/navigation'; -import { useState } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { Suspense, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { useRegisterForm } from '@/features/public/auth/register/hooks'; +import { appendCallbackUrl, normalizeCallbackUrl } from '@/lib/auth/callback-url'; export default function RegisterPage() { + return ( + + + + ); +} + +function RegisterPageContent() { const router = useRouter(); + const searchParams = useSearchParams(); + const rawCallbackUrl = searchParams.get('callbackUrl'); + const callbackUrl = normalizeCallbackUrl(rawCallbackUrl); + const hasCallbackUrl = rawCallbackUrl !== null; + const loginHref = hasCallbackUrl ? appendCallbackUrl('/auth/login', callbackUrl) : '/auth/login'; const [registeredEmail, setRegisteredEmail] = useState(null); - const { form, error, validators } = useRegisterForm((email) => { - setRegisteredEmail(email); - }); + const { form, error, validators } = useRegisterForm( + (email) => { + setRegisteredEmail(email); + }, + hasCallbackUrl ? callbackUrl : undefined, + ); if (registeredEmail) { return ( @@ -27,7 +44,7 @@ export default function RegisterPage() {

メール内のリンクを開くとアカウント作成が完了します。

- @@ -146,7 +163,7 @@ export default function RegisterPage() {

すでにアカウントをお持ちの方は{' '} - + ログイン

diff --git a/apps/frontend/app/(public)/invite/[invitationId]/page.tsx b/apps/frontend/app/(public)/invite/[invitationId]/page.tsx index d687552..58dcb3d 100644 --- a/apps/frontend/app/(public)/invite/[invitationId]/page.tsx +++ b/apps/frontend/app/(public)/invite/[invitationId]/page.tsx @@ -15,6 +15,9 @@ export default function InvitePage({ params }: { params: Promise<{ invitationId: const router = useRouter(); const [status, setStatus] = useState<'idle' | 'loading' | 'error'>('idle'); const [errorMessage, setErrorMessage] = useState(null); + const inviteCallbackUrl = `/invite/${encodeURIComponent(invitationId)}`; + const loginHref = `/auth/login?callbackUrl=${encodeURIComponent(inviteCallbackUrl)}`; + const registerHref = `/auth/register?callbackUrl=${encodeURIComponent(inviteCallbackUrl)}`; const handleAccept = async () => { setStatus('loading'); @@ -55,13 +58,8 @@ export default function InvitePage({ params }: { params: Promise<{ invitationId: ログインまたはアカウントを作成してから承認できます - - + diff --git a/apps/frontend/features/public/auth/register/hooks.ts b/apps/frontend/features/public/auth/register/hooks.ts index d3bad4c..f4dc9b2 100644 --- a/apps/frontend/features/public/auth/register/hooks.ts +++ b/apps/frontend/features/public/auth/register/hooks.ts @@ -4,7 +4,10 @@ import { z } from 'zod'; import { useInvalidateMe } from '@/contexts/AuthContext'; import { authClient } from '@/lib/auth/client'; -export function useRegisterForm(onSuccess: (email: string) => void) { +export function useRegisterForm( + onSuccess: (email: string) => void, + verificationCallbackUrl?: string, +) { const invalidateMe = useInvalidateMe(); const [error, setError] = useState(null); @@ -16,7 +19,9 @@ export function useRegisterForm(onSuccess: (email: string) => void) { name: value.name, email: value.email, password: value.password, - callbackURL: `${window.location.origin}/auth/verify-email`, + callbackURL: verificationCallbackUrl + ? new URL(verificationCallbackUrl, window.location.origin).toString() + : `${window.location.origin}/auth/verify-email`, }); if (result.error) { diff --git a/apps/frontend/lib/auth/callback-url.ts b/apps/frontend/lib/auth/callback-url.ts new file mode 100644 index 0000000..b1b680b --- /dev/null +++ b/apps/frontend/lib/auth/callback-url.ts @@ -0,0 +1,15 @@ +const DEFAULT_CALLBACK_URL = '/dashboard'; + +export function normalizeCallbackUrl(callbackUrl: string | null): string { + if (!callbackUrl) return DEFAULT_CALLBACK_URL; + if (!callbackUrl.startsWith('/') || callbackUrl.startsWith('//')) { + return DEFAULT_CALLBACK_URL; + } + + return callbackUrl; +} + +export function appendCallbackUrl(path: string, callbackUrl: string): string { + const params = new URLSearchParams({ callbackUrl }); + return `${path}?${params.toString()}`; +}