Skip to content

Commit

Permalink
fix(passport): Add error handlers for failed key registrations (#2684)
Browse files Browse the repository at this point in the history
  • Loading branch information
betimshahini authored Sep 14, 2023
1 parent d7b879e commit 4f9be18
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 14 deletions.
22 changes: 18 additions & 4 deletions apps/passport/app/routes/authenticate/$clientId/webauthn/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { WrappedSVG as WebauthnNewKeyIcon } from './WebauthnNewKeyIcon'
import {
verifySignedWebauthnChallenge,
createSignedWebauthnChallenge,
webauthnConstants,
} from './utils'
import { KeyPairSerialized } from '@proofzero/packages/types/application'

Expand Down Expand Up @@ -89,12 +90,12 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(

const passportUrl = new URL(request.url)
const f2l = new Fido2Lib({
timeout: 42,
timeout: webauthnConstants.timeout,
rpId: passportUrl.hostname,
rpName: 'Rollup ID',
challengeSize: 200,
challengeSize: webauthnConstants.challengeSize,
attestation: 'none',
cryptoParams: [-7, -257],
cryptoParams: webauthnConstants.cryptoAlgsArray,
authenticatorAttachment: 'cross-platform',
authenticatorRequireResidentKey: false,
authenticatorUserVerification: 'required',
Expand Down Expand Up @@ -181,6 +182,7 @@ export default () => {
const submit = useSubmit()

const [loginRequested, setLoginRequested] = useState(false)
const webauthnSupported = !!window.PublicKeyCredential

useEffect(() => {
const webauthnLogin = async () => {
Expand Down Expand Up @@ -260,10 +262,21 @@ export default () => {
Connect with Passkey
</Text>
</section>
{!webauthnSupported && (
<section><Text
size="sm"
weight="medium"
className="text-red-500 mt-4 mb-2 text-center"
>
Your browser does not support Passkeys. Please change your security settings or try another browser.
</Text></section>

)}

<section>
<div className="flex-1 w-full flex flex-col gap-4 relative">
<AuthButton
disabled={loginRequested}
disabled={loginRequested || !webauthnSupported}
Graphic={
<TbFingerprint className="w-full h-full dark:text-white"></TbFingerprint>
}
Expand All @@ -273,6 +286,7 @@ export default () => {
text="Use existing Passkey"
/>
<AuthButton
disabled={!webauthnSupported}
Graphic={WebauthnNewKeyIcon}
onClick={() => navigate('register')}
text="Add new Passkey"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import subtractLogo from '~/assets/subtract-logo.svg'
import {
createSignedWebauthnChallenge,
verifySignedWebauthnChallenge,
webauthnConstants
} from './utils'
import { BadRequestError } from '@proofzero/errors'
import { KeyPairSerialized } from '@proofzero/packages/types/application'
import { toast, ToastType } from '@proofzero/design-system/src/atoms/toast'

type RegistrationPayload = {
nickname: string
Expand All @@ -40,12 +42,12 @@ export const loader: LoaderFunction = getRollupReqFunctionErrorWrapper(
const passportUrl = new URL(request.url)

const f2l = new Fido2Lib({
timeout: 42,
timeout: webauthnConstants.timeout,
rpId: passportUrl.hostname,
rpName: 'Rollup ID',
challengeSize: 64,
challengeSize: webauthnConstants.challengeSize,
attestation: 'none',
cryptoParams: [-7, -257],
cryptoParams: webauthnConstants.cryptoAlgsArray,
authenticatorRequireResidentKey: false,
authenticatorUserVerification: 'required',
})
Expand Down Expand Up @@ -74,7 +76,7 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(

if (
!registrationPayload.nickname ||
(registrationPayload.nickname?.length < 4)
registrationPayload.nickname?.length < 4
)
throw new BadRequestError({
message: 'Name of key is required to be 4 or more characters',
Expand All @@ -99,7 +101,7 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
timeout: 42,
rpId: passportUrl.hostname,
rpName: 'Rollup ID',
challengeSize: 64,
challengeSize: 200,
attestation: 'none',
cryptoParams: [-7, -257],
authenticatorRequireResidentKey: false,
Expand Down Expand Up @@ -181,6 +183,8 @@ export default () => {
const [requestedRegistration, setRequestedRegistration] = useState(false)
const [keyName, setKeyName] = useState('')

const webauthnSupported = !!window.PublicKeyCredential

const randomBuffer = new Uint8Array(32)
crypto.getRandomValues(randomBuffer)
const registerKey = async (name: string) => {
Expand All @@ -189,9 +193,20 @@ export default () => {
name,
displayName: name,
}
let credential = await navigator.credentials.create({
publicKey: registrationOptions,
})
let credential
try {
credential = await navigator.credentials.create({
publicKey: registrationOptions,
})
} catch (e) {
console.error("Passkey registration error", JSON.stringify(e, null, 2))
if (e instanceof DOMException && e.name === 'NotAllowedError') {
toast(ToastType.Error, {
message:
'Your browser did not allow creation of the credential. You may need to try again or switch to another browser.',
})
}
}
if (
credential instanceof PublicKeyCredential &&
credential.response instanceof AuthenticatorAttestationResponse
Expand Down Expand Up @@ -252,10 +267,23 @@ export default () => {
Connect with Passkey
</Text>
</section>
{!webauthnSupported && (
<section>
<Text
size="sm"
weight="medium"
className="text-red-500 mt-4 mb-2 text-center"
>
Your browser does not support Passkeys. Please change your
security settings or try another browser.
</Text>
</section>
)}
<section className="flex-1">
<Input
type="text"
id="webauthn_nickname"
disabled={!webauthnSupported}
label="Name your Passkey"
className="h-12 rounded-lg"
onChange={(e) => setKeyName(e.target.value)}
Expand All @@ -276,7 +304,7 @@ export default () => {
setRequestedRegistration(true)
}}
className="w-full mt-4"
disabled={keyName.length < 4}
disabled={!webauthnSupported || keyName.length < 4}
>
Create new Passkey
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BadRequestError, InternalServerError } from '@proofzero/errors'
import { KeyPairSerialized } from '@proofzero/packages/types/application'
import { JWK, SignJWT, importJWK, jwtVerify, errors } from 'jose'
import { SignJWT, importJWK, jwtVerify, errors } from 'jose'

export const createSignedWebauthnChallenge = async (
keyPairJSON: KeyPairSerialized
Expand Down Expand Up @@ -35,3 +35,9 @@ export const verifySignedWebauthnChallenge = async (
})
}
}

export const webauthnConstants = {
challengeSize: 200,
timeout: 60,
cryptoAlgsArray: [-7, -8, -257],
}

0 comments on commit 4f9be18

Please sign in to comment.