Skip to content

Large Auth Cookie Split Into 2 Causing Realtime Failures, etc #963

@ghost

Description

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

Using Keycloak, cookies can be quite large. This has been an issue already (see supabase/realtime#761 and supabase/realtime#762) with some fixes implemented already (see, e.g., supabase/cli#1784).

I am noticing that at a certain character length, supabase-js is splitting authentication cookies into two (see screenshots below). Not only does this ignore any custom cookie name, but it also creates errors when tying to retrieve the cookie manually, as well as, I suspect, issues with realtime data connections automatically refreshing (it would explain why I am experiencing the open issue here https://github.com/supabase/realtime-js/issues/274 along with possibly other users).

I am using the recommended approach for Next.js, of using @supabase/ssr (https://github.com/supabase/auth-helpers/blob/main/packages/ssr/src/createBrowserClient.ts).

Update 1

It looks like the first cookie has an exact length of 3180. This seems to coincide with the chunker used by supabase/ssr:

const MAX_CHUNK_SIZE = 3180;

https://github.com/supabase/auth-helpers/blob/a68e78bc9cb863217db75ac561de93874a85aafa/packages/ssr/src/utils/chunker.ts#L6

I'm wondering if I should just implement my own version of createBrowserClient that I can use to strip out unnecessry information from the JSON object... wondering what fields could be removed? The total length of the cookie I paste below is 4805, which would exceed the maximum length of 4096 stipulated by RFC (I believe they are RFC 2109 (#6.3), RFC 2965 (#5.3), and RFC 6265 from searching around).

Thoughts:

  • is provider_refresh_token needed (I would assume so)?
  • is provider_token needed? This one is obscenely long and perhaps could be omitted (if I have to, by manually implementing createBrowserClient?)
  • is user needed? this section of the JSON is also massive and (if unnecessary) something I'd like to omit, as I generally fetch the user directly from the server using the access token)

To Reproduce

Unless you are using Keycloak, which is generating obscenely long cookies, it may be hard to reproduce this. However, I have attached below an exact JSON copy of what the cookie would look like before it is being split into two somewhere. I have also attached the react code we are using to create our client.

src/hooks/useSupabaseClient.tsx

"use client";

import { useAppSelector } from "@inspire-tms/vault/components/store/hooks";
import { createBrowserClient } from "@supabase/ssr";
import Cookies from "js-cookie";
import { useState } from "react";



export const useSupabaseClient = (): SupabaseClient => {
  const state = useAppSelector(app => app.supabase);
  if (!state.supabase) throw new Error("[useSupabaseClient] function invoked outside of SupabaseProvider!");

  const AUTH_COOKIE = "supabase-inspire-tms-vault";
  const isAuthCookie = (name: string) => name.endsWith("auth-token") || (name.includes(".") && name.split(".")[0].endsWith("auth-token"));

  const [ client ] = useState(() => createBrowserClient(state.supabase!.url, state.supabase!.anonKey, {
    cookieOptions: {
      name: AUTH_COOKIE
    },
    cookies: {
      get: (key) => {
        if (key.includes("auth-token-code-verifier")) return Cookies.get(key);
        if (isAuthCookie(key)) return Cookies.get(AUTH_COOKIE);

        // bad cookie
        console.error("request to get cookie rejected:", { key });
      },
      set: (key, value) => {
        (() => {
          console.log(`set '${key}' to '${value}'`)
          if (key.includes("auth-token-code-verifier")) return Cookies.set(key, value);
          if (isAuthCookie(key)) return Cookies.set("supabase-inspire-tms-vault", value);

          // bad cookie
          console.error("request to set cookie rejected:", { key, value });
        })();
      },
      remove: (key) => {
        if (isAuthCookie(key)) Cookies.remove(AUTH_COOKIE);
        else Cookies.remove(key);
      }
    }
  }));

  return client;
};

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

Note in the screenshot below how the cookie is split, literally breaking any attempt to JSON.parse the cookie as it is no longer a valid JSON object (see the firstconsole.log which begins with a proper '{"access_token":"eyJhb but ends abruptly in NjZXNzI')

Screenshot 2024-01-31 at 7 09 15 PM

If I manually copy and paste the two separate cookies, I get the following (valid) JSON object:

{
   "access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzA2NzQ5Njc0LCJpYXQiOjE3MDY3NDYwNzQsImlzcyI6Imh0dHA6Ly8xMjcuMC4wLjE6NTQzMjEvYXV0aC92MSIsInN1YiI6ImFhZDA3YTRlLTAxNzYtNDQ3YS04ZWNlLWNjNjU1OGYzYTkyZSIsImVtYWlsIjoibmJhcnJvd0BpbnNwaXJldG1zdGVjaC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImtleWNsb2FrIiwicHJvdmlkZXJzIjpbImtleWNsb2FrIl19LCJ1c2VyX21ldGFkYXRhIjp7ImVtYWlsIjoibmJhcnJvd0BpbnNwaXJldG1zdGVjaC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZnVsbF9uYW1lIjoiTmljaG9sYXMgQmFycm93IiwiaXNzIjoiaHR0cHM6Ly9vbmUtaWQuaW5zcGlyZXRtc2Nvbm5lY3QuY29tL3JlYWxtcy9pbnNwaXJlIiwibmFtZSI6Ik5pY2hvbGFzIEJhcnJvdyIsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwicHJvdmlkZXJfaWQiOiI5M2I3ZmJiYi02Y2I4LTQ1MmMtYWJlNC01NTcwYjUxNjM4ZGUiLCJzdWIiOiI5M2I3ZmJiYi02Y2I4LTQ1MmMtYWJlNC01NTcwYjUxNjM4ZGUifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJvYXV0aCIsInRpbWVzdGFtcCI6MTcwNjc0NjA3NH1dLCJzZXNzaW9uX2lkIjoiNTBiYWQyMDgtMGQ5Ni00OTRlLTkwZmYtNjRiZDA0MWRjODQ1In0.NIR5kt8yCUsRfBbqxoEUTfnPb0hlrkVwVgGXSLLI3MQ",
   "token_type":"bearer",
   "expires_in":3600,
   "expires_at":1706749674,
   "refresh_token":"ImhX3j7x3b1ASBeaqXHhyw",
   "user":{
      "id":"aad07a4e-0176-447a-8ece-cc6558f3a92e",
      "aud":"authenticated",
      "role":"authenticated",
      "email":"nbarrow@inspiretmstech.com",
      "email_confirmed_at":"2024-01-31T23:52:25.239995Z",
      "phone":"",
      "confirmed_at":"2024-01-31T23:52:25.239995Z",
      "last_sign_in_at":"2024-02-01T00:07:54.970531429Z",
      "app_metadata":{
         "provider":"keycloak",
         "providers":[
            "keycloak"
         ]
      },
      "user_metadata":{
         "email":"nbarrow@inspiretmstech.com",
         "email_verified":true,
         "full_name":"Nicholas Barrow",
         "iss":"https://one-id.inspiretmsconnect.com/realms/inspire",
         "name":"Nicholas Barrow",
         "phone_verified":false,
         "provider_id":"93b7fbbb-6cb8-452c-abe4-5570b51638de",
         "sub":"93b7fbbb-6cb8-452c-abe4-5570b51638de"
      },
      "identities":[
         {
            "identity_id":"d8f31cee-84e8-4b6e-a335-dce2c41d4868",
            "id":"93b7fbbb-6cb8-452c-abe4-5570b51638de",
            "user_id":"aad07a4e-0176-447a-8ece-cc6558f3a92e",
            "identity_data":{
               "email":"nbarrow@inspiretmstech.com",
               "email_verified":true,
               "full_name":"Nicholas Barrow",
               "iss":"https://one-id.inspiretmsconnect.com/realms/inspire",
               "name":"Nicholas Barrow",
               "phone_verified":false,
               "provider_id":"93b7fbbb-6cb8-452c-abe4-5570b51638de",
               "sub":"93b7fbbb-6cb8-452c-abe4-5570b51638de"
            },
            "provider":"keycloak",
            "last_sign_in_at":"2024-01-31T23:52:25.228859Z",
            "created_at":"2024-01-31T23:52:25.228898Z",
            "updated_at":"2024-02-01T00:07:53.775429Z",
            "email":"nbarrow@inspiretmstech.com"
         }
      ],
      "created_at":"2024-01-31T23:52:25.220433Z",
      "updated_at":"2024-02-01T00:07:54.975724Z"
   },
   "provider_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJXZTlwZ0pJMDJKT3BqeGtfSnBBRW43VVNpOGtCRDBPTDhrLXVhUTRPTFJZIn0.eyJleHAiOjE3MDY3NDYzNzMsImlhdCI6MTcwNjc0NjA3MywiYXV0aF90aW1lIjoxNzA2NzQ1MTQ0LCJqdGkiOiIwMGIzODA1ZS02OGU2LTQyMWItODVmMy0zNmUwZWY1N2I1N2IiLCJpc3MiOiJodHRwczovL29uZS1pZC5pbnNwaXJldG1zY29ubmVjdC5jb20vcmVhbG1zL2luc3BpcmUiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiOTNiN2ZiYmItNmNiOC00NTJjLWFiZTQtNTU3MGI1MTYzOGRlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiaW5zcGlyZS10bXMiLCJzZXNzaW9uX3N0YXRlIjoiY2EzYjUyYzQtYjFiOS00Njk3LTk1YzYtN2M2ODFmYTdlMDI0IiwiYWNyIjoiMCIsImFsbG93ZWQtb3JpZ2lucyI6WyIiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtaW5zcGlyZSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiY2EzYjUyYzQtYjFiOS00Njk3LTk1YzYtN2M2ODFmYTdlMDI0IiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJOaWNob2xhcyBCYXJyb3ciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJiYXJyb3duaWNob2xhcyIsImdpdmVuX25hbWUiOiJOaWNob2xhcyIsImZhbWlseV9uYW1lIjoiQmFycm93IiwiZW1haWwiOiJuYmFycm93QGluc3BpcmV0bXN0ZWNoLmNvbSJ9.ARx9UqYRW8ojbZVWPliG_14BMhEibmdIQe-jcJnuRiwgJMnZEuOs-fjVlfwF-OrxxJHsLOMB-_e-Z4v3ZuFPBs9QFW-3VJm3Kwltttf_lV79nQcOp-zXO_DtYPKkNEEZckmEfpjxmhigNaHjOB3Rw8tvQicTpTTEccuwGmqoChE1StrBVlxYc9uDIAeKIqFDYfkT7kwhSNZEJkqlrxy0RMwq_p6HDOeaoJbJVSxg5UzdpXkJSrkohUGgkui85sIDxDrPRTL6N-ANm32SJNL5T-gQSYDMU8JyMgdiCB2rDlGSpoVP9tXZiB8zY9l_vjgopttGvN65Al5tkqB8aJ3zXQ",
   "provider_refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0N2Y1NTRmMS04MmM0LTRmNDUtOGFmNi1hMjQ1MmNkMjhkMzQifQ.eyJleHAiOjE3MDY3NDc4NzMsImlhdCI6MTcwNjc0NjA3MywianRpIjoiYzgyNDhmZjUtNWU5YS00N2QwLWJiZjgtZWZmYTMxY2RhNjFlIiwiaXNzIjoiaHR0cHM6Ly9vbmUtaWQuaW5zcGlyZXRtc2Nvbm5lY3QuY29tL3JlYWxtcy9pbnNwaXJlIiwiYXVkIjoiaHR0cHM6Ly9vbmUtaWQuaW5zcGlyZXRtc2Nvbm5lY3QuY29tL3JlYWxtcy9pbnNwaXJlIiwic3ViIjoiOTNiN2ZiYmItNmNiOC00NTJjLWFiZTQtNTU3MGI1MTYzOGRlIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Imluc3BpcmUtdG1zIiwic2Vzc2lvbl9zdGF0ZSI6ImNhM2I1MmM0LWIxYjktNDY5Ny05NWM2LTdjNjgxZmE3ZTAyNCIsInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiJjYTNiNTJjNC1iMWI5LTQ2OTctOTVjNi03YzY4MWZhN2UwMjQifQ.HTJEpTHA3bwttIcz6nQcnolY3EOUVy7yo9lnka6CQ2k"
}

System information

  • Version of supabase-js:
{
  "@supabase/ssr": "^0.0.10",
  "@supabase/supabase-js": "^2.39.3"
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions