Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cookie Headers Not Forwarded Correctly in React Router and Backend Integration #10488

Open
sidyr6002 opened this issue Feb 20, 2025 · 0 comments

Comments

@sidyr6002
Copy link

sidyr6002 commented Feb 20, 2025

Reproduction

You can access the backend code: https://github.com/sidyr6002/chat-app-backend,
frontend code: https://github.com/sidyr6002/chat-app-frontend

Frontend Setup

  1. Create an .env file with the following content:
    BACKEND_URL=http://localhost:3000
    SESSION_SECRET=edb906c05d1aa272eca98019cbf637bf9983c043c02b00a5c7a0df5e041b0447
  2. Run the following commands:
    pnpm install
    pnpm dev

Backend Setup

  1. Set up the database:
    cd /database
    docker compose up
  2. Create an .env file with the following content:
    DATABASE_URL="postgres://user:password@localhost:5432/chatapp?schema=public"
    JWT_SECRET=string
    JWT_REFRESH_SECRET=string
    JWT_EXPIRATION="15m"
  3. Run the following commands:
    pnpm install
    pnpm start:dev

System Info

System:
    OS: Linux 6.11 Ubuntu 24.04.2 LTS 24.04.2 LTS (Noble Numbat)
    CPU: (8) x64 Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
    Memory: 1.37 GB / 7.61 GB
    Container: Yes
    Shell: 5.2.21 - /usr/bin/bash
  Binaries:
    Node: 20.15.1 - ~/.nvm/versions/node/v20.15.1/bin/node
    Yarn: 1.22.22 - ~/.nvm/versions/node/v20.15.1/bin/yarn
    npm: 10.8.2 - ~/.nvm/versions/node/v20.15.1/bin/npm
    pnpm: 9.15.3 - ~/.local/share/pnpm/pnpm
    bun: 1.1.10 - ~/.local/share/reflex/bun/bin/bun
  Browsers:
    Chrome: 133.0.6943.98

Used Package Manager

pnpm

Expected Behavior

The headers set in the backend should be reflected in the browser. The backend sets cookies (myRefreshToken and accessToken) during the sign-in process.

Backend Code (NestJS):

@ApiOkResponse({ type: AccessTokenDto })
@Post('signin')
async login(
    @Body() signInDto: SingInDto,
    @Res({ passthrough: true }) res: Response,
): Promise<AccessTokenDto> {
    const { accessToken, refreshToken } = await this.authService.signIn(
        signInDto.email,
        signInDto.password,
    );
    res.cookie('myRefreshToken', refreshToken, {
        httpOnly: true,
        secure: false,
        sameSite: 'lax',
        maxAge: 7 * 24 * 60 * 60 * 1000,
    });
    return { accessToken };
}

Frontend Code (React Router Action):

export const action: ActionFunction = async ({ request }) => {
    const formData = await request.formData();
    const email = formData.get('email');
    const password = formData.get('password');
    const validation = signInSchema.safeParse({
        email,
        password,
    });
    if (!validation.success) {
        return Response.json(
            { errors: validation.error.format() },
            { status: 400 }
        );
    }
    try {
        const backendUrl = process.env.BACKEND_URL || '';
        const response = await axios.post(
            `${backendUrl}/auth/signin`,
            { email, password },
            {
                headers: { 'Content-Type': 'application/json' },
                withCredentials: true,
            }
        );
        const { accessToken } = response.data;
        console.log("accessToken", accessToken);
        console.log('Response headers:', response.headers);

        const session = await getSession(request.headers.get('Cookie'));
        session.set('accessToken', accessToken);
        const sessionCookie = await commitSession(session);

        return redirect("/", {
            headers: {
                "Set-Cookie": sessionCookie,
            },
        });
    } catch (error) {
        console.error('Sign-in error:', error);
        return Response.json(
            { error: "Invalid credentials or server error." },
            { status: 401 }
        );
    }
};

The signin API sets the myRefreshToken cookie in the backend and sends the accessToken to the frontend. Both cookies should appear in the browser.

Actual Behavior

Only one cookie (accessToken) is being set in the browser. The myRefreshToken cookie, which is set by the backend, does not appear in the browser.

Upon inspecting the network request in the browser's developer tools, the response headers from the backend include both cookies:

Set-Cookie: myRefreshToken=...; HttpOnly; Path=/; SameSite=Lax; Max-Age=604800
Set-Cookie: accessToken=...; Path=/; HttpOnly

However, in the browser's "Application" tab, only the accessToken cookie is visible.

Image

Additional Information

  • The withCredentials: true option is set in the Axios request.
  • The backend's CORS configuration allows credentials.
  • The SameSite attribute for the myRefreshToken cookie is set to lax.

Observation:

The response headers returned by the backend during the sign-in process contain the myRefreshToken cookies, as confirmed by logging console.log('Response headers:', response.headers);. While the headers appear correct in the network response and logs, the myRefreshToken cookie is not being set in the browser, even though it is present in the Set-Cookie header of the response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant