Skip to content

Commit

Permalink
feat: Disable Hardware Authenticator identities for unsupported Netwo…
Browse files Browse the repository at this point in the history
…rks (#140)

Co-authored-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com>
  • Loading branch information
stanleyjones and fmorency authored Oct 12, 2023
1 parent de373d2 commit c65b4a1
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 33 deletions.
24 changes: 22 additions & 2 deletions src/features/accounts/components/accounts-menu/accounts-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react"
import { useEffect, useState } from "react"
import {
AnonymousIdentity,
ANON_IDENTITY,
Expand Down Expand Up @@ -26,10 +26,12 @@ import {
useDisclosure,
VStack,
UsbIcon,
useToast,
} from "@liftedinit/ui"
import { AddAccountModal } from "./add-account-modal"
import { EditAccountModal } from "./edit-account-modal"
import { Account, AccountId } from "../../types"
import { useNetworkContext } from "features/network"

export type AccountItemWithIdDisplayStrings = [
AccountId,
Expand Down Expand Up @@ -64,10 +66,28 @@ export function AccountsMenu() {
}),
)

const [editAccount, setEditAccount] = React.useState<
const [editAccount, setEditAccount] = useState<
[number, Account] | undefined
>()

const toast = useToast()
const { services } = useNetworkContext()
useEffect(() => {
; (async () => {
const isWebAuthnIdentity =
activeAccount?.identity instanceof WebAuthnIdentity
if (isWebAuthnIdentity && !services.has("idstore")) {
setActiveId(0) // reset to Anonymous
toast({
status: "warning",
title: "Unsupported Identity",
description:
"Selected Neighborhood does not support Hardware Authenticators",
})
}
})()
}, [activeAccount, services, setActiveId, toast])

function onEditClick(acct: [number, Account]) {
setEditAccount(acct)
onEditModalOpen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Tabs,
VStack,
} from "@liftedinit/ui"
import { useNetworkContext } from "features/network"
import React from "react"

import { SocialLogin } from "../social-login"
Expand Down Expand Up @@ -87,13 +88,13 @@ export function AddAccountModal({
)}
{(addMethod === AddAccountMethodTypes.importAuthenticator ||
addMethod === AddAccountMethodTypes.createAuthenticator) && (
<HardwareAuthenticator
addMethod={addMethod}
setAddMethod={setAddMethod}
onSuccess={onSuccess}
setShowDefaultFooter={setShowDefaultFooter}
/>
)}
<HardwareAuthenticator
addMethod={addMethod}
setAddMethod={setAddMethod}
onSuccess={onSuccess}
setShowDefaultFooter={setShowDefaultFooter}
/>
)}
{showDefaultFooter && (
<Modal.Footer>
<Flex justifyContent="flex-end">
Expand Down Expand Up @@ -164,6 +165,7 @@ const createCards = [
label: "Hardware Authenticator",
title: "create new with hardware authenticator",
onClickArg: AddAccountMethodTypes.createAuthenticator,
requires: "idstore",
},
]

Expand All @@ -172,18 +174,21 @@ function CreateAccountOptions({
}: {
onAddMethodClick: (method: AddAccountMethodTypes) => void
}) {
const { services } = useNetworkContext()
return (
<VStack alignItems="flex-start" w="full">
{createCards.map((c, idx) => {
return (
<AddAccountCard
key={idx}
label={c.label}
title={c.title}
onClick={() => onAddMethodClick(c.onClickArg)}
/>
)
})}
{createCards
.filter(c => !c.requires || services.has(c.requires))
.map((c, idx) => {
return (
<AddAccountCard
key={idx}
label={c.label}
title={c.title}
onClick={() => onAddMethodClick(c.onClickArg)}
/>
)
})}
</VStack>
)
}
Expand All @@ -203,25 +208,29 @@ const importCards = [
label: "Hardware Authenticator",
title: "import with hardware authenticator",
onClickArg: AddAccountMethodTypes.importAuthenticator,
requires: "idstore",
},
]
function ImportAcountOptions({
onAddMethodClick,
}: {
onAddMethodClick: (method: AddAccountMethodTypes) => void
}) {
const { services } = useNetworkContext()
return (
<VStack alignItems="flex-start" w="full">
{importCards.map((c, idx) => {
return (
<AddAccountCard
key={idx}
label={c.label}
title={c.title}
onClick={() => onAddMethodClick(c.onClickArg)}
/>
)
})}
{importCards
.filter(c => !c.requires || services.has(c.requires))
.map((c, idx) => {
return (
<AddAccountCard
key={idx}
label={c.label}
title={c.title}
onClick={() => onAddMethodClick(c.onClickArg)}
/>
)
})}
</VStack>
)
}
Expand Down
39 changes: 35 additions & 4 deletions src/features/network/network-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import React, { createContext, ReactNode, useContext, useMemo } from "react"
import {
createContext,
ReactNode,
useContext,
useEffect,
useMemo,
useState,
} from "react"
import {
Network,
Ledger,
Expand All @@ -15,14 +22,18 @@ export interface INetworkContext {
query?: Network
command?: Network
legacy?: Network[] // Legacy networks are past networks that are no longer active. They are used to query for past events.
services: Set<string>
}

export const NetworkContext = createContext<INetworkContext>({})
export const NetworkContext = createContext<INetworkContext>({
services: new Set(),
})

export function NetworkProvider({ children }: { children: ReactNode }) {
const network = useNetworkStore().getActiveNetwork()
const legacy_networks = useNetworkStore().getLegacyNetworks()
const account = useAccountsStore(s => s.byId.get(s.activeId))
const [services, setServices] = useState<Set<string>>(new Set())

const url = network.url
const legacy_urls = legacy_networks?.map(n => n.url)
Expand All @@ -43,8 +54,28 @@ export function NetworkProvider({ children }: { children: ReactNode }) {
const command = new Network(url, identity)
command.apply([Ledger, IdStore, Account])

return { query, command, legacy }
}, [account, url, legacy_urls])
return { query, command, legacy, services }
}, [account, url, legacy_urls, services])

useEffect(() => {
async function updateServices() {
if (!context.query || !context.query.base) {
return
}
const updated = new Set<string>()
try {
const { endpoints } = await context.query.base.endpoints()
endpoints
.map((endpoint: string) => endpoint.split(".")[0])
.forEach((service: string) => updated.add(service))
} catch (error) {
console.error(`Couldn't update services: ${(error as Error).message}`)
}
setServices(updated)
}
updateServices()
// eslint-disable-next-line
}, [url])

return (
<NetworkContext.Provider value={context}>
Expand Down

0 comments on commit c65b4a1

Please sign in to comment.