Skip to content

Commit

Permalink
Supply authMechanism in email update during mobile user creation flow (
Browse files Browse the repository at this point in the history
…#2409)

* supply authMechanism in email update during mobile user creation flow

* toss logs

* fix web types
  • Loading branch information
Robinnnnn authored Apr 6, 2024
1 parent 551d7d8 commit 607dd36
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 66 deletions.
1 change: 1 addition & 0 deletions apps/mobile/src/screens/Login/Onboarding2FAScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function InnerOnboardingEmailScreen() {
authMechanismType: 'privy',
token,
},
email,
});
}
if (authMethod === 'Wallet' || authMethod === 'Farcaster') {
Expand Down
40 changes: 31 additions & 9 deletions apps/mobile/src/screens/Onboarding/OnboardingUsernameScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import { OnboardingUsernameScreenQuery } from '~/generated/OnboardingUsernameScr
import { LoginStackNavigatorParamList, LoginStackNavigatorProp } from '~/navigation/types';
import { contexts } from '~/shared/analytics/constants';
import { useReportError } from '~/shared/contexts/ErrorReportingContext';
import useCreateUser from '~/shared/hooks/useCreateUser';
import useCreateUser, { getAuthMechanismFromAuthPayload } from '~/shared/hooks/useCreateUser';
import useDebounce from '~/shared/hooks/useDebounce';
import useUpdateEmail from '~/shared/hooks/useUpdateEmail';
import useUpdateUser from '~/shared/hooks/useUpdateUser';
import { useIsUsernameAvailableFetcher } from '~/shared/hooks/useUserInfoFormIsUsernameAvailableQuery';
import colors from '~/shared/theme/colors';
Expand Down Expand Up @@ -65,6 +66,7 @@ function InnerOnboardingUsernameScreen() {
const { colorScheme } = useColorScheme();
const createUser = useCreateUser();
const updateUser = useUpdateUser();
const updateEmail = useUpdateEmail();
const isUsernameAvailableFetcher = useIsUsernameAvailableFetcher();
const reportError = useReportError();
const { isSyncing, syncTokens } = useSyncTokensActions();
Expand Down Expand Up @@ -121,10 +123,27 @@ function InnerOnboardingUsernameScreen() {
authPayloadVariables: authMechanism,
username,
bio,
email,
});

if (response.createUser?.__typename === 'CreateUserPayload') {
if (!email) {
throw new Error('No email found for user creation on mobile');
}
if (!authMechanism) {
throw new Error('No authmechanism found for user creation on mobile');
}

const result = await updateEmail({
email,
authMechanism: getAuthMechanismFromAuthPayload(authMechanism),
});

if (result.updateEmail?.__typename !== 'UpdateEmailPayload') {
throw new Error(
`Update email after create user failed on mobile due to ${result.updateEmail?.__typename}`
);
}

// If the user is signing up with email, redirect to the bio screen
if (authMethod === 'Privy') {
navigation.navigate('OnboardingProfileBio');
Expand Down Expand Up @@ -164,18 +183,21 @@ function InnerOnboardingUsernameScreen() {
setIsCreatingUser(false);
}
}, [
user?.username,
user?.dbid,
user?.bio,
authMechanism,
authMethod,
username,
bio,
email,
createUser,
setEnsProfileImage,
isSyncing,
updateUser,
navigation,
updateEmail,
authMethod,
isSyncing,
syncTokens,
username,
email,
updateUser,
user,
setEnsProfileImage,
]);

const [isCheckingUsername, setIsCheckingUsername] = useState(false);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Email/EmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function EmailForm({ setIsEditMode, queryRef, onClose }: Props) {
};

try {
const response = await updateEmail(email, updater);
const response = await updateEmail({ email, updater });

if (response.updateEmail?.__typename !== 'UpdateEmailPayload') {
// ERROR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function OnboardingAddUsernamePage() {

if (authPayloadQuery.authMechanismType === 'eoa' && authPayloadQuery.email) {
// Attach the email to the user
updateEmail(authPayloadQuery.email);
updateEmail({ email: authPayloadQuery.email });
}

if (!isLocked) {
Expand Down
113 changes: 60 additions & 53 deletions packages/shared/src/hooks/useCreateUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,58 +76,8 @@ export default function useCreateUser() {
bio: string;
email?: string;
}) => {
let authMechanism: useCreateUserMutation['variables']['authMechanism'];

if (isEoaPayload(authPayloadVariables)) {
const { chain, address, nonce, message, signature } = authPayloadVariables;

authMechanism = {
eoa: {
chainPubKey: {
chain,
pubKey: address,
},
nonce,
message,
signature,
},
};
} else if (isEmailPayload(authPayloadVariables)) {
const { token } = authPayloadVariables;
authMechanism = {
privy: {
token: token!,
},
};
} else if (authPayloadVariables.authMechanismType === 'gnosisSafe') {
const { address, nonce, message } = authPayloadVariables;
authMechanism = {
gnosisSafe: {
address,
nonce,
message,
},
};
} else if (authPayloadVariables.authMechanismType === 'neynar') {
const { address, nonce, message, signature, primaryAddress } = authPayloadVariables;
authMechanism = {
neynar: {
nonce,
message,
signature,
custodyPubKey: {
pubKey: address,
chain: 'Ethereum',
},
},
};
if (primaryAddress) {
authMechanism.neynar!.primaryPubKey = {
pubKey: primaryAddress,
chain: 'Ethereum',
};
}
}
const authMechanism: useCreateUserMutation['variables']['authMechanism'] =
getAuthMechanismFromAuthPayload(authPayloadVariables);

const createUserInput: CreateUserInput = {
username,
Expand All @@ -140,7 +90,6 @@ export default function useCreateUser() {

const response = await createUser({
variables: {
// @ts-expect-error: ts doesn't think authMechanism is defined when it clearly is
authMechanism,
input: createUserInput,
},
Expand Down Expand Up @@ -199,6 +148,64 @@ export default function useCreateUser() {
);
}

export function getAuthMechanismFromAuthPayload(authPayloadVariables: AuthPayloadVariables) {
let authMechanism: useCreateUserMutation['variables']['authMechanism'];

if (isEoaPayload(authPayloadVariables)) {
const { chain, address, nonce, message, signature } = authPayloadVariables;

authMechanism = {
eoa: {
chainPubKey: {
chain,
pubKey: address,
},
nonce,
message,
signature,
},
};
} else if (isEmailPayload(authPayloadVariables)) {
const { token } = authPayloadVariables;
authMechanism = {
privy: {
token: token!,
},
};
} else if (authPayloadVariables.authMechanismType === 'gnosisSafe') {
const { address, nonce, message } = authPayloadVariables;
authMechanism = {
gnosisSafe: {
address,
nonce,
message,
},
};
} else if (authPayloadVariables.authMechanismType === 'neynar') {
const { address, nonce, message, signature, primaryAddress } = authPayloadVariables;
authMechanism = {
neynar: {
nonce,
message,
signature,
custodyPubKey: {
pubKey: address,
chain: 'Ethereum',
},
},
};
if (primaryAddress) {
authMechanism.neynar!.primaryPubKey = {
pubKey: primaryAddress,
chain: 'Ethereum',
};
}
}

// @ts-expect-error: ts doesn't think authMechanism is defined when it clearly is
return authMechanism;
}

// TODO: use `useCreateUser` instead after web is migrated from magic link -> privy
export function useDeprecatedCreateUser() {
const environment = useRelayEnvironment();
Expand Down
11 changes: 9 additions & 2 deletions packages/shared/src/hooks/useUpdateEmail.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { useCallback } from 'react';
import { graphql, SelectorStoreUpdater } from 'relay-runtime';

import { useUpdateEmailMutation } from '~/generated/useUpdateEmailMutation.graphql';
import { AuthMechanism, useUpdateEmailMutation } from '~/generated/useUpdateEmailMutation.graphql';

import { usePromisifiedMutation } from '../relay/usePromisifiedMutation';

type UpdateEmailProps = {
email: string;
authMechanism?: AuthMechanism;
updater?: SelectorStoreUpdater<useUpdateEmailMutation['response']>;
};

export default function useUpdateEmail() {
const [updateEmail] = usePromisifiedMutation<useUpdateEmailMutation>(graphql`
mutation useUpdateEmailMutation($input: UpdateEmailInput!) @raw_response_type {
Expand All @@ -27,11 +33,12 @@ export default function useUpdateEmail() {
`);

return useCallback(
(email: string, updater?: SelectorStoreUpdater<useUpdateEmailMutation['response']>) => {
({ email, authMechanism, updater }: UpdateEmailProps) => {
return updateEmail({
variables: {
input: {
email,
authMechanism,
},
},
updater,
Expand Down
5 changes: 5 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,11 @@ union UpdateCollectionTokensPayloadOrError =

input UpdateEmailInput {
email: Email!
"""
authMechanism is an optional parameter that can verify a user's email address in lieu of sending
a verification email to the user. If not provided, a verification email will be sent.
"""
authMechanism: AuthMechanism
}

input UpdateEmailNotificationSettingsInput {
Expand Down

0 comments on commit 607dd36

Please sign in to comment.