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