Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(passport): account mask address #2743

Merged
merged 2 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
18 changes: 11 additions & 7 deletions apps/passport/app/components/accounts/AccountList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AccountURNSpace } from '@proofzero/urns/account'
import type { AccountURN } from '@proofzero/urns/account'

import { EmailAccountType } from '@proofzero/types/account'

import { List } from '@proofzero/design-system/src/atoms/lists/List'
import { Text } from '@proofzero/design-system/src/atoms/text/Text'

Expand All @@ -22,13 +24,15 @@ export const AccountList = ({
}: AccountListProps) => {
return accounts.length ? (
<List
items={accounts.map((ali) => ({
key: ali.id,
val: ali,
primary:
AccountURNSpace.decode(ali.id as AccountURN) ===
AccountURNSpace.decode(primaryAccountURN),
}))}
items={accounts
.filter((ali) => ali.type !== EmailAccountType.Mask)
.map((ali) => ({
key: ali.id,
val: ali,
primary:
AccountURNSpace.decode(ali.id as AccountURN) ===
AccountURNSpace.decode(primaryAccountURN),
}))}
itemRenderer={(item) => (
<AccountListItem
key={item.key}
Expand Down
62 changes: 43 additions & 19 deletions apps/passport/app/components/applications/claims.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Form, useTransition } from '@remix-run/react'

import { Button, Text } from '@proofzero/design-system'
import { FaChevronDown, FaChevronRight } from 'react-icons/fa'
import { TbShield } from 'react-icons/tb'

import { EmailAccountType } from '@proofzero/types/account'

import MultiAvatar from '@proofzero/design-system/src/molecules/avatar/MultiAvatar'
import UserPill from '@proofzero/design-system/src/atoms/pills/UserPill'
Expand All @@ -10,12 +13,14 @@ import { Disclosure } from '@headlessui/react'
import { useState } from 'react'

import passportLogoURL from '~/assets/PassportIcon.svg'
import { HiOutlineMail } from 'react-icons/hi'
import { TbCrown } from 'react-icons/tb'
import { Modal } from '@proofzero/design-system/src/molecules/modal/Modal'
import warningImg from '~/assets/warning.svg'
import InputText from '~/components/inputs/InputText'
import { startCase } from 'lodash'
import { HiOutlineExternalLink, HiOutlineX } from 'react-icons/hi'
import { EmailMaskedPill } from '@proofzero/design-system/src/atoms/pills/EmailMaskPill'

export const ConfirmRevocationModal = ({
title,
Expand Down Expand Up @@ -129,11 +134,16 @@ const AccountExpandedView = ({
<div className="flex flex-col gap-2 p-2.5 bg-white rounded-lg">
{connectedAccounts && (
<section className="flex flex-row gap-2 items-center">
<img src={account.icon} className="rounded-full w-5 h-5" />
{account.type === EmailAccountType.Mask ? (
<TbShield />
) : (
<img src={account.icon} className="rounded-full w-5 h-5" />
)}

<Text size="sm" weight="semibold" className="text-gray-800 truncate">
{account.address}
</Text>
{account.type === EmailAccountType.Mask ? <EmailMaskedPill /> : null}
</section>
)}

Expand Down Expand Up @@ -171,12 +181,14 @@ export const ClaimsMobileView = ({ scopes }: { scopes: any[] }) => {
const RowView = ({
account,
appAskedFor,
masked = false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be used in the mobile view

whatsBeingShared,
sourceOfData,
sourceOfDataIcon,
dropdown = true,
}: {
appAskedFor: string
masked: boolean
sourceOfData: string
sourceOfDataIcon: JSX.Element
dropdown?: boolean
Expand All @@ -203,6 +215,7 @@ export const ClaimsMobileView = ({ scopes }: { scopes: any[] }) => {
>
{appAskedFor}
</Text>
{masked && <EmailMaskedPill />}
{whatsBeingShared && (
<Text
size="sm"
Expand All @@ -229,6 +242,7 @@ export const ClaimsMobileView = ({ scopes }: { scopes: any[] }) => {
>
{appAskedFor}
</Text>
{masked && <EmailMaskedPill />}
{whatsBeingShared && (
<Text
size="sm"
Expand Down Expand Up @@ -373,6 +387,7 @@ export const ClaimsMobileView = ({ scopes }: { scopes: any[] }) => {
size={20}
text={scWallets ? a.title! : a.address}
avatarURL={a.icon}
masked={a.type === EmailAccountType.Mask}
onClick={() => setSelectedAccount(a)}
className={'pointer-events-auto'}
/>
Expand Down Expand Up @@ -421,11 +436,10 @@ export const ClaimsMobileView = ({ scopes }: { scopes: any[] }) => {
<RowView
key={i}
appAskedFor="Email"
whatsBeingShared={scope.account}
masked={scope.masked}
whatsBeingShared={scope.address}
sourceOfData={scope.account}
sourceOfDataIcon={
<img src={scope.icon} className="w-5 h-5 rounded-full" />
}
sourceOfDataIcon={<HiOutlineMail className="w-5 h-5" />}
dropdown={false}
/>
)
Expand Down Expand Up @@ -491,12 +505,14 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
const RowView = ({
account,
appAskedFor,
masked = false,
whatsBeingShared,
sourceOfData,
sourceOfDataIcon,
dropdown = true,
}: {
appAskedFor: string
masked: boolean
sourceOfData: string
sourceOfDataIcon: JSX.Element
dropdown?: boolean
Expand Down Expand Up @@ -527,15 +543,19 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
>
{appAskedFor}
</Text>
{masked && <EmailMaskedPill />}
</Disclosure.Button>
) : (
<Text
size="sm"
weight="medium"
className="text-gray-500 truncate"
>
{appAskedFor}
</Text>
<div className="flex space-x-1">
<Text
size="sm"
weight="medium"
className="text-gray-500 truncate"
>
{appAskedFor}
</Text>
{masked && <EmailMaskedPill />}
</div>
)}
</td>
<td className="px-6 py-3">
Expand Down Expand Up @@ -636,6 +656,7 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
| {
icon: string
address: string
source: string
type: string
title?: string
}
Expand Down Expand Up @@ -675,7 +696,9 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
Wallet(s)
</Text>
) : (
<MultiAvatar avatars={accounts.map((a) => a.icon)!} />
<MultiAvatar
avatars={accounts.filter((a) => a.icon).map((a) => a.icon)}
/>
)}
</td>
<td className="px-6 py-3 flex flex-row items-center gap-2.5">
Expand Down Expand Up @@ -706,6 +729,7 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
size={20}
text={scWallets ? a.title! : a.address}
avatarURL={a.icon}
masked={a.type === EmailAccountType.Mask}
onClick={() => setSelectedAccount(a)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be a type mismatch here, the source might be missing from the accounts entries. If it's there and we want to get around the typescript checker maybe we should // @ts-ignore it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand.

className={'pointer-events-auto'}
/>
Expand All @@ -717,7 +741,8 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
source={
source
? source
: `${startCase(selectedAccount.type)} - ${
: selectedAccount.source ||
`${startCase(selectedAccount.type)} - ${
selectedAccount.address
}`
}
Expand Down Expand Up @@ -754,11 +779,10 @@ export const ClaimsWideView = ({ scopes }: { scopes: any[] }) => {
<RowView
key={i}
appAskedFor="Email"
whatsBeingShared={scope.account}
sourceOfData={scope.account}
sourceOfDataIcon={
<img src={scope.icon} className="w-5 h-5 rounded-full" />
}
masked={scope.masked}
whatsBeingShared={scope.address}
sourceOfData={scope.masked ? scope.source : scope.address}
sourceOfDataIcon={<HiOutlineMail className="w-5 h-5" />}
dropdown={false}
/>
)
Expand Down
83 changes: 76 additions & 7 deletions apps/passport/app/routes/authorize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,7 @@ export const action: ActionFunction = async ({ request, context }) => {
const scope = (form.get('scopes') as string).split(' ')
/* This stores the selection made from the user in the authorization
screen; gets validated and stored for later retrieval at token generation stage */
const personaData = JSON.parse(
form.get('personaData') as string
) as PersonaData
let personaData = JSON.parse(form.get('personaData') as string) as PersonaData

const state = form.get('state') as string
const clientId = form.get('client_id') as string
Expand Down Expand Up @@ -369,6 +367,15 @@ export const action: ActionFunction = async ({ request, context }) => {
context.traceSpan
)

if (personaData.email) {
const emailAccountCoreClient = getCoreClient({
context,
jwt,
accountURN: personaData.email,
})
await emailAccountCoreClient.account.resolveIdentity.query({ jwt })
}

const authorizeRes = await coreClient.authorization.authorize.mutate({
identity: identityURN,
responseType,
Expand Down Expand Up @@ -397,13 +404,13 @@ export const action: ActionFunction = async ({ request, context }) => {
}

export default function Authorize() {
const loaderData = useLoaderData<LoaderData>()
const {
clientId,
appProfile,
scopeMeta,
state,
redirectOverride,
dataForScopes,
redirectUri,
profile,
prompt,
Expand All @@ -412,10 +419,11 @@ export default function Authorize() {

const userProfile = profile as UserProfile

const [dataForScopes, setDataForScopes] = useState(loaderData.dataForScopes)
const {
connectedEmails,
personaData,
requestedScope,
connectedEmails,
connectedAccounts,
connectedSmartContractWallets,
} = dataForScopes as DataForScopes
Expand All @@ -434,6 +442,54 @@ export default function Authorize() {
}
return selected
})

const [maskEmail, setMaskEmail] = useState<boolean>(false)
useEffect(() => {
setMaskEmailCallback()
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a dependency array, either an empty one: [] if its intended to run only on component mount, or possibly a [maskEmail, selectedEmail] one if its intended to run when one of the dependencies change.


const [loadingMaskEmail, setLoadingMaskEmail] = useState<boolean>(false)

const setMaskEmailCallback = async () => {
if (!maskEmail) return

const accountURN = selectedEmail?.value
if (!accountURN) return
if (selectedEmail.mask) return

setLoadingMaskEmail(true)

const response = await fetch('/create/account-mask', {
body: JSON.stringify({ clientId, state, accountURN }),
headers: { 'Content-Type': 'application/json' },
method: 'POST',
})

let maskAccount = selectedEmail
const [mask] = await response.json<DropdownSelectListItem[]>()

setDataForScopes((state) => ({
...state,
connectedAccounts: connectedAccounts.map((ca) => {
if (ca.value !== accountURN) return ca
return {
...ca,
mask,
}
}),
connectedEmails: connectedEmails.map((ce) => {
if (ce.value !== accountURN) return ce
maskAccount = {
...ce,
mask,
}
return maskAccount
}),
}))
setSelectedEmail(maskAccount)
setLoadingMaskEmail(false)
}

const [selectedConnectedAccounts, setSelectedConnectedAccounts] = useState<
Array<DropdownSelectListItem> | Array<AuthorizationControlSelection>
>(() => {
Expand Down Expand Up @@ -510,7 +566,9 @@ export default function Authorize() {
}

if (requestedScope.includes('email') && selectedEmail) {
personaData.email = selectedEmail.value
personaData.email = maskEmail
? selectedEmail.mask?.value
: selectedEmail.value
}

if (
Expand All @@ -521,7 +579,12 @@ export default function Authorize() {
personaData.connected_accounts = AuthorizationControlSelection.ALL
} else {
personaData.connected_accounts = selectedConnectedAccounts.map(
(account) => (account as DropdownSelectListItem).value
(account) => {
const item = account as DropdownSelectListItem
if (!maskEmail) return item.value
if (item.value === selectedEmail?.value) return item.mask?.value
return item.value
}
)
}
}
Expand Down Expand Up @@ -640,10 +703,13 @@ export default function Authorize() {
// Substituting subtitle with icon
// on the client side
return {
address: email.address,
type: email.type,
icon: getEmailIcon(email.subtitle!),
title: email.title,
selected: email.selected,
value: email.value,
mask: email.mask,
}
}) ?? []
}
Expand All @@ -661,6 +727,9 @@ export default function Authorize() {
}}
selectEmailCallback={setSelectedEmail}
selectedEmail={selectedEmail}
maskEmail={maskEmail}
loadingMaskEmail={loadingMaskEmail}
setMaskEmail={setMaskEmail}
connectedAccounts={connectedAccounts ?? []}
selectedConnectedAccounts={selectedConnectedAccounts}
addNewAccountCallback={() => {
Expand Down
Loading
Loading