mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
refactor: Optimize Sentry user ids (#47342)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved signed-in user identification in error tracking to use the current authenticated user ID directly. * Removed reliance on previously stored anonymous identifiers, reducing inconsistent user matching across sessions. * **Tests** * Added coverage to confirm error tracking user identity is set when a user is signed in and not set when signed out. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
4f80bb70cd
commit
a9565c1645
3 files changed
+34
-25
No files matched your search
@@ -7,6 +7,7 @@ const mocks = vi.hoisted(() => ({
|
||||
identify: vi.fn(),
|
||||
useUser: vi.fn(),
|
||||
useOrganizationsQuery: vi.fn(),
|
||||
setUser: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('common', async (importOriginal) => {
|
||||
@@ -34,7 +35,7 @@ vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@sentry/nextjs', () => ({
|
||||
setUser: vi.fn(),
|
||||
setUser: (...args: unknown[]) => mocks.setUser(...args),
|
||||
}))
|
||||
|
||||
const USER_ID = 'user-abc-123'
|
||||
@@ -48,6 +49,7 @@ describe('Telemetry — posthog identify mirroring', () => {
|
||||
mocks.identify.mockReset()
|
||||
mocks.useUser.mockReset()
|
||||
mocks.useOrganizationsQuery.mockReset()
|
||||
mocks.setUser.mockReset()
|
||||
})
|
||||
|
||||
it('fires identify with both org_count and signup_timestamp when user and orgs are loaded', async () => {
|
||||
@@ -146,3 +148,32 @@ describe('Telemetry — posthog identify mirroring', () => {
|
||||
expect(mocks.identify).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Telemetry — Sentry user identification', () => {
|
||||
beforeEach(() => {
|
||||
mocks.identify.mockReset()
|
||||
mocks.useUser.mockReset()
|
||||
mocks.useOrganizationsQuery.mockReset()
|
||||
mocks.setUser.mockReset()
|
||||
})
|
||||
|
||||
it('sets the raw (un-hashed) user id on Sentry', async () => {
|
||||
mocks.useUser.mockReturnValue({ id: USER_ID, created_at: CREATED_AT })
|
||||
mocks.useOrganizationsQuery.mockReturnValue({ data: orgs(1) })
|
||||
|
||||
render(<Telemetry />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks.setUser).toHaveBeenCalledWith({ id: USER_ID })
|
||||
})
|
||||
})
|
||||
|
||||
it('does not set a Sentry user when the user is not logged in', async () => {
|
||||
mocks.useUser.mockReturnValue(null)
|
||||
mocks.useOrganizationsQuery.mockReturnValue({ data: orgs(1) })
|
||||
|
||||
render(<Telemetry />)
|
||||
await new Promise((r) => setTimeout(r, 10))
|
||||
expect(mocks.setUser).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
import { LOCAL_STORAGE_KEYS, PageTelemetry, posthogClient, safeLocalStorage, useUser } from 'common'
|
||||
import { PageTelemetry, posthogClient, useUser } from 'common'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useConsentToast } from 'ui-patterns/consent'
|
||||
|
||||
@@ -7,16 +7,6 @@ import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { API_URL, IS_PLATFORM } from '@/lib/constants'
|
||||
|
||||
const getAnonId = async (id: string) => {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(id)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data as BufferSource)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
const base64String = btoa(hashArray.map((byte) => String.fromCharCode(byte)).join(''))
|
||||
|
||||
return base64String
|
||||
}
|
||||
|
||||
export function Telemetry() {
|
||||
// Although this is "technically" breaking the rules of hooks
|
||||
// IS_PLATFORM never changes within a session, so this won't cause any issues
|
||||
@@ -64,17 +54,7 @@ export function Telemetry() {
|
||||
return
|
||||
}
|
||||
|
||||
const setSentryId = async () => {
|
||||
let sentryUserId = safeLocalStorage.getItem(LOCAL_STORAGE_KEYS.SENTRY_USER_ID)
|
||||
if (!sentryUserId) {
|
||||
sentryUserId = await getAnonId(user?.id)
|
||||
safeLocalStorage.setItem(LOCAL_STORAGE_KEYS.SENTRY_USER_ID, sentryUserId)
|
||||
}
|
||||
Sentry.setUser({ id: sentryUserId })
|
||||
}
|
||||
|
||||
// if an error happens, continue without setting a sentry id
|
||||
setSentryId().catch((e) => console.error(e))
|
||||
Sentry.setUser({ id: user.id })
|
||||
}, [user?.id])
|
||||
|
||||
return (
|
||||
|
||||
@@ -58,8 +58,6 @@ export const LOCAL_STORAGE_KEYS = {
|
||||
LINTER_SHOW_FOOTER: 'supabase-linter-show-footer',
|
||||
// Key to track account deletion requests
|
||||
ACCOUNT_DELETION_REQUEST: 'supabase-account-deletion-request',
|
||||
// Used for storing a user id when sending reports to Sentry. The id is hashed for anonymity.
|
||||
SENTRY_USER_ID: 'supabase-sentry-user-id',
|
||||
// Used for storing the last sign in method used by the user
|
||||
LAST_SIGN_IN_METHOD: 'supabase-last-sign-in-method',
|
||||
// Key to track the last selected schema. The project ref is intentionally put at the end for easier search in the browser console.
|
||||
|
||||
Reference in new issue
Block a user