feat(mobile): add U2F support for Android devices (#2311)
* feat(mobile): add U2F support for Android devices * chore: fix specs
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { Username } from '@standardnotes/snjs'
|
||||
import { ChallengePrompt } from '@standardnotes/services'
|
||||
import { RefObject, useState } from 'react'
|
||||
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { isAndroid } from '@/Utils'
|
||||
|
||||
import Button from '../Button/Button'
|
||||
import Icon from '../Icon/Icon'
|
||||
|
||||
import { InputValue } from './InputValue'
|
||||
import U2FPromptIframeContainer from './U2FPromptIframeContainer'
|
||||
|
||||
@@ -18,7 +23,7 @@ const U2FPrompt = ({ application, onValueChange, prompt, buttonRef, contextData
|
||||
const [authenticatorResponse, setAuthenticatorResponse] = useState<Record<string, unknown> | null>(null)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
if (!application.isFullU2FClient) {
|
||||
if (!application.isFullU2FClient && !isAndroid()) {
|
||||
return (
|
||||
<U2FPromptIframeContainer
|
||||
contextData={contextData}
|
||||
@@ -28,48 +33,71 @@ const U2FPrompt = ({ application, onValueChange, prompt, buttonRef, contextData
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} 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>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-w-76">
|
||||
{error && <div className="text-red-500">{error}</div>}
|
||||
<Button
|
||||
primary
|
||||
fullWidth
|
||||
colorStyle={authenticatorResponse ? 'success' : 'info'}
|
||||
onClick={async () => {
|
||||
if (!contextData || contextData.username === undefined) {
|
||||
setError('No username provided')
|
||||
return
|
||||
}
|
||||
|
||||
const authenticatorResponseOrError = await application.getAuthenticatorAuthenticationResponse.execute({
|
||||
username: contextData.username as string,
|
||||
})
|
||||
|
||||
if (authenticatorResponseOrError.isFailed()) {
|
||||
setError(authenticatorResponseOrError.getError())
|
||||
return
|
||||
}
|
||||
|
||||
const authenticatorResponse = authenticatorResponseOrError.getValue()
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user