How to define a custom base path when NEXTAUTH_URL doesn't work?
Asked Answered
P

2

15

Context

I'm using NextJS v12.0.7, React v17.0.2, NextAuth v4.1.0 and Typescript v4.3.5.

I wanted to create a simple auth system, based on NextAuth documentation and being redirected to homepage after logged in, so I've created a file in pages/api/auth folder named [...nextauth].ts that is containing these parts of code:

export default NextAuth({
  providers: [
    CredentialsProvider({
      id: "credentials",
      name: "Login",
      credentials: {
        username: { type: "email" },
        password: { type: "password" },
      },
      async authorize(credentials) {
          // [...]
      },
    }),
  ],

  // [...]

  callbacks: {
    // [...]
    redirect({ url, baseUrl }) {
      console.log("redirect - url ", url, "baseUrl ", baseUrl); // This is what I was checking
      if(url.startsWith(baseUrl)) {
        return url
      } else if(url.startsWith("/")) {
        return new URL(url, baseUrl).toString();
      }
      return baseUrl;
    }
  },
})

For my NextJS project, I'm using a basepath /espace-personnel defined in next.config.js file, so I've defined NEXTAUTH_URL in .env.local file following the official NextAuth documentation :

NEXTAUTH_URL=http://localhost/espace-personnel/api/auth

My problem

Where I'm checking url and baseUrl variables in redirect callback, I can see that my NEXTAUTH_URL variable isn't defined well, or I missunderstand something.

Here is the result of console.log("redirect - url ", url, "baseUrl ", baseUrl); :

redirect - url  http://localhost:3000 baseUrl  http://localhost

I've done researches and I can't find any case like me on Google or Stackoverflow, I'm new in NextAuth so I'm probably misunderstanding something. Does someone have an idea of what I'm doing wrong ?

Edit (15/01/2022)

After thinking again about it, I've decided to check my env variable directly using process.env.NEXTAUTH_URL, so I've added that line after the previous console.log() :

  console.log("NEXTAUTH_URL", process.env.NEXTAUTH_URL)

And here is the result:

NEXTAUTH_URL http://localhost/espace-personnel/api/auth

So, in fact, this is not a problem of defining NEXTAUTH_URL, I think I misunderstand something about how NextAuth is working, I will continue to try to find a solution to my problem and share there if I'm finding something.

Edit (16/01/2022)

After some research, I've find that we can pass callbackUrl using signIn method, so I've defined my pages like that in [...nextauth].ts :

  pages: {
    signIn: process.env.NEXT_PUBLIC_BASE_URL + '/connexion',
    signOut:  process.env.NEXT_PUBLIC_BASE_URL + '/deconnexion',
    error:  process.env.NEXT_PUBLIC_BASE_URL + '/connexion/erreur', // Error code passed in query string as ?error=
  },

And here is the code of my login form :

import React, { useState } from "react";
import Alert from "../../ui/Alert/Alert";
import Button from "../../ui/Button/Button";
import Card from "../../ui/Card/Card";
import Divider from "../../ui/Divider/Divider";
import { getCsrfToken, signIn } from "next-auth/react";
import Input from "../../ui/Input/Input";
import styles from "./LoginForm.module.scss";
import Router from 'next/router';

type TAlertTitle = string;
type TAlertMessage = string;
type TAlertType = "info" | "warning" | "success" | "error";

export default function LoginForm({ csrfToken }: { csrfToken: string }) {
  const [alertTitle, setAlertTitle] = useState<TAlertTitle>("Information");
  const [alertMessage, setAlertMessage] = useState<TAlertMessage>("Juste un exemple");
  const [alertType, setAlertType] = useState<TAlertType>("info");

  async function onSubmit(event: React.SyntheticEvent<HTMLFormElement>) {
    const target = event.target as typeof event.target & {
      username: { value: string },
      password: { value: string },
    };

    // Login attempt
    const result = signIn('credentials', {
      callbackUrl: process.env.NEXT_PUBLIC_BASE_URL,
      username: target.username.value,
      password: target.password.value
    });

    console.log(result);

    event.preventDefault();
  }

  return (
    <Card className={styles.card}>
      <p className={'textSize40 fontWeightExtraBold textAlignCenter'}>
        Connexion à votre espace personnel
      </p>
      <Divider className={styles.divider} />
      <form onSubmit={onSubmit}>
        <Alert title={alertTitle} message={alertMessage} type={alertType}></Alert>
        <input name="csrfToken" type="hidden" defaultValue={csrfToken} />
        <Input id="username" name="username" className="width100" type="email" autoComplete="email" placeholder="Adresse mail" leftIconLib="line-awesome" leftIcon="envelope" required />
        <Input id="password" name="password" className="width100" type="password" autoComplete="current-password" placeholder="Mot de passe" leftIconLib="line-awesome" leftIcon="key" required />
        <Button className="width100" theme="accent" type="submit">Se connecter</Button>
        <Button className="width100" type="submit">Identifiants oubliés</Button>
      </form>
    </Card>
  );

}

New problem is that when I'm trying to login, I'm redirected to http://localhost/api/auth/error and I'm obtaining that error :

404 | This page could not be found.

Is there something I'm doing wrong there ?

Peoples answered 14/1, 2022 at 22:31 Comment(2)
Have you tried adding the port to NEXTAUTH_URL, i.e. NEXTAUTH_URL=http://localhost:3000/espace-personnel/api/auth?Afternoons
@Afternoons I'm using port 80 in dev environment, here is what I've put in my package.json : "dev": "next dev -p 80"Peoples
P
18

After hours of research, I finally came across a very recent Github discussion concerning the same problem.

Solution to use a custom base path with NextAuth v4

This is the short version, longer is at the bottom.

  1. Define NEXTAUTH_URL in .env file, replacing /espace-personnel by your own basePath :
NEXTAUTH_URL=http://localhost/espace-personnel/api/auth
  1. Define basePath props of <SessionProvider> in _app.tsx, replacing /espace-personnel by your own basePath :
function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <SessionProvider session={session} basePath='/espace-personnel/api/auth'>
      <Component {...pageProps} />
    </SessionProvider>
  );
}
export default App

Solution to use a custom base path with NextAuth v4 (previous solution)

In this example, basePath used is /espace-personnel and domain is localhost.

You need to set the NEXTAUTH_URL environment variable within your .env file, including your basePath followed by /api/auth like this example:

NEXTAUTH_URL=http://localhost/espace-personnel/api/auth

Then edit your _app.tsx and define your base path using the props basePath on your <SessionProvider>, here is my final code to use my custom path /espace-personnel :

function App({ Component, pageProps: { session, ...pageProps} }: AppProps) {
  return (
    <SessionProvider session={session} basePath="/espace-personnel/api/auth">
      <Component {...pageProps} />
    </SessionProvider>
  )
}
export default App
Peoples answered 16/1, 2022 at 20:22 Comment(2)
Would this work for baseUrl as well?Debark
What do you mean @Debark ? This example show how to use a customized basePath with NextAuth, if you want I have added some documentation in a pull request just there, so you can read more information in the fork documentation previewPeoples
C
5

If you are using next.js auth with multiple zones or custom base path there is a couple changes to make NextAuth.js aware of that

1- update rest api path base, .env file

NEXTAUTH_URL=https://example.com

become

NEXTAUTH_URL=https://example.com/{zone name or basePath}/api/auth

2- update client api path base, _app.tsx

<SessionProvider session={pageProps.session}>

become

<SessionProvider session={pageProps.session} basePath="/{zone name or basePath}/api/auth">
Clarineclarinet answered 19/3, 2022 at 12:10 Comment(2)
Thank you. The accepted answer is incomplete unlike this one. You must define NEXTAUTH_URL to make next-auth work with prefixes. From the docs is may seem like a production-only variable but it's not.Increment
@IvanKleshnin I updated my answer, is there anything missing? Please let me know so I can improve the answer.Peoples

© 2022 - 2024 — McMap. All rights reserved.