vercel / next-learn

Learn Next.js Starter Code
https://next-learn-dashboard.vercel.sh/
MIT License
3.72k stars 1.9k forks source link

Several Improvements Needed for signIn and signOut Logic in `Chapter 15 Adding Authentication` of the Official `dashboard-app` Tutorial #861

Open ks4na opened 1 month ago

ks4na commented 1 month ago

currently,The implementation of next-auth Credentials authentication flow in Chapter 15 of the official tutorial works, but it has some flaws that cause several confusion for me and also some other learners. such as

Existing Issues

Based on my observations, the following issues exist with the tutorial's implementation (which can be reproduced in the official live demo):

  1. When navigating from the homepage to the login page and successfully logging in, the user is redirected to the /dashboard page, but the browser's URL does not change and remains as /login. Additionally, the Sign Out button does not work until the page is refreshed, after which the URL displays correctly as .../dashboard and the Sign Out button works as expected.

https://github.com/user-attachments/assets/bfa5979d-05d6-41ab-9b81-bde9a07a4466

  1. Following the steps in point 1, when clicking the Sign Out button on any page (e.g., /dashboard/customers), the page content updates to the /login page, but the browser's URL does not change. After refreshing the page, the browser URL updates to .../login?callbackUrl=https%3A%2F%2Fnext-learn-dashboard.vercel.sh%2Fdashboard%2Fcustomers.

https://github.com/user-attachments/assets/e0153cee-1b0d-400b-9306-321428283e1a

  1. Following the steps in point 2, if the browser URL contains a callbackUrl=... query parameter (e.g., when accessing https://next-learn-dashboard.vercel.sh/dashboard/customers while not logged in, which redirects to the /login page with the callbackUrl), the expected behavior is to be redirected to the callbackUrl page after a successful login. However, after logging in, the user is still redirected to the /dashboard page, and the same issue from point 1 occurs: the browser's URL does not update and remains as .../login?callbackUrl=https%3A%2F%2Fnext-learn-dashboard.vercel.sh%2Fdashboard%2Fcustomers, and the Sign Out button does not work. Refreshing the page resolves the issue.

https://github.com/user-attachments/assets/4dd22877-1e4b-42f8-a471-08b99ff87959

Suggested Improvements

During SignIn

As mentioned in this comment Redirect URL not updating correctly in browser after successful redirect, pass the redirectTo parameter to the signIn() function, and ensure it aligns with the logic in auth.config.ts -> authorized.

The logic in auth.config.ts -> authorized:

// ...

if (isOnDashboard) {
  if (isLoggedIn) {
    return true;
  }
  return false;
} else if (isLoggedIn) {
  return Response.redirect(new URL('/dashboard', nextUrl));
}

// ...

To keep the same logic, the login form should include the redirectTo field, defaulting to /dashboard. If the URL contains a callbackUrl, the field should be set to that value.

modify app/login/page.tsx:

// ...

export default function LoginPage({
  searchParams,
}: {
  searchParams?: {
    callbackUrl?: string;
  };
}) {
  const redirectTo = searchParams?.callbackUrl || '/dashboard'; // <============== get `redirectTo` from search param `callbackUrl`

  return (
    <main className="flex items-center justify-center md:h-screen">
      {/* ... */}

        <LoginForm redirectTo={redirectTo} /> {/* <============== pass `redirectTo` to LoginForm */}
      </div>
    </main>
  );
}

modify app/ui/login-form.tsx:

// ...

export default function LoginForm({ redirectTo }: { redirectTo: string }) {
  // ...

  return (
    <form action={formAction} className="space-y-3">
      {/* ... */}

      <input type="hidden" name="redirectTo" value={redirectTo} /> {/* <============== add `redirectTo` */}

    {/* ... */}
    )
}

During SignOut

In the sidenav.tsx, the redirectTo parameter should be specified. Typically, when signing out, the user should be redirected to the homepage or the login page.

modify app/ui/dashboard/sidenav.tsx:

// ...

export default function SideNav() {
  return (
      {/* ... */}

        <form
          action={async () => {
            'use server';
            await signOut({ redirectTo: '/' }); /* <============== add `redirectTo` */
          }}
        >

      {/* ... */}
    )
}

Verification After the Fixes

minimal reproducible repo: https://github.com/ks4na/nextjs-dashboard/tree/chapter15-improvements

vercel.app preview link

Additional Information

next-auth documentation

For more details on signIn(), signOut(), and redirectTo, see the documentation: signIn(), signOut().

rodicodi commented 2 days ago

I got a few errors lately that Canary is using the new async server component props... So.... just a suggestion:


// ...

type PageProps = {
  searchParams?: Promise<{
    callbackUrl?: string;
  }>;
};

export default async function LoginPage(props: PageProps) {
  const searchParams = await props.searchParams;
  const redirectTo = searchParams?.callbackUrl || "/dashboard"; // <============== get `redirectTo` from search param `callbackUrl`

  return (
    <main className="flex items-center justify-center md:h-screen">
      {/* ... */}

        <LoginForm redirectTo={redirectTo} /> {/* <============== pass `redirectTo` to LoginForm */}
      </div>
    </main>
  );
}