diff --git a/packages/common/src/api/tan-query/queryKeys.ts b/packages/common/src/api/tan-query/queryKeys.ts index d2387368408..a41a199c6a3 100644 --- a/packages/common/src/api/tan-query/queryKeys.ts +++ b/packages/common/src/api/tan-query/queryKeys.ts @@ -45,7 +45,6 @@ export const QUERY_KEYS = { sales: 'sales', salesCount: 'salesCount', mutualFollowers: 'mutualFollowers', - emailInUse: 'emailInUse', handleInUse: 'handleInUse', handleReservedStatus: 'handleReservedStatus', search: 'search', diff --git a/packages/common/src/api/tan-query/users/useEmailInUse.ts b/packages/common/src/api/tan-query/users/useEmailInUse.ts deleted file mode 100644 index b0a1d660434..00000000000 --- a/packages/common/src/api/tan-query/users/useEmailInUse.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { useQuery } from '@tanstack/react-query' - -import { useQueryContext } from '~/api/tan-query/utils' -import { QueryContextType } from '~/api/tan-query/utils/QueryContext' - -import { QUERY_KEYS } from '../queryKeys' -import { QueryKey, SelectableQueryOptions } from '../types' - -export const fetchEmailInUse = async ( - email: string | null | undefined, - { identityService }: QueryContextType -) => { - if (!email) return { exists: false, isGuest: false } - return await identityService.checkIfEmailRegistered(email) -} - -export const getEmailInUseQueryKey = (email: string | null | undefined) => { - return [QUERY_KEYS.emailInUse, email] as unknown as QueryKey<{ - exists: boolean - isGuest: boolean - }> -} - -/** - * Hook to check if an email is already registered - */ -export const useEmailInUse = ( - email: string | null | undefined, - options?: SelectableQueryOptions< - { exists: boolean; isGuest: boolean }, - TResult - > -) => { - const context = useQueryContext() - - return useQuery({ - queryKey: getEmailInUseQueryKey(email), - queryFn: () => fetchEmailInUse(email, context), - ...options, - enabled: options?.enabled !== false && !!email - }) -} diff --git a/packages/common/src/hooks/purchaseContent/usePurchaseContentFormConfiguration.ts b/packages/common/src/hooks/purchaseContent/usePurchaseContentFormConfiguration.ts index a65a173041f..e0b08c87009 100644 --- a/packages/common/src/hooks/purchaseContent/usePurchaseContentFormConfiguration.ts +++ b/packages/common/src/hooks/purchaseContent/usePurchaseContentFormConfiguration.ts @@ -1,12 +1,10 @@ import { useCallback, useEffect, useMemo } from 'react' import { USDC, UsdcWei } from '@audius/fixed-decimal' -import { useQueryClient } from '@tanstack/react-query' import { useDispatch, useSelector } from 'react-redux' import { z } from 'zod' import { useCurrentAccount, useCurrentAccountUser, useUSDCBalance } from '~/api' -import { useQueryContext } from '~/api/tan-query/utils/QueryContext' import { UserCollectionMetadata } from '~/models' import { PurchaseMethod, PurchaseVendor } from '~/models/PurchaseContent' import { UserTrackMetadata } from '~/models/Track' @@ -53,9 +51,6 @@ export const usePurchaseContentFormConfiguration = ({ presetValues: PayExtraAmountPresetValues purchaseVendor?: PurchaseVendor }) => { - const queryClient = useQueryClient() - const queryContext = useQueryContext() - const dispatch = useDispatch() const isAlbum = isContentCollection(metadata) const isTrack = isContentTrack(metadata) @@ -99,14 +94,8 @@ export const usePurchaseContentFormConfiguration = ({ : undefined const validationSchema = useMemo( - () => - createPurchaseContentSchema( - queryContext, - queryClient, - page, - guestEmail ?? undefined - ), - [queryContext, queryClient, guestEmail, page] + () => createPurchaseContentSchema(page), + [page] ) type PurchaseContentValues = z.input diff --git a/packages/common/src/hooks/purchaseContent/validation.ts b/packages/common/src/hooks/purchaseContent/validation.ts index ca897f7bf00..38f3247987d 100644 --- a/packages/common/src/hooks/purchaseContent/validation.ts +++ b/packages/common/src/hooks/purchaseContent/validation.ts @@ -1,9 +1,5 @@ -import { QueryClient } from '@tanstack/react-query' import { z } from 'zod' -import { QUERY_KEYS } from '~/api' -import { fetchEmailInUse } from '~/api/tan-query/users/useEmailInUse' -import { QueryContextType } from '~/api/tan-query/utils/QueryContext' import { PurchaseMethod, PurchaseVendor } from '~/models/PurchaseContent' import { PurchaseContentPage } from '~/store' @@ -21,20 +17,10 @@ import { import { PayExtraPreset } from './types' export const messages = { - amountInvalid: 'Please specify an amount between $1 and $100', - emailRequired: 'Please enter an email.', - invalidEmail: 'Please enter a valid email.', - emailInUse: 'Email already taken.', - somethingWentWrong: 'Something went wrong. Try again later.', - guestAccountExists: 'Guest account exists.' + amountInvalid: 'Please specify an amount between $1 and $100' } -export const createPurchaseContentSchema = ( - queryContext: QueryContextType, - queryClient: QueryClient, - page: PurchaseContentPage, - emailFromLocalStorage?: string -) => { +export const createPurchaseContentSchema = (page: PurchaseContentPage) => { return z .object({ [CUSTOM_AMOUNT]: z @@ -77,39 +63,4 @@ export const createPurchaseContentSchema = ( path: [GUEST_EMAIL] // Specify the path to the error } ) - .superRefine(async ({ guestEmail }, ctx) => { - if ( - page !== PurchaseContentPage.GUEST_CHECKOUT || - !guestEmail || - guestEmail === emailFromLocalStorage - ) { - // only validate email if on guest checkout page and email is different - return - } - - const { exists: isEmailInUse, isGuest } = await queryClient.fetchQuery({ - queryKey: [QUERY_KEYS.emailInUse, guestEmail], - queryFn: async () => await fetchEmailInUse(guestEmail, queryContext) - }) - - if (isEmailInUse === undefined) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: messages.somethingWentWrong, - path: [GUEST_EMAIL] - }) - } else if (isGuest) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: messages.guestAccountExists, - path: [GUEST_EMAIL] - }) - } else if (isEmailInUse) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: messages.emailInUse, - path: [GUEST_EMAIL] - }) - } - }) } diff --git a/packages/common/src/hooks/useChangeEmailFormConfiguration.ts b/packages/common/src/hooks/useChangeEmailFormConfiguration.ts index 4167a55b851..69f25fb79a8 100644 --- a/packages/common/src/hooks/useChangeEmailFormConfiguration.ts +++ b/packages/common/src/hooks/useChangeEmailFormConfiguration.ts @@ -1,6 +1,5 @@ import { useState, useCallback, useMemo } from 'react' -import { useQueryClient } from '@tanstack/react-query' import { FormikHelpers } from 'formik' import { z } from 'zod' import { toFormikValidationSchema } from 'zod-formik-adapter' @@ -55,7 +54,6 @@ export const useChangeEmailFormConfiguration = (onComplete: () => void) => { const [page, setPage] = useState(ChangeEmailPage.ConfirmPassword) const queryContext = useQueryContext() const { authService } = queryContext - const queryClient = useQueryClient() // Move schema initializations inside the hook to prevent initialization timing issues const confirmPasswordFormikSchema = toFormikValidationSchema( @@ -67,10 +65,7 @@ export const useChangeEmailFormConfiguration = (onComplete: () => void) => { ) const verifyEmailFormikSchema = toFormikValidationSchema(confirmEmailSchema) - const EmailSchema = useMemo( - () => toFormikValidationSchema(emailSchema(queryContext, queryClient)), - [queryContext, queryClient] - ) + const EmailSchema = useMemo(() => toFormikValidationSchema(emailSchema()), []) const validationSchema = page === ChangeEmailPage.ConfirmPassword diff --git a/packages/common/src/schemas/sign-on/emailSchema.ts b/packages/common/src/schemas/sign-on/emailSchema.ts index 65673b3964c..bfc464589ef 100644 --- a/packages/common/src/schemas/sign-on/emailSchema.ts +++ b/packages/common/src/schemas/sign-on/emailSchema.ts @@ -1,49 +1,15 @@ -import { QueryClient } from '@tanstack/react-query' import { z } from 'zod' -import { QUERY_KEYS, QueryContextType } from '~/api' -import { fetchEmailInUse } from '~/api/tan-query/users/useEmailInUse' import { EMAIL_REGEX } from '~/utils/email' export const emailSchemaMessages = { emailRequired: 'Please enter an email.', - invalidEmail: 'Please enter a valid email.', - emailInUse: 'Email already taken.', - somethingWentWrong: 'Something went wrong. Try again later.', - guestAccountExists: 'Guest account exists.' + invalidEmail: 'Please enter a valid email.' } -export const emailSchema = ( - queryContext: QueryContextType, - queryClient: QueryClient -) => +export const emailSchema = () => z.object({ email: z .string({ required_error: emailSchemaMessages.emailRequired }) .regex(EMAIL_REGEX, { message: emailSchemaMessages.invalidEmail }) - .superRefine(async (email, ctx) => { - const { exists: isEmailInUse, isGuest } = await queryClient.fetchQuery({ - queryKey: [QUERY_KEYS.emailInUse, email], - queryFn: async () => await fetchEmailInUse(email, queryContext) - }) - if (isEmailInUse === undefined) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: emailSchemaMessages.somethingWentWrong - }) - } else if (isGuest) { - // complete guest accounts only - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: emailSchemaMessages.guestAccountExists - }) - return true - } else if (isEmailInUse) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: emailSchemaMessages.emailInUse - }) - } - return z.NEVER // The return value is not used, but we need to return something to satisfy the typing - }) }) diff --git a/packages/common/src/schemas/sign-on/signInSchema.ts b/packages/common/src/schemas/sign-on/signInSchema.ts index a53cf29e0bb..76fa02d49c8 100644 --- a/packages/common/src/schemas/sign-on/signInSchema.ts +++ b/packages/common/src/schemas/sign-on/signInSchema.ts @@ -1,42 +1,17 @@ -import { QueryClient } from '@tanstack/react-query' import { z } from 'zod' -import { QUERY_KEYS } from '~/api' -import { fetchEmailInUse } from '~/api/tan-query/users/useEmailInUse' -import { QueryContextType } from '~/api/tan-query/utils' - export const messages = { emailRequired: 'Please enter an email.', email: 'Please enter a valid email.', passwordRequired: 'Please enter a password.', - invalidCredentials: 'Invalid credentials.', - guestAccountExists: 'Guest account exists.' + invalidCredentials: 'Invalid credentials.' } -export const signInSchema = ( - queryContext: QueryContextType, - queryClient: QueryClient -) => +export const signInSchema = () => z.object({ email: z - .string({ - required_error: messages.emailRequired - }) - .email(messages.email) - .superRefine(async (email, ctx) => { - const { isGuest } = await queryClient.fetchQuery({ - queryKey: [QUERY_KEYS.emailInUse, email], - queryFn: async () => await fetchEmailInUse(email, queryContext) - }) - if (isGuest) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: messages.guestAccountExists - }) - return true - } - return z.NEVER // The return value is not used, but we need to return something to satisfy the typing - }), + .string({ required_error: messages.emailRequired }) + .email(messages.email), password: z.string({ required_error: messages.passwordRequired }) diff --git a/packages/common/src/services/auth/identity.ts b/packages/common/src/services/auth/identity.ts index d037b144c79..e5c4b5ed163 100644 --- a/packages/common/src/services/auth/identity.ts +++ b/packages/common/src/services/auth/identity.ts @@ -201,19 +201,6 @@ export class IdentityService { }) } - /** - * Check if an email address has been previously registered. - */ - async checkIfEmailRegistered(email: string) { - return await this._makeRequest<{ exists: boolean; isGuest: boolean }>({ - url: '/users/check', - method: 'get', - params: { - email - } - }) - } - /** * Get the user's email used for notifications and display. */ diff --git a/packages/identity-service/src/routes/user.js b/packages/identity-service/src/routes/user.js index c9f25a6dcc9..52fbd3d1cac 100644 --- a/packages/identity-service/src/routes/user.js +++ b/packages/identity-service/src/routes/user.js @@ -123,33 +123,6 @@ module.exports = function (app) { }) ) - /** - * Check if a email address is taken. email is passed in via query param - */ - app.get( - '/users/check', - handleResponse(async (req, res, next) => { - let email = req.query.email - if (email) { - email = email.toLowerCase() - const existingUser = await models.User.findOne({ - where: { - email - } - }) - if (existingUser) { - return successResponse({ - exists: true, - isGuest: existingUser.isGuest - }) - } else { - return successResponse({ exists: false }) - } - } else - return errorResponseBadRequest('Please pass in a valid email address') - }) - ) - /** * Update User Timezone / IP */ diff --git a/packages/identity-service/test/expressAppTest.js b/packages/identity-service/test/expressAppTest.js index 9f11460cf95..b057e9c8a1b 100644 --- a/packages/identity-service/test/expressAppTest.js +++ b/packages/identity-service/test/expressAppTest.js @@ -49,4 +49,8 @@ describe('test /health_check and /balance_check', function () { await request(app).post('/wormhole_relay').send({}).expect(404) await request(app).get('/sol_balance_check').expect(404) }) + + it('does not expose email registration checks', async function () { + await request(app).get('/users/check?email=test@example.com').expect(404) + }) }) diff --git a/packages/libs/src/api/Account.ts b/packages/libs/src/api/Account.ts index fd1788bf65e..5bb58c8af55 100644 --- a/packages/libs/src/api/Account.ts +++ b/packages/libs/src/api/Account.ts @@ -31,7 +31,6 @@ export class Account extends Base { this.confirmCredentials = this.confirmCredentials.bind(this) this.changeCredentials = this.changeCredentials.bind(this) this.resetPassword = this.resetPassword.bind(this) - this.checkIfEmailRegistered = this.checkIfEmailRegistered.bind(this) this.associateTwitterUser = this.associateTwitterUser.bind(this) this.associateInstagramUser = this.associateInstagramUser.bind(this) this.associateTikTokUser = this.associateTikTokUser.bind(this) @@ -281,14 +280,6 @@ export class Account extends Base { return await this.hedgehog.confirmCredentials(args) } - /** - * Check if an email address has been previously registered. - */ - async checkIfEmailRegistered(email: string) { - this.REQUIRES(Services.IDENTITY_SERVICE) - return await this.identityService.checkIfEmailRegistered(email) - } - /** * Associates a user with a twitter uuid. * @param uuid from the Twitter API diff --git a/packages/libs/src/services/identity/IdentityService.ts b/packages/libs/src/services/identity/IdentityService.ts index a5bfd34e819..b61dfa6c74a 100644 --- a/packages/libs/src/services/identity/IdentityService.ts +++ b/packages/libs/src/services/identity/IdentityService.ts @@ -170,19 +170,6 @@ export class IdentityService { }) } - /** - * Check if an email address has been previously registered. - */ - async checkIfEmailRegistered(email: string) { - return await this._makeRequest<{ exists: boolean }>({ - url: '/users/check', - method: 'get', - params: { - email - } - }) - } - /** * Check if the lookupKey exists. * WARNING: might not be the auth credentials for the current user! diff --git a/packages/mobile/src/screens/sign-on-screen/components/GuestEmailHint.tsx b/packages/mobile/src/screens/sign-on-screen/components/GuestEmailHint.tsx deleted file mode 100644 index 9dc510d2936..00000000000 --- a/packages/mobile/src/screens/sign-on-screen/components/GuestEmailHint.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useCallback } from 'react' - -import { confirmEmailMessages } from '@audius/common/messages' -import { emailSchemaMessages } from '@audius/common/schemas' -import { TEMPORARY_PASSWORD } from '@audius/common/utils' -import { - signIn, - setValueField, - setField -} from 'common/store/pages/signon/actions' -import { useField, useFormikContext } from 'formik' -import { useDispatch } from 'react-redux' -import { usePrevious } from 'react-use' - -import { Hint, IconError, TextLink } from '@audius/harmony-native' - -export const GuestEmailHint = () => { - const [{ value: email }, { error }] = useField('email') - const { isValidating } = useFormikContext() - const dispatch = useDispatch() - const lastShownError = usePrevious(error) - - const handleClickConfirmEmail = useCallback(() => { - dispatch(setField('isGuest', true)) - dispatch(setValueField('email', email)) - dispatch(setValueField('password', TEMPORARY_PASSWORD)) - dispatch(signIn(email, TEMPORARY_PASSWORD)) - }, [dispatch, email]) - - const showGuestError = - error === emailSchemaMessages.guestAccountExists || - (isValidating && lastShownError === emailSchemaMessages.guestAccountExists) - - if (!showGuestError) return null - - return ( - - {error}{' '} - - {confirmEmailMessages.finishSigningUp} - - - ) -} diff --git a/packages/mobile/src/screens/sign-on-screen/components/NewEmailField.tsx b/packages/mobile/src/screens/sign-on-screen/components/NewEmailField.tsx index feaca0c18ba..fefec1fa838 100644 --- a/packages/mobile/src/screens/sign-on-screen/components/NewEmailField.tsx +++ b/packages/mobile/src/screens/sign-on-screen/components/NewEmailField.tsx @@ -1,66 +1,5 @@ -import { useEffect } from 'react' - -import { createEmailPageMessages } from '@audius/common/messages' -import { emailSchemaMessages } from '@audius/common/schemas' -import { setField } from 'common/store/pages/signon/actions' -import { useField, useFormikContext } from 'formik' -import { useDispatch } from 'react-redux' -import { usePrevious } from 'react-use' - -import { Hint, IconError, TextLink } from '@audius/harmony-native' - -import type { SignOnScreenType } from '../screens/types' - import { EmailField, type EmailFieldProps } from './EmailField' -import { GuestEmailHint } from './GuestEmailHint' - -type NewEmailFieldProps = EmailFieldProps & { - onChangeScreen: (screen: SignOnScreenType) => void -} - -export const NewEmailField = (props: NewEmailFieldProps) => { - const dispatch = useDispatch() - const { onChangeScreen, ...other } = props - const { name } = other - - const [, { error }] = useField(name) - const { isValidating } = useFormikContext() - const emailInUse = error === emailSchemaMessages.emailInUse - const isGuest = error === emailSchemaMessages.guestAccountExists - const hasError = emailInUse || isGuest - - // Track which specific error was shown last - const lastShownError = usePrevious(error) - - useEffect(() => { - dispatch(setField('isGuest', isGuest)) - }, [dispatch, isGuest]) - - const showGuestError = - isGuest || - (isValidating && lastShownError === emailSchemaMessages.guestAccountExists) - - const showEmailInUseError = - emailInUse || - (isValidating && lastShownError === emailSchemaMessages.emailInUse) - return ( - <> - - {showGuestError ? ( - - ) : showEmailInUseError ? ( - - {error}{' '} - onChangeScreen('sign-in')}> - {createEmailPageMessages.signIn} - - - ) : null} - - ) +export const NewEmailField = (props: EmailFieldProps) => { + return } diff --git a/packages/mobile/src/screens/sign-on-screen/screens/CreateEmailScreen.tsx b/packages/mobile/src/screens/sign-on-screen/screens/CreateEmailScreen.tsx index 1717fa65d90..621be880403 100644 --- a/packages/mobile/src/screens/sign-on-screen/screens/CreateEmailScreen.tsx +++ b/packages/mobile/src/screens/sign-on-screen/screens/CreateEmailScreen.tsx @@ -1,9 +1,7 @@ import { useCallback, useMemo } from 'react' -import { useQueryContext } from '@audius/common/api' import { createEmailPageMessages } from '@audius/common/messages' import { emailSchema } from '@audius/common/schemas' -import { useQueryClient } from '@tanstack/react-query' import { setValueField, startSignUp } from 'common/store/pages/signon/actions' import { getEmailField } from 'common/store/pages/signon/selectors' import { Formik } from 'formik' @@ -36,15 +34,13 @@ export const CreateEmailScreen = (props: SignOnScreenProps) => { const dispatch = useDispatch() const navigation = useNavigation() const existingEmailValue = useSelector(getEmailField) - const queryContext = useQueryContext() - const queryClient = useQueryClient() const initialValues = { email: existingEmailValue.value ?? '' } const EmailSchema = useMemo(() => { - return toFormikValidationSchema(emailSchema(queryContext, queryClient)) - }, [queryContext, queryClient]) + return toFormikValidationSchema(emailSchema()) + }, []) useTrackScreen('CreateEmail') @@ -76,7 +72,6 @@ export const CreateEmailScreen = (props: SignOnScreenProps) => { diff --git a/packages/mobile/src/screens/sign-on-screen/screens/SignInScreen.tsx b/packages/mobile/src/screens/sign-on-screen/screens/SignInScreen.tsx index 6a0f9bf9f4f..59ffb90d171 100644 --- a/packages/mobile/src/screens/sign-on-screen/screens/SignInScreen.tsx +++ b/packages/mobile/src/screens/sign-on-screen/screens/SignInScreen.tsx @@ -1,19 +1,13 @@ import { useCallback, useEffect, useMemo } from 'react' -import { useQueryContext } from '@audius/common/api' import { signInPageMessages } from '@audius/common/messages' -import { - signInSchema, - signInErrorMessages, - emailSchemaMessages -} from '@audius/common/schemas' +import { signInSchema, signInErrorMessages } from '@audius/common/schemas' import { getEmailField, getPasswordField, getRequiresOtp, getStatus } from '@audius/web/src/common/store/pages/signon/selectors' -import { useQueryClient } from '@tanstack/react-query' import { setValueField, signIn } from 'common/store/pages/signon/actions' import { Formik, useField } from 'formik' import { useDispatch, useSelector } from 'react-redux' @@ -25,7 +19,6 @@ import { useDrawer } from 'app/hooks/useDrawer' import { useNavigation } from 'app/hooks/useNavigation' import { EmailField } from '../components/EmailField' -import { GuestEmailHint } from '../components/GuestEmailHint' import { Heading } from '../components/layout' import type { SignOnScreenParamList } from '../types' import { useTrackScreen } from '../utils/useTrackScreen' @@ -43,13 +36,11 @@ export const SignInScreen = () => { const { onOpen } = useDrawer('ForgotPassword') const requiresOtp = useSelector(getRequiresOtp) const navigation = useNavigation() - const queryClient = useQueryClient() useTrackScreen('SignIn') - const queryContext = useQueryContext() const SignInSchema = useMemo( - () => toFormikValidationSchema(signInSchema(queryContext, queryClient)), - [queryContext, queryClient] + () => toFormikValidationSchema(signInSchema()), + [] ) useEffect(() => { @@ -83,21 +74,13 @@ export const SignInScreen = () => { validateOnChange={false} onSubmit={handleSubmit} > - {({ handleSubmit, errors }) => { - const hideEmailError = - errors.email === emailSchemaMessages.guestAccountExists + {({ handleSubmit }) => { return ( <> - + -