mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
fix(studio): emit sign_in on totp challenge (#49755)
The dashboard's `sign_in` event never fires when a user completes a TOTP challenge: `SignInForm` only tracks when no MFA challenge is needed, and the /sign-in-mfa page only tracks on mount when the assurance level is already satisfied (OAuth/SSO returns). Sign-ins that go through the actual MFA form were invisible to analytics, and the login audit event was missing on the same path. **Changed:** - **MFA-challenged sign-ins now tracked**: `SignInMfaForm` fires `sign_in` (reading the same `method` query param the page mount site reads) plus the login audit event on successful TOTP verification, in the sign-in context only. The forgot-password flow stays untracked: it is a reset, not a sign-in. - **Password+MFA sign-ins report `method: email`**: `SignInForm` now passes `?method=email` when routing to /sign-in-mfa instead of falling through to `unknown`. - **Partner TOTP sign-ins carry their provider**: `SignInPartner` now passes `?method=<partner>` when routing to /sign-in-mfa, matching the raw-provider-name convention the other entry points use. - **Join caveat documented**: the `SignInEvent` doc comment now notes the event is captured server-side and races the identify call, so it is not a valid funnel join key across the auth boundary. ## Linear - fixes GROWTH-1156 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added sign-in method details to MFA redirects for email and partner authentication, improving sign-in flow tracking. * Added telemetry and login auditing for successful MFA sign-ins while keeping forgot-password flows untracked. * **Documentation** * Clarified sign-in event tracking coverage, including OAuth providers, server-side capture, anonymous identifiers, and the sign-in page. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
3146650a5a
commit
3338be76f0
4 files changed
+18
-5
No files matched your search
@@ -81,7 +81,7 @@ export const SignInForm = () => {
|
||||
if (data) {
|
||||
if (data.currentLevel !== data.nextLevel) {
|
||||
toast.success(`You need to provide your second factor authentication`, { id: toastId })
|
||||
const url = buildPathWithParams('/sign-in-mfa')
|
||||
const url = buildPathWithParams('/sign-in-mfa?method=email')
|
||||
router.replace(url)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import type { Factor } from '@supabase/supabase-js'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useAuthError } from 'common'
|
||||
import { useAuthError, useParams } from 'common'
|
||||
import { Lock } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -13,10 +13,12 @@ import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
import z from 'zod'
|
||||
|
||||
import { AlertError } from '@/components/ui/AlertError'
|
||||
import { useAddLoginEvent } from '@/data/misc/audit-login-mutation'
|
||||
import { useMfaChallengeAndVerifyMutation } from '@/data/profile/mfa-challenge-and-verify-mutation'
|
||||
import { useMfaListFactorsQuery } from '@/data/profile/mfa-list-factors-query'
|
||||
import { useSignOut } from '@/lib/auth'
|
||||
import { getReturnToPath } from '@/lib/gotrue'
|
||||
import { useTrack } from '@/lib/telemetry/track'
|
||||
|
||||
const schema = z.object({
|
||||
code: z.string().min(1, 'MFA Code is required'),
|
||||
@@ -39,6 +41,10 @@ export const SignInMfaForm = ({ context = 'sign-in' }: SignInMfaFormProps) => {
|
||||
const router = useRouter()
|
||||
const signOut = useSignOut()
|
||||
const queryClient = useQueryClient()
|
||||
const { method: signInMethod = 'unknown' } = useParams()
|
||||
|
||||
const track = useTrack()
|
||||
const { mutate: addLoginEvent } = useAddLoginEvent()
|
||||
|
||||
const [selectedFactor, setSelectedFactor] = useState<Factor | null>(null)
|
||||
const form = useForm<z.infer<typeof schema>>({
|
||||
@@ -61,6 +67,11 @@ export const SignInMfaForm = ({ context = 'sign-in' }: SignInMfaFormProps) => {
|
||||
isSuccess,
|
||||
} = useMfaChallengeAndVerifyMutation({
|
||||
onSuccess: async () => {
|
||||
if (context === 'sign-in') {
|
||||
track('sign_in', { category: 'account', method: signInMethod })
|
||||
addLoginEvent({})
|
||||
}
|
||||
|
||||
await queryClient.resetQueries()
|
||||
|
||||
if (context === 'forgot-password') {
|
||||
|
||||
@@ -21,7 +21,7 @@ export const SignInPartner = () => {
|
||||
try {
|
||||
await auth.signInWithIdToken({ provider: partner, token })
|
||||
} finally {
|
||||
router.replace({ pathname: '/sign-in-mfa' })
|
||||
router.replace({ pathname: '/sign-in-mfa', query: { method: partner } })
|
||||
}
|
||||
} else {
|
||||
router.replace({ pathname: '/sign-in' })
|
||||
|
||||
@@ -46,14 +46,16 @@ export interface SignUpEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a user signs in with GitHub, Email and Password or SSO.
|
||||
* Triggered when a user signs in with an OAuth provider, Email and Password, or SSO.
|
||||
*
|
||||
* Some unintuitive behavior:
|
||||
* - If signing up with GitHub the SignInEvent gets triggered first before the SignUpEvent.
|
||||
* - Captured server-side; the distinct_id often resolves to the anonymous cookie because
|
||||
* the event races identify, so don't use it as a funnel join key across the auth boundary.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /sign-in-mfa
|
||||
* @page /sign-in, /sign-in-mfa
|
||||
*/
|
||||
export interface SignInEvent {
|
||||
action: 'sign_in'
|
||||
|
||||
Reference in new issue
Block a user