Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/browser/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,12 @@
"./src/**/__jest__",
"./npm"
]
},
"lint": {
"rules": {
"exclude": [
"require-await"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type WebAuthnErrorName =
| 'NotAllowedError'
| 'NotSupportedError'
| 'SecurityError'
| 'TypeError'
| 'UnknownError';

export function generateCustomError(
Expand Down
81 changes: 81 additions & 0 deletions packages/browser/src/helpers/identifySignalError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { isValidDomain } from './isValidDomain.ts';
import { WebAuthnError } from './webAuthnError.ts';
import type {
SendSignalAllAcceptedCredentialsOpts,
SendSignalCurrentUserDetailsOpts,
SendSignalUnknownCredentialOpts,
} from '../methods/sendSignal.ts';

/**
* Attempt to intuit _why_ an error was raised after calling one of the WebAuthn Signal APIs
*/
export function identifySignalError({ error, options }: {
error: Error;
options:
| SendSignalUnknownCredentialOpts
| SendSignalAllAcceptedCredentialsOpts
| SendSignalCurrentUserDetailsOpts;
}): WebAuthnError {
/**
* General Signal API error conditions
*/
if (error.name === 'SecurityError') {
const effectiveDomain = globalThis.location.hostname;
if (!isValidDomain(effectiveDomain)) {
// https://w3c.github.io/webauthn/#sctn-signal-methods-async-rp-id-validation (Step 1)
return new WebAuthnError({
message: `"${globalThis.location.hostname}" is an invalid domain`,
code: 'ERROR_INVALID_DOMAIN',
cause: error,
});
}

// https://w3c.github.io/webauthn/#sctn-signal-methods-async-rp-id-validation (Step 3)
return new WebAuthnError({
message:
`The browser does not support Related Origins to enable signals for RP ID "${options.rpID}" on domain "${globalThis.location.hostname}"`,
code: 'ERROR_INVALID_RP_ID',
cause: error,
});
}

/**
* Signal-specific error conditions
*/
if (options.signalName === 'unknownCredential') {
if (error.name === 'TypeError') {
// https://w3c.github.io/webauthn/#sctn-signalUnknownCredential (Step 1)
return new WebAuthnError({
message: 'credentialID is an invalid base64url string',
code: 'ERROR_SIGNAL_INVALID_ARGUMENT',
cause: error,
});
}
} else if (options.signalName === 'allAcceptedCredentials') {
if (error.name === 'TypeError') {
// https://w3c.github.io/webauthn/#sctn-signalAllAcceptedCredentials (Step 1)
// https://w3c.github.io/webauthn/#sctn-signalAllAcceptedCredentials (Step 2)
return new WebAuthnError({
message: 'userID, or an entry in allAcceptedCredentialIDs, is an invalid base64url string',
code: 'ERROR_SIGNAL_INVALID_ARGUMENT',
cause: error,
});
}
} else if (options.signalName === 'currentUserDetails') {
if (error.name === 'TypeError') {
// https://w3c.github.io/webauthn/#sctn-signalCurrentUserDetails
return new WebAuthnError({
message: 'userID is an invalid base64url string',
code: 'ERROR_SIGNAL_INVALID_ARGUMENT',
cause: error,
});
}
}

// Consistently return a WebAuthnError, but point to the original error for more info
return new WebAuthnError({
message: error.message,
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: error,
});
}
1 change: 1 addition & 0 deletions packages/browser/src/helpers/webAuthnError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ export type WebAuthnErrorCode =
| 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED'
| 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG'
| 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE'
| 'ERROR_SIGNAL_INVALID_ARGUMENT'
| 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY';
4 changes: 4 additions & 0 deletions packages/browser/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ Deno.test('should export method `getBrowserCapabilities`', () => {
Deno.test('should export method `browserSupportsPasskeys`', () => {
assert(index.browserSupportsPasskeys);
});

Deno.test('should export method `sendSignal`', () => {
assert(index.sendSignal);
});
1 change: 1 addition & 0 deletions packages/browser/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './methods/startRegistration.ts';
export * from './methods/startAuthentication.ts';
export * from './methods/sendSignal.ts';
export * from './helpers/browserSupportsWebAuthn.ts';
export * from './helpers/browserSupportsPasskeys.ts';
export * from './helpers/platformAuthenticatorIsAvailable.ts';
Expand Down
Loading