@1shotapi/ows-signer 0.3.2 → 0.3.4
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/README.md +1 -0
- package/package.json +1 -1
- package/src/handlers.js +56 -12
- package/src/ui.js +16 -0
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ Ceremony UI params (optional on WebAuthn-triggering methods): `explanationHeader
|
|
|
58
58
|
- `rpName` — relying party display name in passkey UI (default `"OWS"`); does not affect `rpId`
|
|
59
59
|
- `userDisplayName` — friendly account label (defaults to `name`)
|
|
60
60
|
- `userId` — stable user handle bytes (base64); random if omitted
|
|
61
|
+
- `deferKeyDerivation` — when true, skip the PRF follow-up get; `CredentialCreated` includes `credentialId` + `cosePublicKey` only (no `KeyDerived` / no `secp256k1PublicKey`)
|
|
61
62
|
|
|
62
63
|
## Signing schemes
|
|
63
64
|
|
package/package.json
CHANGED
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(
|
|
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
|
-
|
|
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",
|
|
@@ -239,20 +249,54 @@ async function handleCreateCredential(params, correlationId, targetOrigin) {
|
|
|
239
249
|
}
|
|
240
250
|
const options =
|
|
241
251
|
params.options && typeof params.options === "object"
|
|
242
|
-
? /** @type {{ rpName?: string, userDisplayName?: string, userId?: string, explanationHeader?: string, explanationText?: string, confirmButtonText?: string, denyButtonText?: string }} */ (
|
|
252
|
+
? /** @type {{ rpName?: string, userDisplayName?: string, userId?: string, deferKeyDerivation?: boolean, explanationHeader?: string, explanationText?: string, confirmButtonText?: string, denyButtonText?: string }} */ (
|
|
243
253
|
params.options
|
|
244
254
|
)
|
|
245
255
|
: {};
|
|
256
|
+
const deferKeyDerivation = options.deferKeyDerivation === true;
|
|
246
257
|
|
|
247
258
|
await withCeremony(async () => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
/** @type {PublicKeyCredential | undefined} */
|
|
260
|
+
let registrationCredential;
|
|
261
|
+
|
|
262
|
+
if (deferKeyDerivation) {
|
|
263
|
+
// Registration only — opener (or another signer session) will unlock later.
|
|
264
|
+
await promptCeremonyConfirm(options, async () => {
|
|
265
|
+
registrationCredential = await createPasskeyCredential(name, options);
|
|
266
|
+
});
|
|
267
|
+
if (!registrationCredential) {
|
|
268
|
+
throw new Error("createCredential: registration credential missing");
|
|
269
|
+
}
|
|
270
|
+
emitEvent(window.parent, targetOrigin, "CredentialCreated", correlationId, {
|
|
271
|
+
credentialId: getCredentialId(registrationCredential),
|
|
272
|
+
cosePublicKey: getCosePublicKeyBase64Url(registrationCredential),
|
|
273
|
+
});
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Create + optional PRF follow-up get stay under one Confirm panel so the
|
|
278
|
+
// explanation remains visible for every WebAuthn prompt in this RPC.
|
|
279
|
+
const prfCredential = await promptCeremonyConfirm(options, async () => {
|
|
280
|
+
registrationCredential = await createPasskeyCredential(name, options);
|
|
281
|
+
const credentialId = getCredentialId(registrationCredential);
|
|
282
|
+
return credentialForKeyDerivation(
|
|
283
|
+
registrationCredential,
|
|
284
|
+
credentialId,
|
|
285
|
+
() => {
|
|
286
|
+
updateCeremonyCopy({
|
|
287
|
+
explanationHeader: "Unlock your new passkey",
|
|
288
|
+
explanationText:
|
|
289
|
+
"Confirm with your passkey again to finish setting up this wallet.",
|
|
290
|
+
confirmButtonText: options.confirmButtonText,
|
|
291
|
+
denyButtonText: options.denyButtonText,
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
if (!registrationCredential) {
|
|
297
|
+
throw new Error("createCredential: registration credential missing");
|
|
298
|
+
}
|
|
299
|
+
const credentialId = getCredentialId(registrationCredential);
|
|
256
300
|
const keys = await deriveKeysFromCredential(prfCredential);
|
|
257
301
|
emitKeyDerived(
|
|
258
302
|
targetOrigin,
|
|
@@ -263,7 +307,7 @@ async function handleCreateCredential(params, correlationId, targetOrigin) {
|
|
|
263
307
|
|
|
264
308
|
emitEvent(window.parent, targetOrigin, "CredentialCreated", correlationId, {
|
|
265
309
|
credentialId,
|
|
266
|
-
cosePublicKey: getCosePublicKeyBase64Url(
|
|
310
|
+
cosePublicKey: getCosePublicKeyBase64Url(registrationCredential),
|
|
267
311
|
secp256k1PublicKey: to0xHex(keys.secp256k1PublicKey),
|
|
268
312
|
});
|
|
269
313
|
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.
|