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

[BUG] onLogin function does not work properly on vercel once deployed, but works on local development #1919

Open
1 task done
CesarBenavides777 opened this issue Jun 27, 2024 · 16 comments
Labels
needs: reproduction This issue needs to be reproduced independently type: bug Issue that causes incorrect or unexpected behavior

Comments

@CesarBenavides777
Copy link

Description

As a user I want to be able to use the onLogin function to properly log in and see the my-account page using the @faustwp/experimental-app-router package. The implementation shown in the example directory does in fact work with local development as intended. But when deployed to production on vercel you get a:

There was an error logging in the user

Steps to reproduce

  1. Clone https://github.com/CesarBenavides777/cesar-benavides
  2. Enter your own WP settings into the .env file
  3. Run bun dev
  4. Navigate to /login page and log in
  5. Works as expected
  6. Deploy to vercel
  7. Repeat step 4
  8. It does not work

Additional context

Related Discord discussion:
https://discord.com/channels/836253505944813629/1233495010440122419

Vercel Error Logs:

[GET] /api/faust/token?code=BHyAt9ZOeAApN%2FQ4b40C11EeT0neTyjYgYaBZAiOgbMfQj%2BjAF9YHyUs6CHvp8KSYKrcEjFJ8KXP8e9YwDG4nw%3D%3D&nxtProute=token status=500

image

Invalid response for authorize handler: TypeError: t.NextResponse is not a constructor
    at nW (/var/task/.next/server/chunks/25.js:49:3499)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:36258
    at async eR.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:26874)
    at async eR.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:37512)
    at async es (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:16:25465)
    at async en.responseCache.get.routeKind (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:17:1026)
    at async r6.renderToResponseWithComponentsImpl (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:17:508)
    at async r6.renderPageComponent (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:17:5121)
    at async r6.renderToResponseImpl (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:17:5708)

image

@faustwp/core Version

^3.0.3

@faustwp/cli Version

^3.0.2

FaustWP Plugin Version

1.3.2

WordPress Version

6.5.5

Additional environment details

Deployed on Vercel
WordPress is on instaWP

Cookies have been confirmed to work across domains.

WPGraphQL JWT Auth does in fact break this because of a wrong number of segments FYI

Links:
CMS: https://cms.cesarbenavides.com
Frontend: https://staging.cesarbenavides.com

Using the WPGraphQL CORS plugin for additional testing (doesn't work when installed or uninstalled)

Please confirm that you have searched existing issues in the repo.

  • Yes
@ChrisWiegman ChrisWiegman added type: bug Issue that causes incorrect or unexpected behavior needs: reproduction This issue needs to be reproduced independently labels Jun 27, 2024
@CesarBenavides777
Copy link
Author

I think it has something to do with how the Server object is passed into the tokenHandler when logged locally I see the full server object but when logged on production it doesn't seem to get the NextResponse object/constructor.

Production:

server Object [Module] {}

Locally:

server Object [Module] {
  ImageResponse: [Getter],
  NextRequest: [Getter],
  NextResponse: [Getter],
  URLPattern: [Getter],
  unstable_after: [Getter],
  userAgent: [Getter],
  userAgentFromString: [Getter]
}

For some reason production builds don't have access to this?

@CesarBenavides777
Copy link
Author

CesarBenavides777 commented Jun 27, 2024

I think importing NextResponse directly inside the tokenHandler seems to fix it!!!!

Patched this file
experimental-app-router/dist/server/routeHandler/tokenHandler.js

import { cookies } from 'next/headers.js';
import { getWpUrl, getWpSecret } from '../../faust-core-utils.js';
import { NextResponse } from 'next/server';

export async function tokenHandler(req, s) {
    var _a, _b;
    try {
        const secretKey = getWpSecret();
        if (!secretKey) {
            throw new Error('FAUST_SECRET_KEY must be set');
        }
        const { url } = req;
        const code = (_a = new URL(url).searchParams.get('code')) !== null && _a !== void 0 ? _a : undefined;
        const cookieStore = cookies();
        const cookieName = `${getWpUrl()}-rt`;
        const refreshToken = (_b = cookieStore.get(cookieName)) === null || _b === void 0 ? void 0 : _b.value;
        if (!refreshToken && !code) {
            return new Response(JSON.stringify({ error: 'Unauthorized' }), {
                status: 401,
                headers: {
                    'Content-Type': 'application/json',
                },
            });
        }
        const wpFaustAuthorizeEndpoint = `${getWpUrl()}/?rest_route=/faustwp/v1/authorize`;
        const response = await fetch(wpFaustAuthorizeEndpoint, {
            headers: {
                'Content-Type': 'application/json',
                'x-faustwp-secret': secretKey,
            },
            method: 'POST',
            body: JSON.stringify({
                code,
                refreshToken,
            }),
        });

        // Log response status and body
        console.log('Response status:', response.status);
        const responseBody = await response.text();
        console.log('Response body:', responseBody);

        if (!response.ok) {
            // @TODO Delete the cookie
            // cookieStore.delete(cookieName);
            // @TODO throw different errors based on response
            return new Response(JSON.stringify({ error: 'Unauthorized' }), {
                status: 401,
                headers: {
                    'Content-Type': 'application/json',
                },
            });
        }

        const data = JSON.parse(responseBody);

        const res = new NextResponse(JSON.stringify(data), { // Ensure correct usage
            status: 200,
        });

        console.log("data", data);
        console.log("res", res);

        res.cookies.set(cookieName, data.refreshToken, {
            secure: true,
            httpOnly: true,
            path: '/',
            expires: new Date(data.refreshTokenExpiration * 1000),
            sameSite: 'lax',
        });

        return res;
    } catch (err) {
        console.error('Invalid response for authorize handler:', err);
        return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
            status: 500,
            headers: {
                'Content-Type': 'application/json',
            },
        });
    }
}

@CesarBenavides777
Copy link
Author

I also updated the useFormState hook to useActionState using React 19 and Next 15 RCs. On the login page.

"use client";

import { useFormStatus } from "react-dom";
import { useActionState } from "react";
import { loginAction } from "./action";

function SubmitButton() {
  const status = useFormStatus();

  return (
    <button disabled={status.pending}>
      {status.pending ? "Loading..." : "Login"}
    </button>
  );
}

export default function Page() {
  const [state, formAction] = useActionState(loginAction, {});

  return (
    <>
      <h2>Login</h2>

      <form action={formAction}>
        <fieldset>
          <label htmlFor="usernameEmail">Username or Email</label>
          <input type="name" name="usernameEmail" />
        </fieldset>

        <fieldset>
          <label htmlFor="password">Password</label>
          <input type="password" name="password" />
        </fieldset>

        <SubmitButton />

        {state.error && (
          <p dangerouslySetInnerHTML={{ __html: state.error }}></p>
        )}
      </form>
    </>
  );
}

@theodesp
Copy link
Member

theodesp commented Jun 28, 2024

@CesarBenavides777 Thank you I will take a look. I remember we had to provide a workaround at one point
42ded80

in order to fix some build issues. I think Vercel is messing around with those runtimes. It does not make sense that this is happening. I will check your solution as well.

@cwhatley
Copy link

cwhatley commented Jul 2, 2024

FYI, I'm using experimental app router with the same dependencies listed here and am not having any issues on a production instance on vercel. Is there perhaps a vercel configuration that you @CesarBenavides777 has set that might be interfering here? We're not using any of the premium vercel features at this point.

@CesarBenavides777
Copy link
Author

@cwhatley

Nothing outside the ordinary:
image
image

Maybe not using npm or yarn? Using bun currently

@cwhatley
Copy link

cwhatley commented Jul 2, 2024

I'm using yarn + corepack, node 20.x and have the same env vars set.

I do have a monorepo, so my build + start look like:

corepack enable && yarn workspace XXX build and yarn workspace XXX start

@akalex-x
Copy link

I am experience the same issue when deploying to vercel, let me know if you find a fix!

@cgar420
Copy link

cgar420 commented Aug 21, 2024

I have the same problem on Vercel and Netlify

@josephfusco josephfusco pinned this issue Aug 21, 2024
@cgar420
Copy link

cgar420 commented Sep 12, 2024

Something in the newer versions of the Faust packages is causing it to break. Downgrading to the packages below fixes the issue
@josephfusco @CesarBenavides777 @theodesp

{ "name": "@faustwp/app-router-example", "private": true, "type": "module", "scripts": { "dev": "faust dev", "build": "faust build", "generate": "faust generatePossibleTypes", "stylesheet": "faust generateGlobalStylesheet", "start": "faust start" }, "dependencies": { "@apollo/client": "^3.8.0", "@apollo/experimental-nextjs-app-support": "^0.5.1", "@faustwp/cli": "^2.0.0", "@faustwp/core": "^2.1.2", "@faustwp/experimental-app-router": "^0.2.2", "graphql": "^16.7.1", "next": "^14.0.1", "react": "^18.2.0", "react-dom": "^18.2.0" }, "engines": { "node": ">=18", "npm": ">=8" }, "devDependencies": { "@types/node": "^20.6.3", "@types/react": "^18.2.36", "@types/react-dom": "^18.2.14", "typescript": "^5.2.2" } }

@CesarBenavides777
Copy link
Author

Updated patches to @faustwp/experimental-app-router that allow it to work with Next JS 15, fixes auth login, and previews .

diff --git a/dist/server/auth/fetchTokens.js b/dist/server/auth/fetchTokens.js
index ac898fa819de4e249c1bd0a70d8214ffed56426e..5ed8940de63d78c96729f965ce3a5cee02b486c5 100644
--- a/dist/server/auth/fetchTokens.js
+++ b/dist/server/auth/fetchTokens.js
@@ -1,3 +1,4 @@
+
 // eslint-disable-next-line import/extensions
 import { cookies } from 'next/headers.js';
 import { getUrl } from '../../lib/getUrl.js';
@@ -11,7 +12,8 @@ import { getWpUrl } from '../../faust-core-utils.js';
  */
 export async function fetchTokens(code) {
     var _a;
-    const cookieStore = cookies();
+    const cookieStore = await cookies();
+    console.log('cookieStore', cookieStore);
     const cookieName = `${getWpUrl()}-rt`;
     if (!((_a = cookieStore.get(cookieName)) === null || _a === void 0 ? void 0 : _a.value) && !code) {
         // The user is not authenticated.
diff --git a/dist/server/routeHandler/index.js b/dist/server/routeHandler/index.js
index 59e5d2087f55396a43699cc342736d74c67b806d..fc82bc67b7209bd8ee631d4b8143d00e21487578 100644
--- a/dist/server/routeHandler/index.js
+++ b/dist/server/routeHandler/index.js
@@ -1,10 +1,9 @@
-import { notFound } from 'next/navigation.js';
+import { notFound } from 'next/navigation';
 import * as server from 'next/server.js';
 import { tokenHandler } from './tokenHandler.js';
 export async function GetFn(req) {
     const { pathname } = new URL(req.url);
     switch (pathname) {
-        case '/api/faust/token/':
         case '/api/faust/token': {
             return tokenHandler(req, server);
         }
diff --git a/dist/server/routeHandler/tokenHandler.js b/dist/server/routeHandler/tokenHandler.js
index 236a4912cc0e775155ead38a8b2349ee48c0f3ad..282c16daacec6ccebe25907a93d3fdde981bbed9 100644
--- a/dist/server/routeHandler/tokenHandler.js
+++ b/dist/server/routeHandler/tokenHandler.js
@@ -1,6 +1,8 @@
 import { cookies } from 'next/headers.js';
 import { getWpUrl, getWpSecret } from '../../faust-core-utils.js';
-export async function tokenHandler(req, s) {
+import { NextResponse } from 'next/server'
+
+export async function tokenHandler(req, p) {
     var _a, _b;
     try {
         const secretKey = getWpSecret();
@@ -9,7 +11,7 @@ export async function tokenHandler(req, s) {
         }
         const { url } = req;
         const code = (_a = new URL(url).searchParams.get('code')) !== null && _a !== void 0 ? _a : undefined;
-        const cookieStore = cookies();
+        const cookieStore = await cookies();
         const cookieName = `${getWpUrl()}-rt`;
         const refreshToken = (_b = cookieStore.get(cookieName)) === null || _b === void 0 ? void 0 : _b.value;
         if (!refreshToken && !code) {
@@ -59,7 +61,7 @@ export async function tokenHandler(req, s) {
          * @TODO Set the refresh token cookie with the new refresh token
          * and expiration.
          */
-        const res = new s.NextResponse(JSON.stringify(data), {
+        const res = new NextResponse(JSON.stringify(data), {
             status: 200,
         });
         res.cookies.set(cookieName, data.refreshToken, {
diff --git a/dist/server-actions/logoutAction.js b/dist/server-actions/logoutAction.js
index 1057ac5343a446d7f9aad81c3e68a023ae6a4a2f..edf84f7894ae1923c159cfb3abbc8c7d9920bcaa 100644
--- a/dist/server-actions/logoutAction.js
+++ b/dist/server-actions/logoutAction.js
@@ -3,7 +3,7 @@ import { getWpUrl } from '../faust-core-utils.js';
 export async function onLogout() {
     'use server';
     const wpCookieName = `${getWpUrl()}-rt`;
-    const cookieStore = cookies();
+    const cookieStore = await cookies();
     const wpCookie = cookieStore.get(wpCookieName);
     if (wpCookie === null || wpCookie === void 0 ? void 0 : wpCookie.name) {
         cookieStore.delete(wpCookieName);
diff --git a/dist/server-actions/utils/setRefreshToken.js b/dist/server-actions/utils/setRefreshToken.js
index d54c28a955f79692007a490ac5d34cf9305bb3a1..434c6c6cb1f608ddd6bd023c674907c83632de82 100644
--- a/dist/server-actions/utils/setRefreshToken.js
+++ b/dist/server-actions/utils/setRefreshToken.js
@@ -8,7 +8,7 @@ import { getWpUrl } from '../../faust-core-utils.js';
  * @param refreshTokenExpiration The refresh token expiration from the token endpoint
  */
 export async function setRefreshToken(refreshToken, refreshTokenExpiration) {
-    const cookieStore = cookies();
+    const cookieStore = await cookies();
     const cookieName = `${getWpUrl()}-rt`;
     cookieStore.set(cookieName, refreshToken, {
         secure: true,

This is applied using bun patch @faustwp/experimental-app-router

With the changset above in a file called @faustwp%[email protected] within a folder at root called patches

@CesarBenavides777
Copy link
Author

CesarBenavides777 commented Nov 1, 2024

Everything at its latest version
Used here:
https://github.com/CesarBenavides777/cesar-benavides

For my personal website:
https://www.cesarbenavides.com/

@CesarBenavides777
Copy link
Author

@theodesp any update on this? I'm pretty sure the changes I listed above make this fully compatible with Next 15. I would be happy to submit a PR too.

@CesarBenavides777
Copy link
Author

Basically awaiting any cookie calls and importing NextResponse directly.

@theodesp
Copy link
Member

Hey @CesarBenavides777 feel free to open a PR if you want!

CesarBenavides777 added a commit to CesarBenavides777/faustjs that referenced this issue Nov 26, 2024
CesarBenavides777 added a commit to CesarBenavides777/faustjs that referenced this issue Nov 26, 2024
CesarBenavides777 added a commit to CesarBenavides777/faustjs that referenced this issue Nov 26, 2024
CesarBenavides777 added a commit to CesarBenavides777/faustjs that referenced this issue Nov 26, 2024
@CesarBenavides777
Copy link
Author

Hey @CesarBenavides777 feel free to open a PR if you want!

#1994

Let me know if I need to change anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs: reproduction This issue needs to be reproduced independently type: bug Issue that causes incorrect or unexpected behavior
Projects
None yet
Development

No branches or pull requests

6 participants