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
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 ?
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.
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 ?
After hours of research, I finally came across a very recent Github discussion concerning the same problem.
This is the short version, longer is at the bottom.
NEXTAUTH_URL in .env file, replacing /espace-personnel by your own basePath :NEXTAUTH_URL=http://localhost/espace-personnel/api/auth
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
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
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">
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With