Why doesn't the address bar automatically update to the correct path after successfully authenticating with NextAuth using credentials?

Is there a way to automatically refresh the URL path once a successful login is completed with credentials?

I attempted to set up credential authentication similar to the guide provided by Next in their tutorial here. However, I am only using email for authentication.

I can successfully log in, but the URL in the address bar does not update. It should change from /login to /dashboard. The update only occurs after manually refreshing the page. Without the update, I am unable to sign out after clicking "sign out." I can only sign out if I manually refresh the page and the address changes. This can be seen in the gif below.

Here is my folder structure.

https://i.sstatic.net/Av1UqR8J.gif

// ./auth.config.ts

import type { NextAuthConfig } from 'next-auth';

export const authConfig = {
  pages: {
    signIn: '/login',
  },
  callbacks: {
    authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user;
      const isOnDashboard = nextUrl.pathname.startsWith('/dashboard');
      if (isOnDashboard) {
        if (isLoggedIn) return true;
        return false; // Redirect unauthenticated users to login page
      } else if (isLoggedIn) {
        return Response.redirect(new URL('/dashboard', nextUrl));
      }
      return true;
    },
  },
  providers: [],
} satisfies NextAuthConfig;
// ./middleware.ts

import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
};
// ./auth.ts

import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { z } from 'zod';
import { sql } from '@vercel/postgres';
import { authConfig } from './auth.config';
import type { User } from '~/lib/definitions';

async function getUser(email: string): Promise<User | undefined> {
  try {
    const user = await sql<User>`SELECT * FROM users WHERE email=${email}`;
    return user.rows[0];
  } catch (error) {
    console.error('Failed to fetch user:', error);
    throw new Error('Failed to fetch user.');
  }
}

export const { auth, signIn, signOut } = NextAuth({
  ...authConfig,
  providers: [
    Credentials({
      async authorize(credentials) {
        const parsedCredentials = z
          .object({ email: z.string().email() })
          .safeParse(credentials);

        if (parsedCredentials.success) {
          const { email } = parsedCredentials.data;
          const user = await getUser(email);
          if (user) return user;
          return null;
        }
        return null;
      },
    }),
  ],
});

// ./app/lib/actions.ts

'use server';

import { AuthError } from 'next-auth';
import { signIn } from '~/../auth';

export async function authenticate(
  prevState: string | undefined,
  formData: FormData
) {
  try {
    await signIn('credentials', formData);
  } catch (error) {
    if (error instanceof AuthError) {
      switch (error.type) {
        case 'CredentialsSignin':
          return 'Invalid credentials.';
        default:
          return 'Something went wrong.';
      }
    }
    throw error;
  }
}
// ./app/login/page.tsx

'use client';

import { useFormState, useFormStatus } from 'react-dom';
import { authenticate } from '~/lib/actions';

export default function LoginPage() {
  const [errorMessage, dispatch] = useFormState(authenticate, undefined);

  return (
    <main>
      <form action={dispatch}>
        <div>
          <label htmlFor="email">
            Email
          </label>
          <div>
            <input
              id="email"
              type="email"
              name="email"
              placeholder="Enter your email address"
              required
            />
          </div>
        </div>
        <LoginButton />
      </form>
    </main>
  );
}

function LoginButton() {
  const { pending } = useFormStatus();

  return (
    <button aria-disabled={pending}>
      Sign in
    </button>
  );
}
// ./app/dashboard/page.tsx

import { signOut } from '~/../auth';

export default function Page() {
  return (
    <main>
      <h1>Dashboard</h1>
      <p>Dashboard content goes here</p>
      <form
        action={async () => {
          'use server';
          await signOut({ redirectTo: '/login' });
        }}
      >
        <button>
          <div>Sign Out</div>
        </button>
      </form>
    </main>
  );
}

https://i.sstatic.net/eAO2YQav.png

Answer №1

After countless nights of tossing and turning and watching my hair turn gray, I finally stumbled upon a solution that works for me - updating the formData by including a redirect path.

// ./app/lib/actions.ts

'use server';

import { AuthError } from 'next-auth';
import { signIn } from '~/../auth';

export async function authenticate(
  prevState: string | undefined,
  formData: FormData
) {
  formData.set('redirectTo', '/dashboard'); // <-- this resolved the problem
  try {
    await signIn('credentials', formData);
  } catch (error) {
    if (error instanceof AuthError) {
      switch (error.type) {
        case 'CredentialsSignin':
          return 'Invalid credentials.';
        default:
          return 'Something went wrong.';
      }
    }
    throw error;
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Incorporate JSON data to display SVG images

As a beginner in web development, I have been honing my skills with the AngularJS framework due to its user-friendly nature. Currently, I'm working on pulling JSON data from an API using $http.get method. One of the fields contains an image source i ...

A step-by-step guide on how to implement a window scroll-controlled color transition

I've implemented jQuery code to change the opacity of my navbar's background as the user scrolls, transitioning from transparent to blue. Here's the snippet: $(window).scroll(function(){ var range = $(this).scrollTop(); var limit = 45 ...

Building a single-page app optimized for mobile viewing with Foundation framework

Currently facing an issue with the scaling of the viewport on certain mobile devices when loading new content via ng-include in our responsive website built on Foundation. The problem arises as the width of the page breaks, leading to horizontal scrolling. ...

Custom script for Greasemonkey that modifies a parameter within the URL

While using Google Analytics, I have noticed that some pages display a variable in the URL with a default value of 10. The format usually looks like this: ...&trows=10&... Is there a method to alter this to trows=100 in order to change th ...

Steps for including a new script in package.json

Is there a way to add the script dev to my package.json from the terminal? I attempted to manually add it using a text editor, but when I try to run: npm run dev I encounter some errors. Is there a way to include this script through the command line? E ...

Can anyone help me with integrating a search functionality inside a select tag?

I am working on a select dropdown and want to add a search box to easily filter through the available options. I know this can be done using the chosen library, but I prefer to implement it using plain JavaScript or jQuery. Does anyone have suggestions on ...

Tips for resolving Error 400 when images are not appearing in the Next App utilizing Firebase Server-Side Rendering

Currently navigating the process of deploying my ssr app on firebase and making progress. Encountering an issue with my images - a 400 error message is appearing in the console. The images are sourced from an external URL, and although the data is being f ...

What is the solution to fixing the JSON parsing error that says 'JSON.parse: bad control character in string literal'?

When sending data from NodeJS Backend to the client, I utilize the following code: res.end(filex.replace("<userdata>", JSON.stringify({name:user.name, uid:user._id, profile:user.profile}) )) //No errors occur here and the object is successfully stri ...

Adjust the position of an image to move it closer to the top using CSS

I have a fiddle where I am trying to adjust the position of an image (keyboard) to match a specific design. https://i.sstatic.net/flqCH.png In my fiddle, the keyboard image is slightly lower than the desired position shown in the design. I am looking for ...

Creating a dynamic form that efficiently captures user information and automatically redirects to the appropriate web page

Sorry for the unusual question, but it was the best way I could think of asking. I have come across websites with fill-in-the-blank lines where you can input your desired information and then get redirected to another page. For example: "What are you sea ...

An error occurred while trying to access the property 'send' which is undefined

I am currently facing an issue while attempting to send data to the router from an external script. The error message that I receive is TypeError: Cannot read property 'send' of undefined Below is the code snippet: app.js var Cake = require(&a ...

Error: The variable YouTube has not been declared within this context / YouTube API

In my attempt to implement a search using the YouTube Data API, I have come up with the following code. The setup involves an express generated stack with jade. #player script. // 2. This code loads the IFrame Player API code asynchronously. ...

Put emphasis on the input field - React component

My React component features an input field with a disabled attribute. When the component is clicked, the input becomes enabled for the user to type in. I have successfully implemented this functionality so far, but now I need to focus on the input field on ...

What is the process for executing a function if the Materialize.css autocomplete feature is not chosen?

Is it feasible to create a function that triggers only when a user does not select an option from Materialize.css autocomplete feature? My goal is to automatically populate an email field with a predefined value based on the user's selection in anothe ...

Maximizing the potential of NPM modules by harnessing the power of the package.json file

Currently, I am working on developing an NPM module for a command-line tool. Upon installation of the package, it is essential to access and read the user's package.json file. While I understand how to read the file syntactically, my main concern lies ...

Loading an OBJ file from a blob using three.js

I am currently attempting to load an OBJ file from a blob using Three.js. After referring to this resource and successfully loading STL files, I encountered an issue with loading OBJ files. The error message I received is as follows: TypeError: text.indexO ...

Submitting a form with JQuery and Ajax technology

I am encountering an issue where I need to submit a form within Ajax and I keep getting a 'form.submit is not a function' error in jQuery. $("#form").validate({ // Define validation rules rules: { type: "required", group ...

Utilizing JavaScript to present JSON data in HTML tables across various sections of a website

Utilizing JScript to retrieve data from a JSON API URL, I have incorporated the data in the JSON file displayed below - containing information on 8 horse races with details like Horse number, Horse name, and their odds. My goal is to create a Jscript code ...

Deactivate a button when clicked on a card that has been mapped

After creating a card component and mapping through each card, I added an onClick function to disable the button of the clicked card. However, my logic ended up disabling all buttons instead. Here is the code snippet where I define the rendering of the UI ...

Ways to obtain the index of a button

Once upon a time, I used to create a plethora of buttons using a for loop. The title[] array was filled with numerous values. export const renderButtons1 = (numOfBtns,title,navigate) => { const views1 = []; for ( var i = 0; i < numOfBtns; ...