From 222bdec204f5f58f349d16e820cf7e85ea7db17d Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Fri, 23 Jun 2023 18:10:40 +0200 Subject: [PATCH 1/2] feat: add Sentry events to monitor random logouts --- .../interfaces/SignIn/SignInForm.tsx | 7 +++ .../interfaces/SignIn/SignInWithGitHub.tsx | 7 +++ .../interfaces/SignIn/SignUpForm.tsx | 3 ++ studio/lib/auth.tsx | 4 +- studio/lib/local-storage.ts | 53 ++++++++++++++++++- 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/studio/components/interfaces/SignIn/SignInForm.tsx b/studio/components/interfaces/SignIn/SignInForm.tsx index d9adb6e4b6a..4d20f3fa307 100644 --- a/studio/components/interfaces/SignIn/SignInForm.tsx +++ b/studio/components/interfaces/SignIn/SignInForm.tsx @@ -3,10 +3,12 @@ import { useQueryClient } from '@tanstack/react-query' import { useStore } from 'hooks' import { usePushNext } from 'hooks/misc/useAutoAuthRedirect' import { auth } from 'lib/gotrue' +import { incrementSignInClicks } from 'lib/local-storage' import Link from 'next/link' import { useRef, useState } from 'react' import { Button, Form, Input } from 'ui' import { object, string } from 'yup' +import * as Sentry from '@sentry/nextjs' const signInSchema = object({ email: string().email('Must be a valid email').required('Email is required'), @@ -33,6 +35,11 @@ const SignInForm = () => { token = captchaResponse?.response ?? null } + const signInClicks = incrementSignInClicks() + if (signInClicks > 1) { + Sentry.captureMessage('Sign in without previous sing out detected') + } + const { error } = await auth.signInWithPassword({ email, password, diff --git a/studio/components/interfaces/SignIn/SignInWithGitHub.tsx b/studio/components/interfaces/SignIn/SignInWithGitHub.tsx index 36199298427..d6cad1da894 100644 --- a/studio/components/interfaces/SignIn/SignInWithGitHub.tsx +++ b/studio/components/interfaces/SignIn/SignInWithGitHub.tsx @@ -1,7 +1,9 @@ import { useState } from 'react' import { BASE_PATH } from 'lib/constants' import { auth, getReturnToPath } from 'lib/gotrue' +import { incrementSignInClicks } from 'lib/local-storage' import { Button, IconGitHub } from 'ui' +import * as Sentry from '@sentry/nextjs' const SignInWithGitHub = () => { const [loading, setLoading] = useState(false) @@ -10,6 +12,11 @@ const SignInWithGitHub = () => { setLoading(true) try { + const signInClicks = incrementSignInClicks() + if (signInClicks > 1) { + Sentry.captureMessage('Sign in without previous sign out detected') + } + const { error } = await auth.signInWithOAuth({ provider: 'github', options: { diff --git a/studio/components/interfaces/SignIn/SignUpForm.tsx b/studio/components/interfaces/SignIn/SignUpForm.tsx index b84ae70b19e..b206e80e8a6 100644 --- a/studio/components/interfaces/SignIn/SignUpForm.tsx +++ b/studio/components/interfaces/SignIn/SignUpForm.tsx @@ -7,6 +7,7 @@ import { useStore } from 'hooks' import { post } from 'lib/common/fetch' import { API_URL, BASE_PATH } from 'lib/constants' import { passwordSchema } from 'lib/schemas' +import { resetSignInClicks } from 'lib/local-storage' import PasswordConditionsHelper from './PasswordConditionsHelper' const signUpSchema = passwordSchema.shape({ @@ -33,6 +34,8 @@ const SignUpForm = () => { token = captchaResponse?.response ?? null } + resetSignInClicks() + const response = await post(`${API_URL}/signup`, { email, password, diff --git a/studio/lib/auth.tsx b/studio/lib/auth.tsx index 762cc3da511..e8fcc561adf 100644 --- a/studio/lib/auth.tsx +++ b/studio/lib/auth.tsx @@ -12,7 +12,7 @@ import { useProfileQuery } from 'data/profile/profile-query' import { useStore } from 'hooks' import Telemetry from 'lib/telemetry' import { GOTRUE_ERRORS, IS_PLATFORM } from './constants' -import { clearLocalStorage } from './local-storage' +import { clearLocalStorage, resetSignInClicks } from './local-storage' import { useProfileCreateMutation } from 'data/profile/profile-create-mutation' export const AuthContext = AuthContextInternal @@ -99,6 +99,8 @@ export function useSignOut() { const queryClient = useQueryClient() return useCallback(async () => { + resetSignInClicks() + const result = await gotrueClient.signOut() clearLocalStorage() await queryClient.resetQueries() diff --git a/studio/lib/local-storage.ts b/studio/lib/local-storage.ts index 611bcd1616c..d9747df8753 100644 --- a/studio/lib/local-storage.ts +++ b/studio/lib/local-storage.ts @@ -1,4 +1,11 @@ -export const LOCAL_STORAGE_KEYS_ALLOWLIST = ['graphiql:theme', 'theme', 'supabaseDarkMode'] +import { IS_PLATFORM } from './constants' + +export const LOCAL_STORAGE_KEYS_ALLOWLIST = [ + 'graphiql:theme', + 'theme', + 'supabaseDarkMode', + 'supabase.dashboard.sign_in_clicks', +] export function clearLocalStorage() { for (const key in localStorage) { @@ -7,3 +14,47 @@ export function clearLocalStorage() { } } } + +function inferSignInClicks() { + if (localStorage.getItem('supabase.dashboard.sign_in_clicks')) { + return + } + + localStorage.setItem( + 'supabase.dashboard.sign_in_clicks', + localStorage.getItem('supabase.dashboard.auth.token') ? '1' : '0' + ) +} + +export function getSignInClicks(): number { + let count: number | null = null + + try { + count = JSON.parse(localStorage.getItem('supabase.dashboard.sign_in_clicks') || '0') + } catch (e: any) { + // do nothing + } + + return count || 0 +} + +export function incrementSignInClicks(): number { + const clicks = getSignInClicks() + + localStorage.setItem('supabase.dashboard.sign_in_clicks', JSON.stringify(clicks + 1)) + + return clicks + 1 +} + +export function resetSignInClicks(): number { + const clicks = getSignInClicks() + + localStorage.setItem('supabase.dashboard.sign_in_clicks', '0') + + return clicks +} + +if (globalThis && globalThis.localStorage && IS_PLATFORM) { + // populate the value based on the current local storage state + inferSignInClicks() +} From e501ee765b082fd47267ac1cb64a21ea4799e829 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Sat, 24 Jun 2023 20:43:40 +0800 Subject: [PATCH 2/2] chore: update image versions for docker/docker-compose.yml --- docker/docker-compose.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3e28b3acdc4..ae7e6c0c148 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,7 +9,7 @@ version: "3.8" services: studio: container_name: supabase-studio - image: supabase/studio:20230330-99fed3d + image: supabase/studio:20230622-d8395eb restart: unless-stopped healthcheck: test: @@ -57,7 +57,7 @@ services: auth: container_name: supabase-auth - image: supabase/gotrue:v2.60.7 + image: supabase/gotrue:v2.62.1 depends_on: db: # Disable this if you are using an external Postgres database @@ -130,7 +130,7 @@ services: realtime: container_name: realtime-dev.supabase-realtime - image: supabase/realtime:v2.5.1 + image: supabase/realtime:v2.10.1 depends_on: db: # Disable this if you are using an external Postgres database @@ -168,7 +168,7 @@ services: storage: container_name: supabase-storage - image: supabase/storage-api:v0.28.2 + image: supabase/storage-api:v0.40.4 depends_on: db: # Disable this if you are using an external Postgres database @@ -211,7 +211,7 @@ services: imgproxy: container_name: supabase-imgproxy - image: darthsim/imgproxy:v3.13 + image: darthsim/imgproxy:v3.8.0 healthcheck: test: [ "CMD", "imgproxy", "health" ] timeout: 5s @@ -227,7 +227,7 @@ services: meta: container_name: supabase-meta - image: supabase/postgres-meta:v0.60.7 + image: supabase/postgres-meta:v0.66.3 depends_on: db: # Disable this if you are using an external Postgres database @@ -243,7 +243,7 @@ services: functions: container_name: supabase-edge-functions - image: supabase/edge-runtime:v1.2.12 + image: supabase/edge-runtime:v1.4.2 restart: unless-stopped environment: JWT_SECRET: ${JWT_SECRET} @@ -263,7 +263,7 @@ services: # Comment out everything below this point if you are using an external Postgres database db: container_name: supabase-db - image: supabase/postgres:15.1.0.54-rc0 + image: supabase/postgres:15.1.0.90 healthcheck: test: pg_isready -U postgres -h localhost interval: 5s