Skip to content

Commit

Permalink
Merge pull request #832 from ensdomains/dentity-cleanup
Browse files Browse the repository at this point in the history
Cleanup dentity code
  • Loading branch information
sugh01 committed Sep 18, 2024
2 parents 1d8aa24 + 2c557c5 commit 1fa176e
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 36 deletions.
43 changes: 43 additions & 0 deletions e2e/specs/stateless/verifications.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,49 @@ test.describe('OAuth flow', () => {
await page.pause()
})

test('Should show an error message if user is not logged in', async ({
page,
login,
makeName,
}) => {
const name = await makeName({
label: 'dentity',
type: 'legacy',
owner: 'user',
manager: 'user2',
})

await page.route(`${VERIFICATION_OAUTH_BASE_URL}/dentity/token`, async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
name,
verifiedPresentationUri: `${DENTITY_VPTOKEN_ENDPOINT}?name=${name}&federated_token=federated_token`,
}),
})
})

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.'),
).toBeVisible()

await page.locator('.modal').getByRole('button', { name: 'Done' }).click()

await page.pause()
await login.connect('user2')

// 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 ({
page,
login,
Expand Down
15 changes: 14 additions & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"editRoles": "Edit roles",
"setReminder": "Set reminder",
"import": "Import",
"connect": "Connect"
"connect": "Connect",
"goToProfile": "Go to profile"
},
"unit": {
"years_one": "{{count}} year",
Expand Down Expand Up @@ -403,5 +404,17 @@
"calendar": {
"pick_by_years": "Pick by years",
"pick_by_date": "Pick by date"
},
"verification": {
"verifiedBy": "Verified by {{ issuer }}",
"personhoodVerified": "Personhood verified",
"verificationFailed": "Verification failed, please reverify your profile"
},
"verificationErrorDialog": {
"title": "Verification failed",
"resolverRequired": "A valid resolver is required to complete the verification flow",
"ownerNotManager": "You must be connected as the Manager of this name to set the verification record. You can view and update the Manager under the Ownership tab.",
"wrongAccount": "You must be connected as <strong>{{ nameOrAddress }}</strong> to set the verification record.",
"default": "We could't verify your account. Please return to Dentity and try again."
}
}
3 changes: 2 additions & 1 deletion public/locales/en/transactionFlow.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,8 @@
"verifyProfile": {
"list": {
"title": "Verify your profile",
"message": " You can verify profile information and add proofs of personhood. Verified records will be marked on your profile with a blue check."
"message": " You can verify profile information and add proofs of personhood. Verified records will be marked on your profile with a blue check.",
"added": "Added"
},
"dentity": {
"title": "Dentity verification",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import { match } from 'ts-pattern'

Expand Down Expand Up @@ -41,14 +42,15 @@ const Content = styled.div(
)

export const VerificationBadgeAccountTooltipContent = ({ verifiers }: Props) => {
const { t } = useTranslation('common')
const verifier = verifiers?.[0]
return match(verifier)
.with('dentity', () => (
<Container as="a" href="https://dentity.com" target="_blank" $color="accent">
<DentitySVG />
<Content>
<Typography fontVariant="bodyBold" color="inherit">
Verified by Dentity
{t('verification.verifiedBy', { issuer: 'Dentity' })}
</Typography>
<OutlinkSVG />
</Content>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import { match } from 'ts-pattern'

import { Colors, Typography } from '@ensdomains/thorin'

import VerifiedPersonSVG from '@app/assets/VerifiedPerson.svg'
import { SupportOutlink } from '@app/components/@atoms/SupportOutlink'
import { CenteredTypography } from '@app/transaction-flow/input/ProfileEditor/components/CenteredTypography'

const Container = styled.div<{ $color: Colors }>(
Expand Down Expand Up @@ -42,6 +42,7 @@ export const VerificationBadgeVerifierTooltipContent = ({
}: {
isVerified: boolean
}) => {
const { t } = useTranslation('common')
return (
<Container $color="green">
{match(isVerified)
Expand All @@ -51,17 +52,13 @@ export const VerificationBadgeVerifierTooltipContent = ({
<VerifiedPersonSVG />
</IconWrapper>
<Content>
<Typography>Personhood verified</Typography>
<Typography>{t('verification.personhoodVerified')}</Typography>
</Content>
</>
))
.otherwise(() => (
<Content>
<CenteredTypography>
Verification failed, please reverify your profile
</CenteredTypography>
{/* TODO: NEED DOCUMENTATION LINK */}
<SupportOutlink href="https://docs.ens.domains">Learn more</SupportOutlink>
<CenteredTypography>{t('verification.verificationFailed')}</CenteredTypography>
</Content>
))}
</Container>
Expand Down
22 changes: 14 additions & 8 deletions src/components/pages/VerificationErrorDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ComponentProps } from 'react'
import { Trans } from 'react-i18next'

import { Button, Dialog } from '@ensdomains/thorin'

Expand All @@ -7,26 +8,31 @@ import { CenteredTypography } from '@app/transaction-flow/input/ProfileEditor/co
export type ButtonProps = ComponentProps<typeof Button>

export type VerificationErrorDialogProps =
| (Omit<ComponentProps<typeof Dialog>, 'variant' | 'children'> & {
title: string
message: string
actions: {
| (Omit<ComponentProps<typeof Dialog>, 'open' | 'variant' | 'children'> & {
open?: boolean
title?: string
message?: string | ComponentProps<typeof Trans>
actions?: {
leading?: ButtonProps
trailing: ButtonProps
}
})
| undefined

export const VerificationErrorDialog = (props: VerificationErrorDialogProps) => {
export const VerificationErrorDialog = (
props: VerificationErrorDialogProps = { open: false, title: '', message: '' },
) => {
if (!props) return null

const { title, message, actions, ...dialogProps } = props
const { title, message, actions, open, ...dialogProps } = props

const _message = typeof message === 'string' ? message : <Trans {...message} />

return (
<Dialog {...dialogProps} variant="blank">
<Dialog open={open ?? false} variant="blank" {...dialogProps}>
<Dialog.Heading title={title} alert="warning" />
<Dialog.Content>
<CenteredTypography>{message}</CenteredTypography>
<CenteredTypography>{_message}</CenteredTypography>
</Dialog.Content>
{actions && (
<Dialog.Footer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { QueryFunctionContext, useQuery } from '@tanstack/react-query'
import { Hash } from 'viem'

import { getOwner, getRecords } from '@ensdomains/ensjs/public'
import { getName, getOwner, getRecords } from '@ensdomains/ensjs/public'

import { VERIFICATION_RECORD_KEY } from '@app/constants/verification'
import { useQueryOptions } from '@app/hooks/useQueryOptions'
Expand All @@ -22,6 +22,7 @@ export type UseVerificationOAuthReturnType = {
name: string
owner: Hash
manager?: Hash
primaryName?: string
address: Hash
resolverAddress: Hash
verifiedPresentationUri: string
Expand Down Expand Up @@ -68,11 +69,17 @@ export const getVerificationOAuth =
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,
owner: _owner,
manager,
primaryName,
resolverAddress: records.resolverAddress,
verificationRecord: records.texts.find((text) => text.key === VERIFICATION_RECORD_KEY)?.value,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useSearchParams } from 'next/navigation'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { match } from 'ts-pattern'
import { useAccount } from 'wagmi'

Expand All @@ -26,11 +27,12 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn
const iss = searchParams.get('iss')
const code = searchParams.get('code')
const router = useRouterWithHistory()
const { t } = useTranslation('common')
const { createTransactionFlow } = useTransactionFlow()

const { address: userAddress } = useAccount()

const isReady = !!createTransactionFlow && !!router && !!userAddress && !!iss && !!code
const isReady = !!createTransactionFlow && !!router && !!iss && !!code

const { data, isLoading, error } = useVerificationOAuth({
verifier: issToVerificationProtocol(iss),
Expand All @@ -43,7 +45,7 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn
const onDismiss = () => setDialogProps(undefined)

useEffect(() => {
if (data && !isLoading && userAddress) {
if (data && !isLoading) {
const newProps = match(data)
.with(
{
Expand All @@ -55,6 +57,7 @@ export const useVerificationOAuthHandler = (): UseVerificationOAuthHandlerReturn
onDismiss,
router,
createTransactionFlow,
t,
}),
)
.otherwise(() => undefined)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TFunction } from 'i18next'
import { match, P } from 'ts-pattern'
import { Hash } from 'viem'

Expand All @@ -9,6 +10,7 @@ import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory'
import { getDestination } from '@app/routes'
import { CreateTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider'

import { shortenAddress } from '../../../../utils/utils'
import { UseVerificationOAuthReturnType } from '../../useVerificationOAuth/useVerificationOAuth'
import { createVerificationTransactionFlow } from './createVerificationTransactionFlow'

Expand Down Expand Up @@ -38,12 +40,14 @@ export const dentityVerificationHandler =
onDismiss,
router,
createTransactionFlow,
t,
}: {
userAddress?: Hash
onClose: () => void
onDismiss: () => void
router: ReturnType<typeof useRouterWithHistory>
createTransactionFlow: CreateTransactionFlow
t: TFunction
}) =>
(json: UseVerificationOAuthReturnType): VerificationErrorDialogProps => {
return match(json)
Expand All @@ -55,14 +59,15 @@ export const dentityVerificationHandler =
resolverAddress: P.string,
},
({ owner, manager }) => {
if (owner && manager) return manager === userAddress
return owner === userAddress
if (owner && manager) return manager.toLowerCase() === userAddress?.toLowerCase()
return owner.toLowerCase() === userAddress?.toLowerCase()
},
({ verifier, name, resolverAddress, verifiedPresentationUri, verificationRecord }) => {
router.push(`/${name}`)

const vcUris = verificationRecord ? tryJsonParse(verificationRecord) : []

// If the user has already verified the presentation, do not create a new transaction
if (Array.isArray(vcUris) && vcUris.includes(verifiedPresentationUri)) return undefined

createVerificationTransactionFlow({
Expand All @@ -82,16 +87,16 @@ export const dentityVerificationHandler =
open: true,
onDismiss,
onClose,
title: 'Verification failed',
message: 'Resolver address is required to complete verification flow',
title: t('verificationErrorDialog.title'),
message: t('verificationErrorDialog.resolverRequired'),
actions: {
leading: {
children: 'Close',
children: t('action.close'),
colorStyle: 'accentSecondary',
onClick: () => onClose(),
} as ButtonProps,
trailing: {
children: 'Go to profile',
children: t('action.goToProfile'),
as: 'a',
href: getDestination(`/${json.name}`),
} as ButtonProps,
Expand All @@ -103,12 +108,37 @@ export const dentityVerificationHandler =
() =>
({
open: true,
title: 'Verification failed',
message:
'You must be connected as the Manager of this name to set the verification record. You can view and update the Manager under the Ownership tab.',
title: t('verificationErrorDialog.title'),
message: t('verificationErrorDialog.ownerNotManager'),
actions: {
trailing: {
children: 'Done',
children: t('action.done'),
onClick: () => onClose(),
} as ButtonProps,
},
onClose,
onDismiss,
}) as VerificationErrorDialogProps,
)
.with(
{
owner: P.string,
name: P.string,
verifiedPresentationUri: P.string,
resolverAddress: P.string,
},
({ manager, owner, primaryName }) =>
({
open: true,
title: t('verificationErrorDialog.title'),
message: {
t,
i18nKey: 'verificationErrorDialog.wrongAccount',
values: { nameOrAddress: primaryName ?? shortenAddress(manager ?? owner) },
},
actions: {
trailing: {
children: t('action.done'),
onClick: () => onClose(),
} as ButtonProps,
},
Expand All @@ -120,11 +150,11 @@ export const dentityVerificationHandler =
() =>
({
open: true,
title: 'Verification failed',
message: "We could't verify your account. Please return to Dentity and try again.",
title: t('verificationErrorDialog.title'),
message: t('verificationErrorDialog.default'),
actions: {
trailing: {
children: 'Close',
children: t('action.close'),
colorStyle: 'accentSecondary',
onClick: () => onClose(),
} as ButtonProps,
Expand Down
Loading

0 comments on commit 1fa176e

Please sign in to comment.