feat-dev: add U2F iframe for desktop client authentication (#2236)

This commit is contained in:
Karol Sójko
2023-03-07 00:52:35 +01:00
committed by GitHub
parent e5ca69fc8e
commit cf5330d7cf
25 changed files with 362 additions and 73 deletions

View File

@@ -1,6 +1,6 @@
import { FunctionComponent, useCallback, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { UseCaseInterface } from '@standardnotes/snjs'
import { AddAuthenticator } from '@standardnotes/snjs'
import DecoratedInput from '@/Components/Input/DecoratedInput'
import { UserProvider } from '@/Components/Preferences/Providers'
@@ -9,7 +9,7 @@ import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/u
type Props = {
userProvider: UserProvider
addAuthenticator: UseCaseInterface<void>
addAuthenticator: AddAuthenticator
onDeviceAddingModalToggle: (show: boolean) => void
onDeviceAdded: () => Promise<void>
}
@@ -82,11 +82,22 @@ const U2FAddDeviceView: FunctionComponent<Props> = ({
},
]}
>
<div className="w-25 h-25 flex items-center justify-center bg-info">...Some Cool Device Picture Here...</div>
<div className="flex flex-grow flex-col gap-2">
<DecoratedInput className={{ container: 'w-92 ml-4' }} value={deviceName} onChange={handleDeviceNameChange} />
<div className="flex px-4 py-4">
<div className="ml-4 flex flex-grow flex-col gap-1">
<label htmlFor="u2f-device-name" className="mb-2 text-sm font-semibold">
Device Name
</label>
<DecoratedInput
autofocus
id="u2f-device-name"
className={{ container: 'w-92' }}
value={deviceName}
onChange={handleDeviceNameChange}
onEnter={handleAddDeviceClick}
/>
{errorMessage && <div className="mt-1.5 text-danger">{errorMessage}</div>}
</div>
</div>
{errorMessage && <div className="text-error">{errorMessage}</div>}
</Modal>
)
}

View File

@@ -3,17 +3,27 @@ import { observer } from 'mobx-react-lite'
import { Text } from '@/Components/Preferences/PreferencesComponents/Content'
import { UserProvider } from '@/Components/Preferences/Providers'
import { useApplication } from '@/Components/ApplicationProvider'
type Props = {
userProvider: UserProvider
}
const U2FDescription: FunctionComponent<Props> = ({ userProvider }) => {
const application = useApplication()
if (userProvider.getUser() === undefined) {
return <Text>Sign in or register for an account to configure U2F.</Text>
}
return <Text>Authenticate with a U2F hardware device.</Text>
return (
<div>
<Text>Authenticate with a U2F hardware device such as Yubikey.</Text>
{!application.isFullU2FClient && (
<Text className="italic">Please visit the web app in order to add a U2F Device.</Text>
)}
</div>
)
}
export default observer(U2FDescription)

View File

@@ -1,9 +1,10 @@
import { FunctionComponent, useCallback } from 'react'
import { observer } from 'mobx-react-lite'
import { Text } from '@/Components/Preferences/PreferencesComponents/Content'
import { Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
import { WebApplication } from '@/Application/Application'
import Button from '@/Components/Button/Button'
import Icon from '@/Components/Icon/Icon'
type Props = {
application: WebApplication
@@ -31,24 +32,25 @@ const U2FDevicesList: FunctionComponent<Props> = ({ application, devices, onErro
)
return (
<div className="flex flex-row items-center">
<div>
{devices.length > 0 && (
<div className="flex flex-grow flex-col">
<div>
<Text>Devices:</Text>
<>
<Subtitle>Devices</Subtitle>
<div className="flex flex-grow flex-col divide-y divide-border">
{devices.map((device) => (
<div className="flex items-center py-2" key={`device-${device.id}`}>
<Icon type="security" />
<div className="ml-2 mr-auto text-sm">{device.name}</div>
<Button
small
key={device.id}
label="Delete"
onClick={async () => handleDeleteButtonOnClick(device.id)}
></Button>
</div>
))}
</div>
{devices.map((device) => (
<div key="device-{device.id}">
<Text>{device.name}</Text>
<Button
key={device.id}
primary={true}
label="Delete"
onClick={async () => handleDeleteButtonOnClick(device.id)}
></Button>
</div>
))}
</div>
</>
)}
</div>
)

View File

@@ -10,10 +10,10 @@ type Props = {
const U2FTitle: FunctionComponent<Props> = ({ userProvider }) => {
if (userProvider.getUser() === undefined) {
return <Title>Universal 2nd Factor authentication not available</Title>
return <Title>Universal 2nd factor authentication not available</Title>
}
return <Title>Universal 2nd Factor authentication</Title>
return <Title>Universal 2nd Factor Authentication</Title>
}
export default observer(U2FTitle)

View File

@@ -11,6 +11,7 @@ import U2FDescription from './U2FDescription'
import Button from '@/Components/Button/Button'
import U2FAddDeviceView from '../U2FAddDeviceView'
import U2FDevicesList from './U2FDevicesList'
import ModalOverlay from '@/Components/Modal/ModalOverlay'
type Props = {
application: WebApplication
@@ -45,34 +46,36 @@ const U2FView: FunctionComponent<Props> = ({ application, userProvider }) => {
<>
<PreferencesGroup>
<PreferencesSegment>
<div className="flex flex-row items-center">
<div className="flex flex-grow flex-col">
<U2FTitle userProvider={userProvider} />
<U2FDescription userProvider={userProvider} />
</div>
<PreferencesSegment>
<Button label="Add Device" primary onClick={handleAddDeviceClick} />
</PreferencesSegment>
<div className="flex flex-col">
<U2FTitle userProvider={userProvider} />
<U2FDescription userProvider={userProvider} />
</div>
</PreferencesSegment>
<PreferencesSegment>
{error && <div className="text-red-500">{error}</div>}
<PreferencesSegment classes="mt-2">
{error && <div className="text-danger">{error}</div>}
<U2FDevicesList
application={application}
devices={devices}
onError={setError}
onDeviceDeleted={loadAuthenticatorDevices}
/>
<Button
className="mt-1"
disabled={!application.isFullU2FClient}
label="Add Device"
primary
onClick={handleAddDeviceClick}
/>
</PreferencesSegment>
</PreferencesGroup>
{showDeviceAddingModal && (
<ModalOverlay isOpen={showDeviceAddingModal}>
<U2FAddDeviceView
onDeviceAddingModalToggle={setShowDeviceAddingModal}
onDeviceAdded={loadAuthenticatorDevices}
userProvider={userProvider}
addAuthenticator={application.addAuthenticator}
/>
)}
</ModalOverlay>
</>
)
}