@1shotapi/ows-signer 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1shotapi/ows-signer",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "OWS Signing Layer — plain JavaScript WebAuthn PRF key custody with curve-based signing (on-chain via EIP-8244)",
5
5
  "type": "module",
6
6
  "files": [
package/src/handlers.js CHANGED
@@ -37,6 +37,7 @@ import {
37
37
  promptPassphrase,
38
38
  promptPrivateKey,
39
39
  showPrivateKey,
40
+ updateCeremonyCopy,
40
41
  } from "./ui.js";
41
42
  import {
42
43
  createPasskeyCredential,
@@ -202,9 +203,14 @@ function ceremonyFieldsFromParams(params) {
202
203
  *
203
204
  * @param {PublicKeyCredential} credential
204
205
  * @param {string} [credentialId]
206
+ * @param {(() => void) | undefined} [onBeforeFollowUpGet]
205
207
  * @returns {Promise<PublicKeyCredential>}
206
208
  */
207
- async function credentialForKeyDerivation(credential, credentialId) {
209
+ async function credentialForKeyDerivation(
210
+ credential,
211
+ credentialId,
212
+ onBeforeFollowUpGet,
213
+ ) {
208
214
  debugLog("ceremony extension results", describePrfExtensionResults(credential));
209
215
 
210
216
  const rawPrf = credential.getClientExtensionResults()?.prf?.results?.first;
@@ -217,7 +223,11 @@ async function credentialForKeyDerivation(credential, credentialId) {
217
223
  debugLog("no PRF results on registration; running assertion with prf.eval", {
218
224
  credentialId: id,
219
225
  });
220
- // Same RPC — no second Confirm (one screen per call).
226
+ if (typeof onBeforeFollowUpGet === "function") {
227
+ onBeforeFollowUpGet();
228
+ }
229
+ // Prefer keeping the parent Confirm panel open (call from runOnConfirm) so
230
+ // the user still sees why this second WebAuthn prompt appears.
221
231
  const assertion = await getPasskeyAssertion(undefined, id);
222
232
  debugLog(
223
233
  "assertion extension results",
@@ -245,14 +255,31 @@ async function handleCreateCredential(params, correlationId, targetOrigin) {
245
255
  : {};
246
256
 
247
257
  await withCeremony(async () => {
248
- const credential = await promptCeremonyConfirm(options, () =>
249
- createPasskeyCredential(name, options),
250
- );
251
- const credentialId = getCredentialId(credential);
252
- const prfCredential = await credentialForKeyDerivation(
253
- credential,
254
- credentialId,
255
- );
258
+ // Create + optional PRF follow-up get stay under one Confirm panel so the
259
+ // explanation remains visible for every WebAuthn prompt in this RPC.
260
+ /** @type {PublicKeyCredential | undefined} */
261
+ let registrationCredential;
262
+ const prfCredential = await promptCeremonyConfirm(options, async () => {
263
+ registrationCredential = await createPasskeyCredential(name, options);
264
+ const credentialId = getCredentialId(registrationCredential);
265
+ return credentialForKeyDerivation(
266
+ registrationCredential,
267
+ credentialId,
268
+ () => {
269
+ updateCeremonyCopy({
270
+ explanationHeader: "Unlock your new passkey",
271
+ explanationText:
272
+ "Confirm with your passkey again to finish setting up this wallet.",
273
+ confirmButtonText: options.confirmButtonText,
274
+ denyButtonText: options.denyButtonText,
275
+ });
276
+ },
277
+ );
278
+ });
279
+ if (!registrationCredential) {
280
+ throw new Error("createCredential: registration credential missing");
281
+ }
282
+ const credentialId = getCredentialId(registrationCredential);
256
283
  const keys = await deriveKeysFromCredential(prfCredential);
257
284
  emitKeyDerived(
258
285
  targetOrigin,
@@ -263,7 +290,7 @@ async function handleCreateCredential(params, correlationId, targetOrigin) {
263
290
 
264
291
  emitEvent(window.parent, targetOrigin, "CredentialCreated", correlationId, {
265
292
  credentialId,
266
- cosePublicKey: getCosePublicKeyBase64Url(credential),
293
+ cosePublicKey: getCosePublicKeyBase64Url(registrationCredential),
267
294
  secp256k1PublicKey: to0xHex(keys.secp256k1PublicKey),
268
295
  });
269
296
  zeroize(keys.secp256k1PrivateKey);
package/src/ui.js CHANGED
@@ -131,6 +131,22 @@ export function resolveCeremonyUi(fields) {
131
131
  };
132
132
  }
133
133
 
134
+ /**
135
+ * Update header/body on an open ceremony Confirm panel (e.g. before a follow-up
136
+ * WebAuthn get). No-op if the ceremony UI is not showing.
137
+ *
138
+ * @param {CeremonyUiFields | Record<string, unknown> | undefined} fields
139
+ */
140
+ export function updateCeremonyCopy(fields) {
141
+ if (!root) return;
142
+ const header = root.querySelector(".ows-ceremony-header");
143
+ const text = root.querySelector(".ows-ceremony-text");
144
+ if (!header && !text) return;
145
+ const ui = resolveCeremonyUi(fields);
146
+ if (header) header.textContent = ui.header;
147
+ if (text) text.textContent = ui.explanation;
148
+ }
149
+
134
150
  /**
135
151
  * Show Confirm/Cancel. On Confirm, runs `runOnConfirm` **synchronously from the
136
152
  * click handler** so WebAuthn keeps user activation / document focus on mobile.