104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
import { Username } from '@standardnotes/snjs'
|
|
import { ChallengePrompt } from '@standardnotes/services'
|
|
import { RefObject, useState } from 'react'
|
|
|
|
import { WebApplication } from '@/Application/WebApplication'
|
|
import { isAndroid } from '@/Utils'
|
|
|
|
import Button from '../Button/Button'
|
|
import Icon from '../Icon/Icon'
|
|
|
|
import { InputValue } from './InputValue'
|
|
import U2FPromptIframeContainer from './U2FPromptIframeContainer'
|
|
|
|
type Props = {
|
|
application: WebApplication
|
|
onValueChange: (value: InputValue['value'], prompt: ChallengePrompt) => void
|
|
prompt: ChallengePrompt
|
|
buttonRef: RefObject<HTMLButtonElement>
|
|
contextData?: Record<string, unknown>
|
|
}
|
|
|
|
const U2FPrompt = ({ application, onValueChange, prompt, buttonRef, contextData }: Props) => {
|
|
const [authenticatorResponse, setAuthenticatorResponse] = useState<Record<string, unknown> | null>(null)
|
|
const [error, setError] = useState('')
|
|
|
|
if (!application.isFullU2FClient && !isAndroid()) {
|
|
return (
|
|
<U2FPromptIframeContainer
|
|
contextData={contextData}
|
|
apiHost={application.getHost() || window.defaultSyncServer}
|
|
onResponse={(response) => {
|
|
onValueChange(response, prompt)
|
|
}}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<div className="min-w-76">
|
|
{error && <div className="text-red-500">{error}</div>}
|
|
<Button
|
|
primary
|
|
fullWidth
|
|
colorStyle={authenticatorResponse ? 'success' : 'info'}
|
|
onClick={async () => {
|
|
const usernameOrError = Username.create((contextData as { username: string }).username)
|
|
if (usernameOrError.isFailed()) {
|
|
setError(usernameOrError.getError())
|
|
return
|
|
}
|
|
const username = usernameOrError.getValue()
|
|
|
|
let authenticatorResponse: Record<string, unknown> | null = null
|
|
if (isAndroid()) {
|
|
const authenticatorOptionsOrError = await application.getAuthenticatorAuthenticationOptions.execute({
|
|
username: username.value,
|
|
})
|
|
if (authenticatorOptionsOrError.isFailed()) {
|
|
setError(authenticatorOptionsOrError.getError())
|
|
return
|
|
}
|
|
const authenticatorOptions = authenticatorOptionsOrError.getValue()
|
|
|
|
authenticatorResponse = await application
|
|
.mobileDevice()
|
|
.authenticateWithU2F(JSON.stringify(authenticatorOptions))
|
|
} else {
|
|
const authenticatorResponseOrError = await application.getAuthenticatorAuthenticationResponse.execute({
|
|
username: username.value,
|
|
})
|
|
|
|
if (authenticatorResponseOrError.isFailed()) {
|
|
setError(authenticatorResponseOrError.getError())
|
|
return
|
|
}
|
|
|
|
authenticatorResponse = authenticatorResponseOrError.getValue()
|
|
}
|
|
|
|
if (authenticatorResponse === null) {
|
|
setError('Failed to obtain device response')
|
|
return
|
|
}
|
|
|
|
setAuthenticatorResponse(authenticatorResponse)
|
|
onValueChange(authenticatorResponse, prompt)
|
|
}}
|
|
ref={buttonRef}
|
|
>
|
|
{authenticatorResponse ? (
|
|
<span className="flex items-center justify-center gap-3">
|
|
<Icon type="check-circle" />
|
|
Obtained Device Response
|
|
</span>
|
|
) : (
|
|
'Authenticate Device'
|
|
)}
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
export default U2FPrompt
|