From 847a68f3fb49247da7182dbbdad6b41391c7c01c Mon Sep 17 00:00:00 2001 From: storywithoutend <storywithoutend@gmail.com> Date: Thu, 24 Oct 2024 23:14:54 +0800 Subject: [PATCH 1/2] split up verification oauth process --- e2e/specs/stateless/verifications.spec.ts | 39 ++++------- .../useDentityProfile.ts} | 62 +++++++---------- .../useDentityToken/useDentityToken.ts | 67 +++++++++++++++++++ .../utils/getAPIEndpointForVerifier.ts | 9 --- .../useVerificationOAuthHandler.ts | 28 ++++---- .../createVerificationTransactionFlow.ts | 4 +- .../utils/dentityHandler.ts | 4 +- 7 files changed, 123 insertions(+), 90 deletions(-) rename src/hooks/verification/{useVerificationOAuth/useVerificationOAuth.ts => useDentityProfile/useDentityProfile.ts} (66%) create mode 100644 src/hooks/verification/useDentityToken/useDentityToken.ts delete mode 100644 src/hooks/verification/useVerificationOAuth/utils/getAPIEndpointForVerifier.ts diff --git a/e2e/specs/stateless/verifications.spec.ts b/e2e/specs/stateless/verifications.spec.ts index 3f23e22e4..0db792646 100644 --- a/e2e/specs/stateless/verifications.spec.ts +++ b/e2e/specs/stateless/verifications.spec.ts @@ -108,8 +108,6 @@ test.describe('Verified records', () => { await page.goto(`/${name}`) // await login.connect() - await page.pause() - await expect(page.getByTestId('profile-section-verifications')).toBeVisible() await profilePage.isRecordVerified('text', 'com.twitter') @@ -180,8 +178,6 @@ test.describe('Verified records', () => { await page.goto(`/${name}`) - await page.pause() - await expect(page.getByTestId('profile-section-verifications')).toBeVisible() await profilePage.isRecordVerified('text', 'com.twitter', false) @@ -345,8 +341,6 @@ test.describe('Verify profile', () => { await profilePage.goto(name) await login.connect() - await page.pause() - await expect(page.getByTestId('profile-section-verifications')).toBeVisible() await profilePage.isRecordVerified('text', 'com.twitter') @@ -374,7 +368,6 @@ test.describe('Verify profile', () => { await profilePage.isRecordVerified('text', 'com.discord', false) await profilePage.isRecordVerified('verification', 'dentity', false) await profilePage.isPersonhoodVerified(false) - await page.pause() }) }) @@ -488,8 +481,6 @@ test.describe('OAuth flow', () => { await page.goto(`/?iss=${DENTITY_ISS}&code=dummyCode`) await login.connect() - await page.pause() - await expect(page.getByText('Verification failed')).toBeVisible() await expect( page.getByText( @@ -497,13 +488,10 @@ test.describe('OAuth flow', () => { ), ).toBeVisible() - await page.pause() await login.switchTo('user2') // Page should redirect to the profile page await expect(page).toHaveURL(`/${name}`) - - await page.pause() }) test('Should show an error message if user is not logged in', async ({ @@ -531,8 +519,6 @@ test.describe('OAuth flow', () => { await page.goto(`/?iss=${DENTITY_ISS}&code=dummyCode`) - await page.pause() - await expect(page.getByText('Verification failed')).toBeVisible() await expect( page.getByText('You must be connected as 0x709...c79C8 to set the verification record.'), @@ -540,13 +526,21 @@ test.describe('OAuth flow', () => { await page.locator('.modal').getByRole('button', { name: 'Done' }).click() - await page.pause() + await page.route(`${VERIFICATION_OAUTH_BASE_URL}/dentity/token`, async (route) => { + await route.fulfill({ + status: 401, + contentType: 'application/json', + body: JSON.stringify({ + error_msg: 'Unauthorized', + }), + }) + }) + await login.connect('user2') + await page.reload() // Page should redirect to the profile page await expect(page).toHaveURL(`/${name}`) - - await page.pause() }) test('Should redirect to profile page without showing set verification record if it already set', async ({ @@ -594,15 +588,9 @@ test.describe('OAuth flow', () => { await expect(page).toHaveURL(`/${name}`) await expect(transactionModal.transactionModal).not.toBeVisible() - - await page.pause() }) - test('Should show general error message if other problems occur', async ({ - page, - login, - makeName, - }) => { + test('Should show general error message if other problems occur', async ({ page, makeName }) => { const name = await makeName({ label: 'dentity', type: 'legacy', @@ -622,9 +610,6 @@ test.describe('OAuth flow', () => { }) await page.goto(`/?iss=${DENTITY_ISS}&code=dummyCode`) - await login.connect('user') - - await page.pause() await expect(page.getByText('Verification failed')).toBeVisible() await expect( diff --git a/src/hooks/verification/useVerificationOAuth/useVerificationOAuth.ts b/src/hooks/verification/useDentityProfile/useDentityProfile.ts similarity index 66% rename from src/hooks/verification/useVerificationOAuth/useVerificationOAuth.ts rename to src/hooks/verification/useDentityProfile/useDentityProfile.ts index b77123ccd..0bb5c9846 100644 --- a/src/hooks/verification/useVerificationOAuth/useVerificationOAuth.ts +++ b/src/hooks/verification/useDentityProfile/useDentityProfile.ts @@ -9,27 +9,25 @@ import { VerificationProtocol } from '@app/transaction-flow/input/VerifyProfile/ import { ConfigWithEns, CreateQueryKey, QueryConfig } from '@app/types' import { prepareQueryOptions } from '@app/utils/prepareQueryOptions' -import { getAPIEndpointForVerifier } from './utils/getAPIEndpointForVerifier' +import { type DentityFederatedToken } from '../useDentityToken/useDentityToken' type UseVerificationOAuthParameters = { - verifier?: VerificationProtocol | null - code?: string | null - onSuccess?: (resp: UseVerificationOAuthReturnType) => void + token?: DentityFederatedToken } -export type UseVerificationOAuthReturnType = { +export type UseDentityProfileReturnType = { verifier: VerificationProtocol - name: string - owner: Hash + name?: string + owner?: Hash | null manager?: Hash primaryName?: string - address: Hash - resolverAddress: Hash - verifiedPresentationUri: string + address?: Hash + resolverAddress?: Hash + verifiedPresentationUri?: string verificationRecord?: string } -type UseVerificationOAuthConfig = QueryConfig<UseVerificationOAuthReturnType, Error> +type UseVerificationOAuthConfig = QueryConfig<UseDentityProfileReturnType, Error> type QueryKey<TParams extends UseVerificationOAuthParameters> = CreateQueryKey< TParams, @@ -37,58 +35,46 @@ type QueryKey<TParams extends UseVerificationOAuthParameters> = CreateQueryKey< 'standard' > -export const getVerificationOAuth = +export const getDentityProfile = (config: ConfigWithEns) => async <TParams extends UseVerificationOAuthParameters>({ - queryKey: [{ verifier, code }, chainId], - }: QueryFunctionContext<QueryKey<TParams>>): Promise<UseVerificationOAuthReturnType> => { - // Get federated token from oidc worker - const url = getAPIEndpointForVerifier(verifier) - const response = await fetch(url, { - method: 'POST', - body: JSON.stringify({ code }), - }) - const json = await response.json() - - const { name } = json as UseVerificationOAuthReturnType - - if (!name) + queryKey: [{ token }, chainId], + }: QueryFunctionContext<QueryKey<TParams>>): Promise<UseDentityProfileReturnType> => { + if (!token || !token.name || !token.verifiedPresentationUri) { return { - verifier, - ...json, + verifier: 'dentity', + ...token, } + } + + const { name } = token // Get resolver address since it will be needed for setting verification record const client = config.getClient({ chainId }) const records = await getRecords(client, { name, texts: [VERIFICATION_RECORD_KEY] }) - // Get owner data to const ownerData = await getOwner(client, { name }) const { owner, registrant, ownershipLevel } = ownerData || {} - const _owner = ownershipLevel === 'registrar' ? registrant : owner const manager = ownershipLevel === 'registrar' ? owner : undefined - const userWithSetRecordAbility = manager ?? _owner const primaryName = userWithSetRecordAbility ? await getName(client, { address: userWithSetRecordAbility }) : undefined - const data = { - ...json, - verifier, + ...token, + verifier: 'dentity' as const, owner: _owner, manager, - primaryName, + primaryName: primaryName?.name, resolverAddress: records.resolverAddress, verificationRecord: records.texts.find((text) => text.key === VERIFICATION_RECORD_KEY)?.value, } return data } -export const useVerificationOAuth = <TParams extends UseVerificationOAuthParameters>({ +export const useDentityProfile = <TParams extends UseVerificationOAuthParameters>({ enabled = true, - onSuccess, gcTime, staleTime, scopeKey, @@ -99,13 +85,13 @@ export const useVerificationOAuth = <TParams extends UseVerificationOAuthParamet scopeKey, functionName: 'getVerificationOAuth', queryDependencyType: 'standard', - queryFn: getVerificationOAuth, + queryFn: getDentityProfile, }) const preparedOptions = prepareQueryOptions({ queryKey: initialOptions.queryKey, queryFn: initialOptions.queryFn, - enabled: enabled && !!params.verifier && !!params.code, + enabled: enabled && !!params.token, gcTime, staleTime, retry: 0, diff --git a/src/hooks/verification/useDentityToken/useDentityToken.ts b/src/hooks/verification/useDentityToken/useDentityToken.ts new file mode 100644 index 000000000..bfb235e07 --- /dev/null +++ b/src/hooks/verification/useDentityToken/useDentityToken.ts @@ -0,0 +1,67 @@ +import { QueryFunctionContext, useQuery } from '@tanstack/react-query' + +import { VERIFICATION_OAUTH_BASE_URL } from '@app/constants/verification' +import { useQueryOptions } from '@app/hooks/useQueryOptions' +import { CreateQueryKey, QueryConfig } from '@app/types' +import { prepareQueryOptions } from '@app/utils/prepareQueryOptions' + +export type DentityFederatedToken = { + name: string + verifiedPresentationUri: string +} + +type UseDentityTokenParameters = { + code?: string | null +} + +export type UseDentityTokenReturnType = DentityFederatedToken + +type UseVerificationOAuthConfig = QueryConfig<UseDentityTokenReturnType, Error> + +type QueryKey<TParams extends UseDentityTokenParameters> = CreateQueryKey< + TParams, + 'getDentityToken', + 'independent' +> + +export const getDentityToken = async <TParams extends UseDentityTokenParameters>({ + queryKey: [{ code }], +}: QueryFunctionContext<QueryKey<TParams>>): Promise<UseDentityTokenReturnType> => { + // Get federated token from oidc worker + const url = `${VERIFICATION_OAUTH_BASE_URL}/dentity/token` + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify({ code }), + }) + const json = await response.json() + + return json as UseDentityTokenReturnType +} + +export const useDentityToken = <TParams extends UseDentityTokenParameters>({ + enabled = true, + gcTime, + scopeKey, + ...params +}: TParams & UseVerificationOAuthConfig) => { + const initialOptions = useQueryOptions({ + params, + scopeKey, + functionName: 'getDentityToken', + queryDependencyType: 'independent', + queryFn: getDentityToken, + }) + + const preparedOptions = prepareQueryOptions({ + queryKey: initialOptions.queryKey, + queryFn: initialOptions.queryFn, + enabled: enabled && !!params.code, + gcTime, + staleTime: Infinity, + retry: 0, + }) + + const query = useQuery(preparedOptions) + + return query +} diff --git a/src/hooks/verification/useVerificationOAuth/utils/getAPIEndpointForVerifier.ts b/src/hooks/verification/useVerificationOAuth/utils/getAPIEndpointForVerifier.ts deleted file mode 100644 index b49c6ce8c..000000000 --- a/src/hooks/verification/useVerificationOAuth/utils/getAPIEndpointForVerifier.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { match } from 'ts-pattern' - -import { VERIFICATION_OAUTH_BASE_URL } from '@app/constants/verification' - -export const getAPIEndpointForVerifier = (verifier?: string | null): string => { - return match(verifier) - .with('dentity', () => `${VERIFICATION_OAUTH_BASE_URL}/dentity/token`) - .otherwise(() => '') -} diff --git a/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts b/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts index fd1f2bcbe..105c8e370 100644 --- a/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts +++ b/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts @@ -7,17 +7,12 @@ import { useAccount } from 'wagmi' import type { VerificationErrorDialogProps } from '@app/components/pages/VerificationErrorDialog' import { DENTITY_ISS } from '@app/constants/verification' import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory' -import { VerificationProtocol } from '@app/transaction-flow/input/VerifyProfile/VerifyProfile-flow' import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' -import { useVerificationOAuth } from '../useVerificationOAuth/useVerificationOAuth' +import { useDentityProfile } from '../useDentityProfile/useDentityProfile' +import { useDentityToken } from '../useDentityToken/useDentityToken' import { dentityVerificationHandler } from './utils/dentityHandler' -const issToVerificationProtocol = (iss: string | null): VerificationProtocol | null => { - if (iss === DENTITY_ISS) return 'dentity' - return null -} - type UseVerificationOAuthHandlerReturnType = { dialogProps: VerificationErrorDialogProps } @@ -32,14 +27,23 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn const { address: userAddress } = useAccount() - const isReady = !!createTransactionFlow && !!router && !!iss && !!code - - const { data, isLoading, error } = useVerificationOAuth({ - verifier: issToVerificationProtocol(iss), + const isReady = !!createTransactionFlow && !!router && !!iss && !!code && iss === DENTITY_ISS + const { data: dentityToken, isLoading: isDentityTokenLoading } = useDentityToken({ code, enabled: isReady, }) + const isReadyToFetchProfile = !!dentityToken && !isDentityTokenLoading + const { + data, + isLoading: isDentityProfileLoading, + error, + } = useDentityProfile({ + token: dentityToken, + enabled: isReadyToFetchProfile, + }) + + const isLoading = isDentityTokenLoading || isDentityProfileLoading const [dialogProps, setDialogProps] = useState<VerificationErrorDialogProps>() const onClose = () => setDialogProps(undefined) const onDismiss = () => setDialogProps(undefined) @@ -64,7 +68,7 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn setDialogProps(newProps) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data, isLoading, error]) + }, [data, isDentityProfileLoading, error]) return { dialogProps, diff --git a/src/hooks/verification/useVerificationOAuthHandler/utils/createVerificationTransactionFlow.ts b/src/hooks/verification/useVerificationOAuthHandler/utils/createVerificationTransactionFlow.ts index 4a2dd12a0..4115d060a 100644 --- a/src/hooks/verification/useVerificationOAuthHandler/utils/createVerificationTransactionFlow.ts +++ b/src/hooks/verification/useVerificationOAuthHandler/utils/createVerificationTransactionFlow.ts @@ -3,10 +3,10 @@ import { Hash } from 'viem' import { createTransactionItem } from '@app/transaction-flow/transaction' import { CreateTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' -import { UseVerificationOAuthReturnType } from '../../useVerificationOAuth/useVerificationOAuth' +import { UseDentityProfileReturnType } from '../../useDentityProfile/useDentityProfile' type Props = Pick< - UseVerificationOAuthReturnType, + UseDentityProfileReturnType, 'name' | 'verifier' | 'resolverAddress' | 'verifiedPresentationUri' > & { userAddress?: Hash diff --git a/src/hooks/verification/useVerificationOAuthHandler/utils/dentityHandler.ts b/src/hooks/verification/useVerificationOAuthHandler/utils/dentityHandler.ts index 5a00fb73f..19e85809a 100644 --- a/src/hooks/verification/useVerificationOAuthHandler/utils/dentityHandler.ts +++ b/src/hooks/verification/useVerificationOAuthHandler/utils/dentityHandler.ts @@ -11,7 +11,7 @@ import { getDestination } from '@app/routes' import { CreateTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' import { shortenAddress } from '../../../../utils/utils' -import { UseVerificationOAuthReturnType } from '../../useVerificationOAuth/useVerificationOAuth' +import { UseDentityProfileReturnType } from '../../useDentityProfile/useDentityProfile' import { createVerificationTransactionFlow } from './createVerificationTransactionFlow' // Patterns @@ -49,7 +49,7 @@ export const dentityVerificationHandler = createTransactionFlow: CreateTransactionFlow t: TFunction }) => - (json: UseVerificationOAuthReturnType): VerificationErrorDialogProps => { + (json: UseDentityProfileReturnType): VerificationErrorDialogProps => { return match(json) .with( { From bec7dab3d67d988395ef9bdff6b5b63ea1895278 Mon Sep 17 00:00:00 2001 From: storywithoutend <storywithoutend@gmail.com> Date: Fri, 25 Oct 2024 00:05:36 +0800 Subject: [PATCH 2/2] used wrong boolean flag in dependency array --- .../useVerificationOAuthHandler/useVerificationOAuthHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts b/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts index 105c8e370..811e2e45c 100644 --- a/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts +++ b/src/hooks/verification/useVerificationOAuthHandler/useVerificationOAuthHandler.ts @@ -68,7 +68,7 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn setDialogProps(newProps) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data, isDentityProfileLoading, error]) + }, [data, isLoading, error]) return { dialogProps,