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,