Skip to content

feat: add PasskeyModule for WebAuthn P-256 session key registration a…#948

Merged
wheval merged 1 commit into
ancore-org:mainfrom
Hahfyeex:main
Jul 5, 2026
Merged

feat: add PasskeyModule for WebAuthn P-256 session key registration a…#948
wheval merged 1 commit into
ancore-org:mainfrom
Hahfyeex:main

Conversation

@Hahfyeex

@Hahfyeex Hahfyeex commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

closes #859

#859 [ACCOUNT-ABSTRACTION] Add PasskeyModule for WebAuthn P-256 session key registration and signing
Repo Avatar
ancore-org/ancore

Feature Description
Add a PasskeyModule to packages/account-abstraction/ that wraps the WebAuthn API to register P-256 public keys as session keys on the smart account and sign relay payloads using the authenticator.

Problem Statement
Ancore's AA value proposition is passwordless UX via WebAuthn — users authenticate with Touch ID or Face ID instead of managing seed phrases. This flow is not implemented anywhere in the codebase.

…nd signing

Implements ancore-org#859. Adds packages/account-abstraction/src/passkey/passkeyModule.ts
with three public functions:

- registerPasskey(): calls navigator.credentials.create() with ES256 (alg: -7),
  parses the SubjectPublicKeyInfo DER to extract P-256 x/y coordinates, and
  returns the credentialId plus key coordinates ready for on-chain registration
- buildAddSessionKeyInvocation(): builds an add_session_key contract invocation
  using the P-256 x-coordinate (32 bytes) as BytesN<32> with PERMISSION_EXECUTE
- signRelayPayload(): SHA-256 hashes the canonical payload JSON as the WebAuthn
  challenge, converts the DER ECDSA response to compact r‖s (128 hex chars), and
  returns authenticatorData + clientDataJSON for relay server verification

Also adds typed PasskeyError hierarchy, 27 unit tests with mocked
navigator.credentials, DOM lib to the package tsconfig, and barrel exports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@drips-wave

drips-wave Bot commented Jun 30, 2026

Copy link
Copy Markdown

@Hahfyeex Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds passkeyModule.ts to packages/account-abstraction implementing WebAuthn-based P-256 session key registration (registerPasskey), on-chain invocation construction (buildAddSessionKeyInvocation), and relay payload signing (signRelayPayload). Custom error classes, internal crypto helpers, full unit tests, package index re-exports, and a tsconfig.json lib update are included.

Changes

PasskeyModule WebAuthn P-256 Registration and Signing

Layer / File(s) Summary
Public types and error classes
packages/account-abstraction/src/passkey/passkeyModule.ts
Defines P256PublicKey, PasskeyRegistrationResult, PasskeySignatureResult, option interfaces, UnsignedRelayPayload, and PasskeyError base plus PasskeyNotSupportedError, PasskeyRegistrationError, PasskeySigningError subclasses with typed error codes.
Internal crypto and encoding utilities
packages/account-abstraction/src/passkey/passkeyModule.ts
Implements WebAuthn availability guard, base64url↔bytes↔hex converters, SPKI P-256 coordinate extractor, DER ECDSA→compact r‖s parser, and SHA-256 challenge derivation from relay payload JSON.
registerPasskey, buildAddSessionKeyInvocation, signRelayPayload
packages/account-abstraction/src/passkey/passkeyModule.ts
Wires the helpers into the three public API functions: credential creation with key extraction and expiry, session key invocation with PERMISSION_EXECUTE, and WebAuthn assertion with compact hex signature output.
Package index re-exports and tsconfig
packages/account-abstraction/src/index.ts, packages/account-abstraction/tsconfig.json
Re-exports all passkey runtime values and types from the package entry point; adds ES2022 and DOM to compilerOptions.lib.
Unit tests
packages/account-abstraction/src/__tests__/passkey-module.test.ts
Full test suite with DER/SPKI builder helpers, Jest mocks for navigator.credentials, and cases for all three public functions covering success paths, option propagation, encoding correctness, DER padding stripping, challenge determinism, and all error branches.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 Hop hop, my paws now touch the key,
A WebAuthn dance, a P-256 spree!
DER bytes unwrapped, r and s revealed,
The passkey registered, the session sealed.
Base64url hops, no padding in sight—
This bunny signs payloads with biometric might! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The module adds passkey registration/signing, but diverges from #859’s API and low-s signature requirements, and the tests are Jest rather than vitest. Add the requested signWithPasskey(digest)->Uint8Array flow with low-s normalization, and align the test suite with vitest/@simplewebauthn mocks.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the new PasskeyModule and WebAuthn session-key work.
Out of Scope Changes check ✅ Passed All changes stay within the passkey module, its exports, tests, and needed DOM typing updates.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
packages/account-abstraction/src/__tests__/passkey-module.test.ts (1)

244-251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert expiresAt and PERMISSION_EXECUTE to match the test's intent.

The test name claims "x-coordinate and PERMISSION_EXECUTE", and args length is asserted as 3, but only args[0] (the x-coordinate) is validated. The expiry (args[1]) and permission (args[2]) encodings are unchecked, so a regression in either would pass silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/__tests__/passkey-module.test.ts` around
lines 244 - 251, The test for buildAddSessionKeyInvocation only verifies the
x-coordinate, so it can miss regressions in the other encoded arguments. Update
the assertions in passkey-module.test.ts to also validate inv.args[1] for the
expiresAt encoding and inv.args[2] for PERMISSION_EXECUTE, using the same
invocation object from buildAddSessionKeyInvocation so the test matches its
stated intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/account-abstraction/src/__tests__/passkey-module.test.ts`:
- Around line 286-295: The current test only verifies DER padding removal in
signRelayPayload and does not cover signature normalization for a high-s
signature. Update the passkey-module test by introducing a high-s vector for
mockS, then assert that the compact signature output uses n - s instead of the
original high-s value; keep the existing mockGet/makeAssertionCredential flow
and use signRelayPayload plus the returned signature to validate both padding
stripping and low-s normalization.

In `@packages/account-abstraction/src/passkey/passkeyModule.ts`:
- Around line 289-292: The WebAuthn awaits in passkeyModule should not leak DOM
exceptions; wrap the navigator.credentials.create flow in the registration path
and the corresponding signing path in try/catch, then rethrow as
PasskeyRegistrationError or PasskeySigningError respectively. Update the logic
around the credential creation/assertion calls so browser cancellation and
authenticator failures are converted into the module’s promised error types,
while keeping the existing null/type checks for non-exception failures.
- Around line 232-237: The challenge canonicalization in passkeyModule.ts only
normalizes the top-level payload shape, so nested parameters can still hash
differently across equivalent requests. Update the canonicalization used in the
WebAuthn challenge flow around the payload JSON.stringify in the passkeyModule
code to recursively normalize nested payload data, especially
payload.parameters, before hashing so the same logical request always produces
the same challenge.
- Around line 211-220: The DER-to-compact signature parsing logic currently
strips padding for r and s but does not normalize s to low-s, so update the
signature helper in passkeyModule.ts to canonicalize s before building the
fixed-width output. Detect high-s values, convert them to the low-s equivalent
using the curve order, then continue returning the 32-byte rFixed and sFixed
pair so the compact signature is non-malleable and verifier-friendly.
- Around line 121-129: The WebAuthn guard in assertWebAuthnSupport() only
validates navigator.credentials.create, but signRelayPayload() also relies on
navigator.credentials.get. Update the guard to check that
navigator.credentials.get is available as a function as well, and keep throwing
PasskeyNotSupportedError when either WebAuthn method is missing so partial mocks
fail consistently.

---

Nitpick comments:
In `@packages/account-abstraction/src/__tests__/passkey-module.test.ts`:
- Around line 244-251: The test for buildAddSessionKeyInvocation only verifies
the x-coordinate, so it can miss regressions in the other encoded arguments.
Update the assertions in passkey-module.test.ts to also validate inv.args[1] for
the expiresAt encoding and inv.args[2] for PERMISSION_EXECUTE, using the same
invocation object from buildAddSessionKeyInvocation so the test matches its
stated intent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 098b6555-d030-4eb6-a028-c374ae56199f

📥 Commits

Reviewing files that changed from the base of the PR and between 4867ec0 and e9829af.

📒 Files selected for processing (4)
  • packages/account-abstraction/src/__tests__/passkey-module.test.ts
  • packages/account-abstraction/src/index.ts
  • packages/account-abstraction/src/passkey/passkeyModule.ts
  • packages/account-abstraction/tsconfig.json

Comment on lines +286 to +295
it('strips the leading 0x00 DER padding byte from r and s', async () => {
const paddedDer = buildDerSig(mockR, mockS, true, true);
mockGet.mockResolvedValue(makeAssertionCredential({ sig: paddedDer }));

const { signature } = await signRelayPayload(unsignedPayload, 'AQID');

expect(signature).toHaveLength(128);
const rHex = mockR.reduce((acc, b) => acc + b.toString(16).padStart(2, '0'), '');
expect(signature.slice(0, 64)).toBe(rHex);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP 'parseDerSignature|low[-_ ]?s|n\s*-\s*s|SECP256R1|0xFFFFFFFF00000000' packages/account-abstraction/src/passkey/passkeyModule.ts -C3

Repository: ancore-org/ancore

Length of output: 1136


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the signing path around parseDerSignature and signature assembly.
sed -n '160,420p' packages/account-abstraction/src/passkey/passkeyModule.ts

# Search for any low-s normalization logic anywhere in the passkey module/package.
rg -n --hidden -S 'low[-_ ]?s|n\s*-\s*s|canonical low[-_ ]?s|SECP256R1|P-256|signature.*normalize|normalize.*signature' packages/account-abstraction/src -C 3

# Inspect the related test file for existing high-s coverage.
sed -n '1,380p' packages/account-abstraction/src/__tests__/passkey-module.test.ts

Repository: ancore-org/ancore

Length of output: 33276


Exercise the high-s path too. This case only strips DER padding; mockS is already below n/2, so it doesn’t cover signature normalization. Add a high-s vector and assert the compact result uses n - s, since signRelayPayload currently just returns r || s unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/__tests__/passkey-module.test.ts` around
lines 286 - 295, The current test only verifies DER padding removal in
signRelayPayload and does not cover signature normalization for a high-s
signature. Update the passkey-module test by introducing a high-s vector for
mockS, then assert that the compact signature output uses n - s instead of the
original high-s value; keep the existing mockGet/makeAssertionCredential flow
and use signRelayPayload plus the returned signature to validate both padding
stripping and low-s normalization.

Comment on lines +121 to +129
function assertWebAuthnSupport(): void {
if (
typeof navigator === 'undefined' ||
typeof navigator.credentials === 'undefined' ||
typeof navigator.credentials.create !== 'function'
) {
throw new PasskeyNotSupportedError();
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file outline ==\n'
ast-grep outline packages/account-abstraction/src/passkey/passkeyModule.ts --view expanded || true

printf '\n== relevant lines ==\n'
sed -n '1,260p' packages/account-abstraction/src/passkey/passkeyModule.ts | cat -n

printf '\n== usages ==\n'
rg -n "assertWebAuthnSupport|signRelayPayload|navigator\.credentials\.(create|get)" packages/account-abstraction/src/passkey -S || true

Repository: ancore-org/ancore

Length of output: 14427


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '340,390p' packages/account-abstraction/src/passkey/passkeyModule.ts | cat -n

Repository: ancore-org/ancore

Length of output: 2169


Check navigator.credentials.get() in the WebAuthn guard. signRelayPayload() calls navigator.credentials.get(), but assertWebAuthnSupport() only checks create(). In partial mocks or incomplete WebAuthn implementations, signing can throw a raw TypeError instead of PasskeyNotSupportedError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/passkey/passkeyModule.ts` around lines 121 -
129, The WebAuthn guard in assertWebAuthnSupport() only validates
navigator.credentials.create, but signRelayPayload() also relies on
navigator.credentials.get. Update the guard to check that
navigator.credentials.get is available as a function as well, and keep throwing
PasskeyNotSupportedError when either WebAuthn method is missing so partial mocks
fail consistently.

Comment on lines +211 to +220
// Strip DER sign-padding zero.
if (r.length === 33 && r[0] === 0x00) r = r.slice(1);
if (s.length === 33 && s[0] === 0x00) s = s.slice(1);

const rFixed = new Uint8Array(32);
const sFixed = new Uint8Array(32);
rFixed.set(r, 32 - r.length);
sFixed.set(s, 32 - s.length);

return { r: rFixed, s: sFixed };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Normalize s to low-s before returning the compact signature.

The DER parser strips padding but returns high-s signatures unchanged, which misses the linked requirement and can produce malleable or verifier-rejected signatures.

Suggested fix
+const P256_N = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551');
+const P256_HALF_N = P256_N >> 1n;
+
+function bytesToBigInt(bytes: Uint8Array): bigint {
+  return BigInt(`0x${toHex(bytes)}`);
+}
+
+function bigIntTo32Bytes(value: bigint): Uint8Array {
+  const hex = value.toString(16).padStart(64, '0');
+  const out = new Uint8Array(32);
+  for (let i = 0; i < 32; i++) {
+    out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
+  }
+  return out;
+}
+
   // Strip DER sign-padding zero.
   if (r.length === 33 && r[0] === 0x00) r = r.slice(1);
   if (s.length === 33 && s[0] === 0x00) s = s.slice(1);
+
+  if (r.length > 32 || s.length > 32) {
+    throw new PasskeySigningError('DER signature: invalid P-256 integer length');
+  }
 
   const rFixed = new Uint8Array(32);
   const sFixed = new Uint8Array(32);
   rFixed.set(r, 32 - r.length);
   sFixed.set(s, 32 - s.length);
 
-  return { r: rFixed, s: sFixed };
+  const sValue = bytesToBigInt(sFixed);
+  return { r: rFixed, s: sValue > P256_HALF_N ? bigIntTo32Bytes(P256_N - sValue) : sFixed };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Strip DER sign-padding zero.
if (r.length === 33 && r[0] === 0x00) r = r.slice(1);
if (s.length === 33 && s[0] === 0x00) s = s.slice(1);
const rFixed = new Uint8Array(32);
const sFixed = new Uint8Array(32);
rFixed.set(r, 32 - r.length);
sFixed.set(s, 32 - s.length);
return { r: rFixed, s: sFixed };
const P256_N = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551');
const P256_HALF_N = P256_N >> 1n;
function bytesToBigInt(bytes: Uint8Array): bigint {
return BigInt(`0x${toHex(bytes)}`);
}
function bigIntTo32Bytes(value: bigint): Uint8Array {
const hex = value.toString(16).padStart(64, '0');
const out = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
}
return out;
}
// Strip DER sign-padding zero.
if (r.length === 33 && r[0] === 0x00) r = r.slice(1);
if (s.length === 33 && s[0] === 0x00) s = s.slice(1);
if (r.length > 32 || s.length > 32) {
throw new PasskeySigningError('DER signature: invalid P-256 integer length');
}
const rFixed = new Uint8Array(32);
const sFixed = new Uint8Array(32);
rFixed.set(r, 32 - r.length);
sFixed.set(s, 32 - s.length);
const sValue = bytesToBigInt(sFixed);
return { r: rFixed, s: sValue > P256_HALF_N ? bigIntTo32Bytes(P256_N - sValue) : sFixed };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/passkey/passkeyModule.ts` around lines 211 -
220, The DER-to-compact signature parsing logic currently strips padding for r
and s but does not normalize s to low-s, so update the signature helper in
passkeyModule.ts to canonicalize s before building the fixed-width output.
Detect high-s values, convert them to the low-s equivalent using the curve
order, then continue returning the 32-byte rFixed and sFixed pair so the compact
signature is non-malleable and verifier-friendly.

Comment on lines +232 to +237
const canonical = JSON.stringify({
nonce: payload.nonce,
operation: payload.operation,
parameters: payload.parameters,
sessionKey: payload.sessionKey,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Canonicalize nested parameters before hashing.

This only fixes top-level key order. If the relay/server canonicalizes nested parameters differently, equivalent payloads can hash to different WebAuthn challenges and fail verification.

Suggested fix
+function canonicalize(value: unknown): unknown {
+  if (Array.isArray(value)) {
+    return value.map(canonicalize);
+  }
+
+  if (value !== null && typeof value === 'object') {
+    return Object.fromEntries(
+      Object.entries(value as Record<string, unknown>)
+        .sort(([a], [b]) => a.localeCompare(b))
+        .map(([key, nested]) => [key, canonicalize(nested)])
+    );
+  }
+
+  return value;
+}
+
 async function computePayloadChallenge(payload: UnsignedRelayPayload): Promise<Uint8Array> {
-  const canonical = JSON.stringify({
+  const canonical = JSON.stringify(canonicalize({
     nonce: payload.nonce,
     operation: payload.operation,
     parameters: payload.parameters,
     sessionKey: payload.sessionKey,
-  });
+  }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const canonical = JSON.stringify({
nonce: payload.nonce,
operation: payload.operation,
parameters: payload.parameters,
sessionKey: payload.sessionKey,
});
function canonicalize(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(canonicalize);
}
if (value !== null && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value as Record<string, unknown>)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, nested]) => [key, canonicalize(nested)])
);
}
return value;
}
async function computePayloadChallenge(payload: UnsignedRelayPayload): Promise<Uint8Array> {
const canonical = JSON.stringify(canonicalize({
nonce: payload.nonce,
operation: payload.operation,
parameters: payload.parameters,
sessionKey: payload.sessionKey,
}));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/passkey/passkeyModule.ts` around lines 232 -
237, The challenge canonicalization in passkeyModule.ts only normalizes the
top-level payload shape, so nested parameters can still hash differently across
equivalent requests. Update the canonicalization used in the WebAuthn challenge
flow around the payload JSON.stringify in the passkeyModule code to recursively
normalize nested payload data, especially payload.parameters, before hashing so
the same logical request always produces the same challenge.

Comment on lines +289 to +292
const credential = await navigator.credentials.create(creationOptions);

if (!credential || credential.type !== 'public-key') {
throw new PasskeyRegistrationError('Passkey creation was cancelled or failed');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wrap WebAuthn rejections in the module error types.

Browser cancellation and authenticator failures reject with DOM exceptions, so callers will not receive the promised PasskeyRegistrationError / PasskeySigningError unless these awaits are wrapped.

Suggested fix
-  const credential = await navigator.credentials.create(creationOptions);
+  let credential: Credential | null;
+  try {
+    credential = await navigator.credentials.create(creationOptions);
+  } catch (error) {
+    const detail = error instanceof Error ? `: ${error.message}` : '';
+    throw new PasskeyRegistrationError(`Passkey creation failed${detail}`);
+  }
...
-  const assertion = await navigator.credentials.get(requestOptions);
+  let assertion: Credential | null;
+  try {
+    assertion = await navigator.credentials.get(requestOptions);
+  } catch (error) {
+    const detail = error instanceof Error ? `: ${error.message}` : '';
+    throw new PasskeySigningError(`Passkey assertion failed${detail}`);
+  }

Also applies to: 372-375

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/account-abstraction/src/passkey/passkeyModule.ts` around lines 289 -
292, The WebAuthn awaits in passkeyModule should not leak DOM exceptions; wrap
the navigator.credentials.create flow in the registration path and the
corresponding signing path in try/catch, then rethrow as
PasskeyRegistrationError or PasskeySigningError respectively. Update the logic
around the credential creation/assertion calls so browser cancellation and
authenticator failures are converted into the module’s promised error types,
while keeping the existing null/type checks for non-exception failures.

@wheval wheval merged commit 6d99d66 into ancore-org:main Jul 5, 2026
8 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ACCOUNT-ABSTRACTION] Add PasskeyModule for WebAuthn P-256 session key registration and signing

2 participants