Search code examples
javascriptreactjshttp-redirectnext.jsnext-auth

useRouter().push() from "next/navigation" doesn't work on custom signIn-Page with next-auth


After a successful login on my custom signIn-Page, I use with next-auth, I'm trying to redirect back to the callbackUrl, but that doesn't work. I'm using react 18.2.0, next 13.4.8-canary.2 and next-auth 4.22.1.

signIn-Page (\src\app\api\auth\signin\page.js):

"use client";

import { signIn } from "next-auth/react";
import { useSearchParams, useRouter } from "next/navigation";
import { useState } from "react";

export default function LoginPage() {
    const router = useRouter();
    const callbackUrl = useSearchParams().get("callbackUrl") || "/";
    const course = callbackUrl.split("/").pop();
    let [loading, setLoading] = useState(false);
    let [password, setPassword] = useState("");
    const [error, setError] = useState("");

    async function onSubmit(event) {
        event.preventDefault();
        try {
            setLoading(true);
            const res = await signIn("credentials", {
                redirect: false,
                courseName: course,
                password: password,
                callbackUrl
            });
            if (!res?.error) {
                setError("");
                console.log(`tryin to redirect to ${callbackUrl}`);
                router.push(callbackUrl);
            } else {
                setError("falsches Passwort");
            }
            setLoading(false);
        } catch (error) {
            setLoading(false);
            setError(error);
        }
    }

    function handleChange(event) {
        event.preventDefault();
        setPassword(event.target.value);
    }

    return (
        <main style={{ display: "flex", justifyContent: "center" }}>
            <div className="LoginContainer">
                <br />
                <div className="LoginBackgroundImage">
                    <h1 className="LoginHeading">Login</h1>
                </div>
                <form onSubmit={onSubmit}>
                    {error ? <strong className="w3-text-red">{error}</strong> : ""}
                    <div>
                        <p style={{ margin: 5 }}>
                            <strong>{course}</strong>
                        </p>
                    </div>
                    <div>
                        <input
                            className="passwordField"
                            required
                            type="password"
                            name="password"
                            value={password}
                            onChange={handleChange}
                            placeholder="Passwort"
                        />
                    </div>
                    <button className="loginButton" type="submit" disabled={loading}>
                        {loading ? "lädt..." : "Anmelden"}
                    </button>
                </form>
            </div>
        </main>
    );
}

As you can see from the path, I'm using the new /app directory for routing. I know the login process is working because I can then call the callbackUrl manually in the browser and the content that requires authorization is displayed. I can also see in the developer console that the command

console.log(`tryin to redirect to ${callbackUrl}`);

is performed. But the page keeps showing the signIn-Page.


Solution

  • I think, I found the reason. The target-page to which I useRouter().push() had a redirect itself to the loginpage. This redirect should only be executed if you are not logged in as the correct user. Since the redirect was already executed once to get to the login page, it was probably executed again afterwards, although the correct user was then logged in. The reason for this could be that the target-page is a server-page which is not rendered again after login. I have now solved this by linking directly to the login-page without calling up the target-page first and only after login is the target-page called up for the first time.