user10033434
user10033434

Reputation: 455

Cookies are not set in Next.js with Node.js (Express) as the backend

I followed this tutorial to use HTTP-Only cookies in my project: https://maxschmitt.me/posts/next-js-http-only-cookie-auth-tokens

The backend is working fine, I can see the cookie in Postman. But for reference, I'll post the backend code: (note: secure: true is hashed)

router.post('/login', upload.none(), async (req, res, next) => {

    const { username, password } = req.body;

    if (password != "12345678") {
        return res.status(401).send({
            message: `You're not authorized!`,
            success: false
        })
    }
    const admin_id = "1";

    const payload = { id: admin_id, role: "admin" };
    const jwt = JWT.sign(payload, secret, { algorithm: 'HS256', expiresIn: "7d" });

    res.cookie( "token", jwt, {
        httpOnly: true,
        // secure: true // only works on https
    });

    return res.status(200).send({
        message: 'Logged in!',
        success: true
    });
});

The frontend:

The admin login service:

const adminLogin = async (url: string, data: any) => {

    const { arg: { username, password } } = data;

    let formData = new FormData();
    formData.append("username", username);
    formData.append("password", password);

    try {
        const result = await axios.post(url, formData, { headers: { "Content-Type": "multipart/form-data" } });
        console.log(result);
        return result.data;
    } catch (error) {
        const err = error as AxiosError;
        return err.response!.data;
    }
};

The login page: (I'm using SWR package, the onLogin is inside LoginPage component)

const { trigger, isMutating } = useSWRMutation("http://localhost:8000/api/v1/admins/login", adminLogin);

const onLogin = async (data: any) => {
        const response = await trigger(data);

        if (response.success === true) {
            console.log(response);
        } else {
            setErrorMsg(response.message);
            setOpenAlertDialog(true);
        }
    };

next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true
};

module.exports = {
    async rewrites() {
        return [{
            source: "/api/v1/:path*",
            destination: "http://localhost:8000/api/v1/:path*"
        }];
    }
};

module.exports = nextConfig;

api/[...path].ts:

import httpProxy from "http-proxy";
import Cookies from "cookies";
import url from "url";


const proxy = httpProxy.createProxyServer();

export const config = {
    api: {
        bodyParser: false
    }
};

const main = (req: any, res: any) => {
    return new Promise<void>((resolve, reject) => {
        const pathname = url.parse(req.url).pathname;
        const isLogin = pathname === "/api/login" || pathname === "/api/v1/admins/login";

        const cookies = new Cookies(req, res);
        const authToken = cookies.get("token");

        req.url = req.url.replace(/^\/api/, "");

        req.headers.cookie = "";

        if (authToken) {
            req.headers["token"] = authToken;
        }

        if (isLogin) {
            proxy.once("proxyRes", interceptLoginResponse);
        }

        proxy.once("error", reject);
        proxy.web(req, res, {
            target: "http://localhost:8000/api/v1",
            autoRewrite: false,
            selfHandleResponse: isLogin
        });

        function interceptLoginResponse(proxyRes: any, req: any, res: any) {
            let apiResponseBody = "";
            proxyRes.on("data", (chunk: any) => {
                apiResponseBody += chunk;
            });

            proxyRes.on("end", () => {
                try {
                    const { authToken } = JSON.parse(apiResponseBody);

                    const cookies = new Cookies(req, res);
                    cookies.set("token", authToken, {
                        httpOnly: true,
                        sameSite: "lax",
                        path: "/"
                    });

                    res.status(200).json({ loggedIn: true });
                    resolve();
                } catch (err) {
                    reject(err);
                }
            });
        }
    });
};

export default main;

What am I missing, and how can I cookies from the backend in the login function inside my next.js on my local machine? And what changes will I need to make in my production set up? (I'll be using NginX on my server).

Thanks in advance...

Upvotes: 0

Views: 2198

Answers (2)

Jean-Marie
Jean-Marie

Reputation: 1

Set 'use client' in the login service file from the frontend.

'use client';

const adminLogin = async (url: string, data: any) => {}

Upvotes: 0

Coyoteazul
Coyoteazul

Reputation: 123

You are not telling axios to set the cookies, so it just ignores any cookie it gets

try {
        const result = await axios.post(url, formData, { headers: { "Content-Type": "multipart/form-data" },withCredentials: true });
        console.log(result);
        return result.data;
    } catch (error) {
        const err = error as AxiosError;
        return err.response!.data;
    }

Upvotes: 0

Related Questions