next-auth
next-auth copied to clipboard
Redirect to custom credential sign in page using ServersideProps requires submitting twice to login
Description 🐜
There is already a discussion opened for this issue, but it seems to be stagnant.
- I have a custom credential sign-in page at
auth/signin - Logic built in to redirect all pages to /auth/signin using
getServerSidePropsif no user found in session
When submitting the form for the first time, the page will reload and nothing happens.
Is this a bug in your own project?
No
How to reproduce ☕️
Here is the repository to reproduce the issue
- Make sure that all cookies are cleared
- Direct to
http://localhost:3000/ - The redirection will happen and will redirect you to
http://localhost:3000/auth/signin?callbackUrl=http://localhost:3000 - When submitting for the first time, the page will reload and flash, but no redirection to the index page will happen
- Submitting it for the second time will work
On an unrelated note, I noticed that it is possible to see the submitted form inputs in the Network tab, is this not a security vulnerability?

Screenshots / Logs 📽
No response
Environment 🖥
System: OS: macOS 11.4 CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Memory: 60.74 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.16.1 - /usr/local/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 6.14.12 - /usr/local/bin/npm Browsers: Brave Browser: 91.1.26.77 Edge: 92.0.902.55 Safari: 14.1.1 npmPackages: next: ^11.0.0 => 11.0.1 next-auth: latest => 3.27.3 react: ^17.0.2 => 17.0.2
Contributing 🙌🏽
No, I am afraid I cannot help regarding this
Not a security vulnerability, since it's using a post method. You should make sure the page is using https in production yourself.
Okay thanks for the clarification, how about the issue that I am reporting? Is this something you are able to comment on...?
Will do when I have the time! 😊
@sabi-wabi I was unable to reproduce! Here's what I did:
- Cloned the repository
cd next-auth-example && yarnecho "NEXTAUTH_URL=http://localhost:3000" > .env.local- Tried to log in with any username / password (given that the user is hardcoded)
I was able to login with just one press of the submit button. There didn't appear to be any reload first, nor did I need to click the button twice to get logged in. I was then able to see the hardcoded user logged-in, and was able to log out successfully.
I suggest you might play around with your .env.local to see if there's anything in there causing a mess!
I am also seeing this issue using the v4 beta, the email provider and a custom sign in page. It doesn't happen every time, but often enough that it's annoying. I will try to see if I can get to the bottom of what's going on.
I have the same problem with the next-auth ^4.0.5 version. The only thing I caught is that the first time I'm redirected to the login page I have a link like this
http://localhost:3000/auth/signin?callbackUrl=http://localhost:3000
after the first try to login, the page changed its URL to
http://localhost:3000/auth/signin?#
and after this one change I can log in in the first attempt.
It's working even if I just type /auth/signin?# by myself or after this one click on submit button.
EDIT:
everything seems to work fine when instead of using signIn() function on login button
async function signInHandler() { await signIn() }
I redirect user to exactly /auth/signin?# path
async function signInHandler() { Router.push('/auth/signin?#'); }
I'm running into the same issue with the EmailProvider (password less sign in). The sign in page redirects to /auth/signin?callbackUrl=http://localhost:3000 on the first login attempt and forces you to resubmit again.
Seems like this issue only happens when calling getCsrfToken in getServerSideProps. I moved getCsrfToken into getInitialProps and never ran into the redirect.
Sample:
SignIn.getInitialProps = async (context) => {
const { req, res } = context;
const session = await getSession({ req });
if (session && res && session.user) {
res.writeHead(302, {
Location: '/',
});
res.end();
return;
}
return {
csrfToken: await getCsrfToken(context),
error: context.query.error,
};
};
I was experiencing this both in production and development. The silent failure on first attempt was caused by the csrfToken in my login form to be mismatching the one in my browser's cookie.
In development, I can see that calls to /api/auth/session is setting the next-auth.csrf-token cookie. I was generating a csrfToken using getCsrfToken() in my getServerSideProps, and this caused my server side rendered login form to be incoherent with the next-auth.csrf-token cookie. When a subsequent request to api/auth/session was made with no cookie, my browser got a cookie, still not matching the one in my login form.
On Vercel, the /api/auth/session responses is not setting thenext-auth.csrf-token cookie. In this case, it was mismatching simply because I did not have any next-auth.csrf-token cookie when requesting /api/auth/signin. I guess I could set the cookie myself inside getServerSideProps using context.res.setHeader(), but I'm not sure what other effects that might have, so I went with using the documented getCsrfToken() function client side.
TLDR; for me, the fix was simple: don't provide csrfToken as a prop with getServerSideProps, but fetch it in your login component client side. Would be nice if this didn't fail silently, at least not during development.
const Login = () => {
const session = useSession();
const [csrfToken, setCsrfToken] = useState('');
useEffect(() => {
async function fetchCsrfToken() {
const result = await getCsrfToken();
if (!result) {
throw new Error('Can not sign in without a CSRF token');
}
setCsrfToken(result);
}
/*
Wait until session is fetched before obtaining csrfToken
to prevent synchronization issues caused by both
/api/auth/session and /api/auth/csrf setting the cookie.
Only happens in dev environment.
*/
if (session.status !== 'loading') {
fetchCsrfToken();
}
}, [session.status]);
return <form>
{/* Hiding the rest for brevity */}
</form>
}
@magnusdr Thank you!!
Sorry for the late revisiting - in the issue description, I see that OP was using getSession on the server. This is not recommended anymore as we have provided a getServerSession to use on the server side. If anyone is still having this issue, feel free to reopen a new issue with a working reproduction. Closing this one for now 🙇♂️