mirror of https://github.com/buster-so/buster.git
Merge pull request #909 from buster-so/nate/get-from-cookie
supabase session with a cookie
This commit is contained in:
commit
f37fd4d6e3
|
@ -1,32 +1,4 @@
|
|||
import { parse } from '@supabase/ssr';
|
||||
import { parseCookies } from '@tanstack/react-start/server';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { getExpiresAtMilliseconds } from './expiration-helpers';
|
||||
|
||||
export const checkTokenValidityFromToken = (token: string | undefined) => {
|
||||
const decoded = decodeJwtToken(token);
|
||||
};
|
||||
|
||||
export const decodeJwtToken = (token: string | undefined) => {
|
||||
try {
|
||||
const decoded = jwtDecode(token || '');
|
||||
return decoded as { exp?: number };
|
||||
} catch {
|
||||
console.error('Error decoding token', token);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getExpiresAtFromToken = (token: string | undefined) => {
|
||||
try {
|
||||
const expiresAtDecoded = decodeJwtToken(token)?.exp ?? 0;
|
||||
return getExpiresAtMilliseconds(expiresAtDecoded);
|
||||
} catch {
|
||||
console.error('Error decoding token', token);
|
||||
// If token is missing/invalid, report that it is effectively expired now
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
let supabaseCookieName = '';
|
||||
|
||||
function parseJwt(payload: string) {
|
||||
// Remove the prefix if present
|
||||
|
@ -44,19 +16,43 @@ function parseJwt(payload: string) {
|
|||
return JSON.parse(jsonStr);
|
||||
}
|
||||
|
||||
export const getSupabaseCookie = () => {
|
||||
const supabaseCookieRaw = Object.entries(parseCookies()).find(
|
||||
([name]) => name.startsWith('sb-') && name.endsWith('-auth-token')
|
||||
)?.[1];
|
||||
if (!supabaseCookieRaw) {
|
||||
return '';
|
||||
}
|
||||
export const getSupabaseCookieClient = async () => {
|
||||
const supabaseCookieRaw = await getSupabaseCookieRawClient();
|
||||
|
||||
try {
|
||||
const decodedCookie = parseJwt(supabaseCookieRaw);
|
||||
} catch (error) {
|
||||
console.error('nope');
|
||||
}
|
||||
|
||||
return '';
|
||||
const decodedCookie = parseJwt(supabaseCookieRaw || '') as {
|
||||
access_token: string;
|
||||
expires_at: number;
|
||||
expires_in: number;
|
||||
token_type: 'bearer';
|
||||
user: {
|
||||
id: string;
|
||||
is_anonymous: boolean;
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
return decodedCookie;
|
||||
};
|
||||
|
||||
function listAllCookies() {
|
||||
return Object.fromEntries(
|
||||
document.cookie.split('; ').map((cookieStr) => {
|
||||
const [name, ...rest] = cookieStr.split('=');
|
||||
return [name, rest.join('=')];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function getSupabaseCookieRawClient() {
|
||||
const getCookieByKey = (d: [string, string][]) => {
|
||||
const cookie = d.find(([name]) => name.startsWith('sb-') && name.endsWith('-auth-token'));
|
||||
supabaseCookieName = cookie?.[0] || '';
|
||||
return cookie?.[1];
|
||||
};
|
||||
|
||||
if (supabaseCookieName) {
|
||||
const Cookies = await import('js-cookie').then((m) => m.default);
|
||||
return Cookies.get(supabaseCookieName);
|
||||
}
|
||||
return getCookieByKey(Object.entries(listAllCookies()));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { getCookie } from '@tanstack/react-start/server';
|
||||
import Cookies from 'js-cookie';
|
||||
import type { LayoutSize } from '@/components/ui/layouts/AppLayout';
|
||||
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/create-auto-save-id';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import cookies from 'js-cookie';
|
||||
import Cookies from 'js-cookie';
|
||||
import React, { useRef } from 'react';
|
||||
import { Button } from '@/components/ui/buttons/Button';
|
||||
import { Text } from '@/components/ui/typography/Text';
|
||||
|
@ -83,7 +83,7 @@ export const PassingInitialLayoutFromCookies: Story = {
|
|||
const tagRaw = 'this-is-a-test';
|
||||
const tag = createAutoSaveId(tagRaw);
|
||||
const initialLayoutFromCookie: { value: number } = JSON.parse(
|
||||
cookies.get(tag) ?? '["100px", "auto"]'
|
||||
Cookies.get(tag) ?? '["100px", "auto"]'
|
||||
);
|
||||
|
||||
const initialLayout: LayoutSize = [
|
||||
|
@ -589,11 +589,11 @@ const ThreePanelWithAnimationExample = () => {
|
|||
};
|
||||
|
||||
const initialLayoutParent = parseLayout(
|
||||
cookies.get('app-splitter-three-panel-outer') ?? '',
|
||||
Cookies.get('app-splitter-three-panel-outer') ?? '',
|
||||
'left'
|
||||
);
|
||||
const initialLayoutInner = parseLayout(
|
||||
cookies.get('app-splitter-three-panel-inner') ?? '',
|
||||
Cookies.get('app-splitter-three-panel-inner') ?? '',
|
||||
'right'
|
||||
);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { isServer } from '@tanstack/react-query';
|
||||
import cookies from 'js-cookie';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useState } from 'react';
|
||||
import { useMemoizedFn } from './useMemoizedFn';
|
||||
import { useMount } from './useMount';
|
||||
|
@ -43,7 +43,7 @@ const setCookie = (
|
|||
const expires = new Date(Date.now() + expirationTime);
|
||||
const { domain, path = '/', secure = true, sameSite = 'lax' } = options;
|
||||
|
||||
cookies.set(name, value, {
|
||||
Cookies.set(name, value, {
|
||||
expires,
|
||||
path,
|
||||
domain,
|
||||
|
@ -57,7 +57,7 @@ const removeCookie = (name: string, options: CookieOptions = {}): void => {
|
|||
if (isServer) return;
|
||||
|
||||
const { domain, path = '/' } = options;
|
||||
cookies.remove(name, { path, domain });
|
||||
Cookies.remove(name, { path, domain });
|
||||
};
|
||||
|
||||
export function useCookieState<T>(
|
||||
|
@ -79,7 +79,7 @@ export function useCookieState<T>(
|
|||
return typeof defaultValue === 'function' ? (defaultValue as () => T)() : defaultValue;
|
||||
});
|
||||
|
||||
// Get initial value from cookies or use default
|
||||
// Get initial value from Cookies or use default
|
||||
const getInitialValue = useMemoizedFn((): T | undefined => {
|
||||
// Prefer explicitly provided initialValue if present
|
||||
if (initialValue !== undefined) {
|
||||
|
@ -87,7 +87,7 @@ export function useCookieState<T>(
|
|||
}
|
||||
|
||||
try {
|
||||
const cookieValue = cookies.get(key);
|
||||
const cookieValue = Cookies.get(key);
|
||||
|
||||
if (!cookieValue) {
|
||||
return executeBustStorage();
|
||||
|
@ -128,7 +128,7 @@ export function useCookieState<T>(
|
|||
|
||||
const [state, setState] = useState<T | undefined>(() => getInitialValue());
|
||||
|
||||
// Initialize state from cookies on mount
|
||||
// Initialize state from Cookies on mount
|
||||
useMount(() => {
|
||||
setState(getInitialValue());
|
||||
});
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { isTokenExpired } from '@/api/auth_helpers/expiration-helpers';
|
||||
import { isTokenAlmostExpired, isTokenExpired } from '@/api/auth_helpers/expiration-helpers';
|
||||
import {
|
||||
getSupabaseSessionServerFn,
|
||||
getSupabaseUserServerFn,
|
||||
} from '@/api/server-functions/getSupabaseSession';
|
||||
import { isServer } from '@/lib/window';
|
||||
import { getSupabaseCookieClient } from '../../api/auth_helpers/cookie-helpers';
|
||||
import { getBrowserClient } from './client';
|
||||
|
||||
const supabase = getBrowserClient();
|
||||
|
@ -11,7 +12,7 @@ const supabase = getBrowserClient();
|
|||
export const getSupabaseSession = async () => {
|
||||
const { data: sessionData, error: sessionError } = isServer
|
||||
? await getSupabaseSessionServerFn()
|
||||
: await supabase.auth.getSession(); //10 - 15ms locally, maybe consider getting it from the cookie instead. console the supabase object it had it there.
|
||||
: await getClientSupabaseSessionFast();
|
||||
|
||||
if ((!sessionData?.session || sessionError) && !isServer) {
|
||||
return {
|
||||
|
@ -30,6 +31,29 @@ export const getSupabaseSession = async () => {
|
|||
};
|
||||
};
|
||||
|
||||
const getClientSupabaseSessionFast = async () => {
|
||||
try {
|
||||
const cookieRes = await getSupabaseCookieClient();
|
||||
const almostExpired = isTokenAlmostExpired(cookieRes.expires_at);
|
||||
if (!almostExpired) {
|
||||
return {
|
||||
data: {
|
||||
session: {
|
||||
access_token: cookieRes.access_token,
|
||||
isExpired: false,
|
||||
expires_at: cookieRes.expires_at,
|
||||
},
|
||||
},
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
//fail silently
|
||||
}
|
||||
|
||||
return await supabase.auth.getSession(); //100ms on server, that's why we're using the cookie instead.
|
||||
};
|
||||
|
||||
export const getSupabaseUser = async () => {
|
||||
const { data: userData } = isServer
|
||||
? await getSupabaseUserServerFn()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import cookies from 'js-cookie';
|
||||
import Cookies from 'js-cookie';
|
||||
import { getServerCookie } from '@/api/server-functions/getServerCookie';
|
||||
import { isServer } from './window';
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const detectClientTimezone = (): string => {
|
|||
export const setTimezoneInCookie = (timezone: string): void => {
|
||||
if (isServer || !timezone) return;
|
||||
|
||||
cookies.set(TIMEZONE_COOKIE_KEY, timezone, {
|
||||
Cookies.set(TIMEZONE_COOKIE_KEY, timezone, {
|
||||
expires: TIMEZONE_COOKIE_EXPIRY,
|
||||
sameSite: 'lax',
|
||||
secure: true,
|
||||
|
@ -46,7 +46,7 @@ export const getTimezoneFromCookie = async (): Promise<string | null> => {
|
|||
return cookieData || null;
|
||||
}
|
||||
|
||||
return cookies.get(TIMEZONE_COOKIE_KEY) || null;
|
||||
return Cookies.get(TIMEZONE_COOKIE_KEY) || null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue