mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## Context Just extracting the fixes which I think are applicable from this [PR](https://github.com/supabase/supabase/pull/47695) Main files are - `apps/studio/hooks/analytics/useLogsQuery.tsx` - `packages/common/auth.tsx` - `packages/common/feature-flags.tsx` ## Changes involved - Adjust `useLogsQuery` to accept an object as prop, rather than 4 individual params - This one doesn't address any Sentry issues, but is just a improvement to the function's API imo, more readable - Adjust how user email is retrieved in `feature-flags` - Related Sentry issue [here](https://supabase.sentry.io/issues/7592718607/?project=5459134) - The error is a bit vague, but Claude's attempt to fix looks alright in general IMO - Minimally verified that feature flags are loading as expected still <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved log-related screens and queries for more reliable loading and filtering across the app. * Fixed profile and account data handling so identity details are retrieved more consistently. * Improved authentication handling to better recognize missing user data and keep the app stable. * Updated feature flag personalization to use more accurate account information. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
186 lines
4.1 KiB
TypeScript
186 lines
4.1 KiB
TypeScript
'use client'
|
|
|
|
import type { AuthError, Session } from '@supabase/supabase-js'
|
|
import {
|
|
createContext,
|
|
PropsWithChildren,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from 'react'
|
|
|
|
import { clearLocalStorage } from './constants/local-storage'
|
|
import { gotrueClient, type User } from './gotrue'
|
|
|
|
export type { User }
|
|
|
|
const DEFAULT_SESSION: any = {
|
|
access_token: undefined,
|
|
expires_at: 0,
|
|
expires_in: 0,
|
|
refresh_token: '',
|
|
token_type: '',
|
|
user: {
|
|
aud: '',
|
|
app_metadata: {},
|
|
confirmed_at: '',
|
|
created_at: '',
|
|
email: '',
|
|
email_confirmed_at: '',
|
|
id: '',
|
|
identities: [],
|
|
last_signed_in_at: '',
|
|
phone: '',
|
|
role: '',
|
|
updated_at: '',
|
|
user_metadata: {},
|
|
},
|
|
} as unknown as Session
|
|
|
|
/* Auth Context */
|
|
|
|
type AuthState =
|
|
| {
|
|
session: Session | null
|
|
error: AuthError | null
|
|
isLoading: false
|
|
}
|
|
| {
|
|
session: null
|
|
error: AuthError | null
|
|
isLoading: true
|
|
}
|
|
|
|
export type AuthContext = { refreshSession: () => Promise<Session | null> } & AuthState
|
|
|
|
export const AuthContext = createContext<AuthContext>({
|
|
session: null,
|
|
error: null,
|
|
isLoading: true,
|
|
refreshSession: () => Promise.resolve(null),
|
|
})
|
|
|
|
export type AuthProviderProps = {
|
|
alwaysLoggedIn?: boolean
|
|
}
|
|
|
|
export const AuthProvider = ({
|
|
alwaysLoggedIn,
|
|
children,
|
|
}: PropsWithChildren<AuthProviderProps>) => {
|
|
const [state, setState] = useState<AuthState>({ session: null, error: null, isLoading: true })
|
|
|
|
useEffect(() => {
|
|
let mounted = true
|
|
gotrueClient.initialize().then(({ error }) => {
|
|
if (mounted && error !== null) {
|
|
setState((prev) => ({ ...prev, error }))
|
|
}
|
|
})
|
|
|
|
return () => {
|
|
mounted = false
|
|
}
|
|
}, [])
|
|
|
|
// Keep the session in sync
|
|
useEffect(() => {
|
|
const {
|
|
data: { subscription },
|
|
} = gotrueClient.onAuthStateChange((_event, session) => {
|
|
setState((prev) => ({
|
|
session,
|
|
// If there is a session, we clear the error
|
|
error: session !== null ? null : prev.error,
|
|
isLoading: false,
|
|
}))
|
|
})
|
|
|
|
return subscription.unsubscribe
|
|
}, [])
|
|
|
|
// Helper method to refresh the session.
|
|
// For example after a user updates their profile
|
|
const refreshSession = useCallback(async () => {
|
|
const {
|
|
data: { session },
|
|
} = await gotrueClient.refreshSession()
|
|
|
|
return session
|
|
}, [])
|
|
|
|
const value = useMemo(() => {
|
|
if (alwaysLoggedIn) {
|
|
return { session: DEFAULT_SESSION, error: null, isLoading: false, refreshSession } as const
|
|
} else {
|
|
return { ...state, refreshSession } as const
|
|
}
|
|
}, [state, refreshSession])
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
|
}
|
|
|
|
/* Auth Utils */
|
|
|
|
export const useAuth = () => useContext(AuthContext)
|
|
|
|
export const useSession = () => useAuth().session
|
|
|
|
export const useUser = () => {
|
|
const user = useSession()?.user ?? null
|
|
|
|
// auth-js substitutes a throwing proxy for `user` when it was configured with
|
|
// `userStorage` and hasn't migrated the user into storage yet. Treat that as
|
|
// "no user" instead of crashing whenever a property on it is read.
|
|
if (user && (user as any).__isUserNotAvailableProxy) return null
|
|
|
|
return user
|
|
}
|
|
|
|
export const useIsUserLoading = () => useAuth().isLoading
|
|
|
|
export const useIsLoggedIn = () => {
|
|
const user = useUser()
|
|
|
|
return user !== null
|
|
}
|
|
|
|
export const useAuthError = () => useAuth().error
|
|
|
|
export const useIsMFAEnabled = () => {
|
|
const user = useUser()
|
|
|
|
return user !== null && user.factors && user.factors.length > 0
|
|
}
|
|
|
|
export const signOut = async () => await gotrueClient.signOut()
|
|
|
|
export const logOut = async () => {
|
|
await signOut()
|
|
clearLocalStorage()
|
|
}
|
|
|
|
gotrueClient.onAuthStateChange((_event, _session) => {})
|
|
|
|
/**
|
|
* Gets a current access token.
|
|
*
|
|
* Calls getSession, which will refresh the token if needed.
|
|
*/
|
|
export async function getAccessToken() {
|
|
// ignore if server-side
|
|
if (typeof window === 'undefined') return undefined
|
|
|
|
const {
|
|
data: { session },
|
|
error,
|
|
} = await gotrueClient.getSession()
|
|
if (error) {
|
|
throw error
|
|
}
|
|
|
|
return session?.access_token
|
|
}
|