pbteja1998 / remix-auth-email-link

MIT License
92 stars 22 forks source link

Error: Cookie length will exceed browser maximum. Length: number #37

Open aayestashn opened 1 year ago

aayestashn commented 1 year ago

Describe the bug

As a user, I am receiving the email with the magic link, but production gave me the following error message after submitting the form with the email.

Error: Cookie length will exceed browser maximum. Length: 4503

after calling the function:

  await auth.authenticate("email-link", request, {
    successRedirect: "/auth/login",
    // If this is not set, any error will be throw and the ErrorBoundary will be
    // rendered.
    failureRedirect: "/auth/login",
  });

This is the track trace:

The throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length); method in the remix createCookieSessionStorageFactory function

/**
 * Creates and returns a SessionStorage object that stores all session data
 * directly in the session cookie itself.
 *
 * This has the advantage that no database or other backend services are
 * needed, and can help to simplify some load-balanced scenarios. However, it
 * also has the limitation that serialized session data may not exceed the
 * browser's maximum cookie size. Trade-offs!
 *
 * @see https://remix.run/utils/sessions#createcookiesessionstorage
 */
const createCookieSessionStorageFactory = createCookie => ({
  cookie: cookieArg
} = {}) => {
  let cookie = cookies.isCookie(cookieArg) ? cookieArg : createCookie((cookieArg === null || cookieArg === void 0 ? void 0 : cookieArg.name) || "__session", cookieArg);
  sessions.warnOnceAboutSigningSessionCookie(cookie);
  return {
    async getSession(cookieHeader, options) {
      return sessions.createSession(cookieHeader && (await cookie.parse(cookieHeader, options)) || {});
    },
    async commitSession(session, options) {
      let serializedCookie = await cookie.serialize(session.data, options);
      if (serializedCookie.length > 4096) {
        throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length);
      }
      return serializedCookie;
    },
    async destroySession(_session, options) {
      return cookie.serialize("", {
        ...options,
        expires: new Date(0)
      });
    }
  };
};

In the function of the library authenticate and in the line const cookie = await sessionStorage.commitSession(session);

    async authenticate(request, sessionStorage, options) {
        var _a;
        const session = await sessionStorage.getSession(request.headers.get('Cookie'));
        const form = new URLSearchParams(await request.text());
        // This should only be called in an action if it's used to start the login process
        if (request.method === 'POST') {
            if (!options.successRedirect) {
                throw new Error('Missing successRedirect. The successRedirect is required for POST requests.');
            }
            // get the email address from the request body
            const emailAddress = form.get(this.emailField);
            // if it doesn't have an email address,
            if (!emailAddress || typeof emailAddress !== 'string') {
                const message = 'Missing email address.';
                if (!options.failureRedirect) {
                    throw new Error(message);
                }
                session.flash(this.sessionErrorKey, { message });
                const cookie = await sessionStorage.commitSession(session);
                throw (0, server_runtime_1.redirect)(options.failureRedirect, {
                    headers: { 'Set-Cookie': cookie },
                });
            }
            try {
                // Validate the email address
                await this.validateEmail(emailAddress);
                const domainUrl = this.getDomainURL(request);
                const magicLink = await this.sendToken(emailAddress, domainUrl, form);
                session.set(this.sessionMagicLinkKey, await this.encrypt(magicLink));
                session.set(this.sessionEmailKey, emailAddress);
                throw (0, server_runtime_1.redirect)(options.successRedirect, {
                    headers: {
                        'Set-Cookie': await sessionStorage.commitSession(session),
                    },
                });
            }
            catch (error) {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                if (error.status === 302) {
                    // If it's a redirect, then just throw the redirect as it is
                    throw error;
                }
                if (!options.failureRedirect) {
                    throw error;
                }
                const { message } = error;
                session.flash(this.sessionErrorKey, { message });
                const cookie = await sessionStorage.commitSession(session);
                throw (0, server_runtime_1.redirect)(options.failureRedirect, {
                    headers: { 'Set-Cookie': cookie },
                });
            }
        }
        let user;
        try {
            // If we get here, the user clicked on the magic link inside email
            const magicLink = (_a = session.get(this.sessionMagicLinkKey)) !== null && _a !== void 0 ? _a : '';
            const { emailAddress: email, form } = await this.validateMagicLink(request.url, await this.decrypt(magicLink));
            // now that we have the user email we can call verify to get the user
            user = await this.verify({ email, form, magicLinkVerify: true });
        }
        catch (error) {
            // if something happens, we should redirect to the failureRedirect
            // and flash the error message, or just throw the error if failureRedirect
            // is not defined
            if (!options.failureRedirect) {
                throw error;
            }
            const { message } = error;
            session.flash(this.sessionErrorKey, { message });
            const cookie = await sessionStorage.commitSession(session);
            throw (0, server_runtime_1.redirect)(options.failureRedirect, {
                headers: { 'Set-Cookie': cookie },
            });
        }
        if (!options.successRedirect) {
            return user;
        }
        // remove the magic link and email from the session
        session.unset(this.sessionMagicLinkKey);
        session.unset(this.sessionEmailKey);
        session.set(options.sessionKey, user);
        const cookie = await sessionStorage.commitSession(session);
        throw (0, server_runtime_1.redirect)(options.successRedirect, {
            headers: { 'Set-Cookie': cookie },
        });
    }

Is any way to print all the values of the session to check if for some reason I am putting external data?

Your Example Website or App

https://aa8d-190-135-204-71.ngrok-free.app/auth/login

Steps to Reproduce the Bug or Issue

  1. Using the library fill the email, and then trigger the authentication process.

Expected behavior

As a user, I am receiving the email with the magic link, but production gave me the following error message after submitting the form with the email. Error: Cookie length will exceed browser maximum. Length: 4503

Screenshots or Videos

Screenshot 2023-06-02 at 5 21 36 PM Screenshot 2023-06-02 at 5 21 43 PM

Platform

Additional context

No response