gladly-team / next-firebase-auth

Simple Firebase authentication for all Next.js rendering strategies
https://nfa-example-git-v1x-gladly-team.vercel.app/
MIT License
1.34k stars 291 forks source link

Infinite rediect to login page when deploy to vercel #639

Closed UnkAtreus closed 1 year ago

UnkAtreus commented 1 year ago

When filing a bug report, please confirm you've done the following:

  1. Have you set onVerifyTokenError and onTokenRefreshError in your config and checked for any helpful errors? yes
  2. Have you set debug: true in your config and read through server-side and client-side debug logs for any helpful messages? yes
  3. Have you tried the example app with your own Firebase credentials? yes
  4. Have you read through the troubleshooting Q&A? yes

Describe the bug A clear and concise description of the bug. It work fine in localhost but when I deploy to vercel, Its redirect to login page infinite and have this log in login page before redirect and repeat this process again and again next-firebase-auth: [withAuthUserSSR] Calling "withAuthUserSSR" / "withAuthUserTokenSSR". next-firebase-auth: [getUserFromCookies] Attempting to get user info from cookies via the ID token. next-firebase-auth: [getUserFromCookies] Failed to retrieve the ID token from cookies. This will happen if the user is not logged in, the provided cookie values are invalid, or the cookie values don't align with your cookie settings. The user will be unauthenticated. next-firebase-auth: [withAuthUserSSR] Redirecting to login.

Vercel : vercel log

Infinite redirection :

https://www.example.com/login?destination=https%3A%2F%2Fwww.example.com%2Flogin%3Fdestination%3Dhttps%253A%252F%252Fwww.example.com%252Flogin%253Fdestination%253Dhttps%25253A%25252F%25252Fwww.example.com%25252Flogin%25253Fdestination%25253D ... and continue appending until the url is too long.

I have the following setup: Login page: `import React, { useState } from 'react'; import { withAuthUser, AuthAction } from 'next-firebase-auth'; import { getAuth, signInWithEmailAndPassword } from 'firebase/auth'; import { initializeApp } from 'firebase/app'; import { Button, Steps } from 'antd'; import { Form } from 'antd'; import { Input } from 'antd'; import { decode as base64_decode } from 'base-64'; import { message } from 'antd'; import { useRouter } from 'next/router'; import Image from 'next/image'; import Logo from '@/assets/zenith-logo.png';

const firebaseConfig = JSON.parse( base64_decode(process.env.NEXT_PUBLIC_FIREBASE_CONFIG) );

const Auth = () => { const [loading, setLoading] = useState(false); const app = initializeApp(firebaseConfig);

const auth = getAuth(app);
const router = useRouter();

const onFinish = async ({ email, password }) => {
    setLoading(true);
    signInWithEmailAndPassword(auth, email, password)
        .then(() => {
            message.success('Login successful');
            // router.push('/populations');
        })
        .catch((error) => {
            const errorMessage = error.message;
            message.error('Error: ' + errorMessage);
            setLoading(false);
        });
};

return (
    <div className="min-h-screen bg-slate-50 p-16">
        <div className="m-auto max-w-xl ">
            <div className="mb-6">
                <Steps
                    current={0}
                    items={[
                        {
                            title: 'Login',
                        },
                        {
                            title: 'Select Population',
                        },
                    ]}
                />
            </div>

            <div className="m-auto max-w-sm">
                <Image
                    src={Logo}
                    alt="ZENITH"
                    className="mx-auto mb-8 w-64"
                />
                <Form
                    layout="vertical"
                    initialValues={{ remember: true }}
                    onFinish={onFinish}
                    autoComplete="off"
                >
                    <Form.Item
                        label="email"
                        name="email"
                        rules={[
                            {
                                required: true,
                                message: 'Please input your email!',
                            },
                        ]}
                    >
                        <Input type="email" />
                    </Form.Item>

                    <Form.Item
                        label="Password"
                        name="password"
                        rules={[
                            {
                                required: true,
                                message: 'Please input your password!',
                            },
                        ]}
                    >
                        <Input.Password />
                    </Form.Item>

                    <Form.Item>
                        <Button
                            type="primary"
                            htmlType="submit"
                            block
                            loading={loading}
                        >
                            Login
                        </Button>
                    </Form.Item>
                </Form>
            </div>
        </div>
    </div>
);

};

export default withAuthUser({ whenAuthed: AuthAction.REDIRECT_TO_APP, appPageURL: 'populations', })(Auth); `

Config: `import { init } from 'next-firebase-auth'; import absoluteUrl from 'next-absolute-url'; import { decode as base64_decode } from 'base-64';

const TWELVE_DAYS_IN_MS = 12 60 60 24 1000;

const initAuth = () => { const firebaseConfig = JSON.parse( base64_decode(process.env.NEXT_PUBLIC_FIREBASE_CONFIG) ); init({ debug: true, onVerifyTokenError: (err) => { console.log( 'πŸš€ ~ file: initAuth.js ~ onVerifyTokenError ~ err:', err ); }, onTokenRefreshError: (err) => { console.log( 'πŸš€ ~ file: initAuth.js ~ onTokenRefreshError ~ err:', err ); }, onLoginRequestError: (err) => { console.log( 'πŸš€ ~ file: initAuth.js ~ onLoginRequestError ~ err:', err ); }, // This demonstrates setting a dynamic destination URL when // redirecting from app pages. Alternatively, you can simply // specify authPageURL: '/auth-ssr'. authPageURL: ({ ctx }) => { const isServerSide = typeof window === 'undefined'; const origin = isServerSide ? absoluteUrl(ctx.req).origin : window.location.origin; const destPath = typeof window === 'undefined' ? ctx.resolvedUrl : window.location.href; const destURL = new URL(destPath, origin); return /login?destination=${encodeURIComponent(destURL)}; },

    // This demonstrates setting a dynamic destination URL when
    // redirecting from auth pages. Alternatively, you can simply
    // specify `appPageURL: '/'`.
    appPageURL: ({ ctx }) => {
        const isServerSide = typeof window === 'undefined';
        const origin = isServerSide
            ? absoluteUrl(ctx.req).origin
            : window.location.origin;
        const params = isServerSide
            ? new URL(ctx.req.url, origin).searchParams
            : new URLSearchParams(window.location.search);
        const destinationParamVal = params.get('destination')
            ? decodeURIComponent(params.get('destination'))
            : undefined;

        // By default, go to the index page if the destination URL
        // is invalid or unspecified.
        let destURL = '/';
        if (destinationParamVal) {
            // Verify the redirect URL host is allowed.
            // https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/11-Client_Side_Testing/04-Testing_for_Client_Side_URL_Redirect
            const allowedHosts = [
                'localhost:3000',
                'example.com',
                'www.example.com',
            ];
            const allowed =
                allowedHosts.indexOf(new URL(destinationParamVal).host) >
                -1;
            if (allowed) {
                destURL = destinationParamVal;
            } else {
                // eslint-disable-next-line no-console
                console.warn(
                    `Redirect destination host must be one of ${allowedHosts.join(
                        ', '
                    )}.`
                );
            }
        }
        return destURL;
    },
    loginAPIEndpoint: '/api/login',
    logoutAPIEndpoint: '/api/logout',
    firebaseAdminInitConfig: {
        credential: {
            projectId: firebaseConfig.projectId,
            clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
            // Using JSON to handle newline problems when storing the
            // key as a secret in Vercel. See:
            // https://github.com/vercel/vercel/issues/749#issuecomment-707515089
            privateKey: process.env.FIREBASE_PRIVATE_KEY
                ? JSON.parse(process.env.FIREBASE_PRIVATE_KEY)
                : undefined,
        },
    },
    firebaseClientInitConfig: firebaseConfig,
    cookies: {
        name: 'Zenith',
        keys: [
            process.env.COOKIE_SECRET_CURRENT,
            process.env.COOKIE_SECRET_PREVIOUS,
        ],
        httpOnly: true,
        maxAge: TWELVE_DAYS_IN_MS,
        overwrite: true,
        path: '/',
        sameSite: 'strict',
        secure: 'true',
        signed: true,
    },
});

};

export default initAuth; `

Versions

next-firebase-auth version: 1.0.0-canary.19 Firebase JS SDK: 9.19.0 Next.js: latest

To Reproduce Steps to reproduce the behavior: After redirect to login I get next-firebase-auth: [withAuthUserSSR] Calling "withAuthUserSSR" / "withAuthUserTokenSSR". next-firebase-auth: [getUserFromCookies] Attempting to get user info from cookies via the ID token. next-firebase-auth: [getUserFromCookies] Failed to retrieve the ID token from cookies. This will happen if the user is not logged in, the provided cookie values are invalid, or the cookie values don't align with your cookie settings. The user will be unauthenticated. next-firebase-auth: [withAuthUserSSR] Redirecting to login. and redirect to login again, creating an endless loop.

Expected behavior A clear and concise description of what you expected to happen. should be able to redirect the to /login correctly

Debug and error logs Please provide debug logs or errors from onVerifyTokenError and onTokenRefreshError.

Additional context Add any other context about the problem here.