I use Next.js 15. Like shown below I pass a server action "onLogin" to a client component called "LoginForm". the action is called when a user clicks on the login button in the form. In the server action I instantiate a authenticator class which is responsible for sending the provided credentials to the server and saves the received access token with cookies().set("...") and returning the authentication state. If the authentication is successful the client component sends the user to the next page. In my understanding I follow the documentation regarding manipulating cookies in a server action. However, I get the error "Cookies can only be modified in a Server Action or Route Handler". What am I missing? Is it because I delegate the authentication flow to the Authenticator class? That's the only difference I can see in comparison to the example. The strange thing is that this exact implementation worked previously and stopped working without me being aware of any changes I made.
export async function LoginPage() {
async function onLogin(state: LoginFormState, formData: FormData): Promise<LoginFormState> {
"use server";
const username = formData.get("username")?.toString();
const password = formData.get("password")?.toString();
const authenticator = new Authenticator()
return authenticator.authenticate(username, password)
};
return (
<LoginForm formAction={ onLogin }/>
);
}
and here the client component
'use client';
export function LoginForm({ formAction }: Props) {
const [isRejected, setIsRejected] = useState(false);
const [state, action] = useActionState<LoginFormState, FormData>(formAction, {});
const router = useRouter()
useEffect(() => {
if (state.errorState?.unauthorized) {
setIsRejected(true)
}
if (state.successState) router.push('/clients')
}, [state]);
return (
<div className="flex h-screen">
<div className="secondary-color-background m-auto p-8 sm:rounded-md flex flex-col justify-center items-center">
<form action={action} className="">
<label className="block mb-6">
<span className="h2 white-color">Username</span>
<input name="username" type="text" className={`${state.errorState?.username ? "border-orange" : ""} w-full border-2 rounded p-2 focus:outline-none focus:bg-white focus:border-primary `}
placeholder="type here"/>
</label>
<label className="block mb-6">
<span className="h2 white-color">Password</span>
<input name="password" type="password" className={`${state.errorState?.password ? "border-orange" : ""} w-full border-2 rounded p-2 focus:outline-none focus:bg-white focus:border-primary `}
placeholder="type here" />
</label>
<div className="flex flow-root w-full content-start items-end">
<ShakeButton text="Inloggen" isShaking= { isRejected } onStop= { () => setIsRejected(false) }/>
<Link className="body2 text-primary ml-4" href={"/komt nog"}>
Ik ben mijn wachtwoord vergeten
</Link>
</div>
</form>
</div>
</div>
);
}