@calimero-network/mero-js 29.7.5 → 29.7.7
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/dist/cloud-v2/client.d.ts +20 -1
- package/dist/cloud-v2/client.js +48 -19
- package/dist/cloud-v2/index.d.ts +1 -1
- package/dist/cloud-v2/passkey.d.ts +25 -7
- package/dist/cloud-v2/passkey.js +16 -5
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +49 -20
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +49 -20
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
|
@@ -408,6 +408,13 @@ export interface ServedOrigins {
|
|
|
408
408
|
*/
|
|
409
409
|
export interface SignInOptions {
|
|
410
410
|
origin?: string | null;
|
|
411
|
+
/**
|
|
412
|
+
* Closes an open passkey prompt when it aborts (a Cancel button): the
|
|
413
|
+
* ceremony then rejects as a dismissed prompt does (`NotAllowedError`; in a
|
|
414
|
+
* sign-up's confirmation, {@link ConfirmCancelled}). Given to the
|
|
415
|
+
* {@link PasskeyProvider} as `signal`.
|
|
416
|
+
*/
|
|
417
|
+
signal?: AbortSignal;
|
|
411
418
|
}
|
|
412
419
|
/**
|
|
413
420
|
* {@link SignInOptions} for a sign-up, and the invite it is made with: an
|
|
@@ -648,9 +655,21 @@ export declare class CalimeroClient {
|
|
|
648
655
|
*/
|
|
649
656
|
refresh(): Promise<CalimeroSession>;
|
|
650
657
|
private exchangeRefresh;
|
|
658
|
+
/**
|
|
659
|
+
* End this session. The client is signed out whether or not the node
|
|
660
|
+
* answers: the revoke (`POST /auth/logout`) is attempted, and its failure
|
|
661
|
+
* (the node unreachable, say) is thrown after the session is dropped, so a
|
|
662
|
+
* caller can say the server may still hold it, but no request after this
|
|
663
|
+
* one carries its token.
|
|
664
|
+
*/
|
|
651
665
|
signOut(): Promise<void>;
|
|
652
|
-
/**
|
|
666
|
+
/**
|
|
667
|
+
* End every session of the signed-in account, on every device, this one
|
|
668
|
+
* too. Like {@link signOut}, this client is signed out even when the
|
|
669
|
+
* request fails, and the failure is thrown.
|
|
670
|
+
*/
|
|
653
671
|
signOutEverywhere(): Promise<void>;
|
|
672
|
+
private signingOut;
|
|
654
673
|
/** Who the credential is: the account, how it signed in, and the package (`app`) and `origin` a session is bound to. */
|
|
655
674
|
me(): Promise<MeInfo>;
|
|
656
675
|
/**
|
package/dist/cloud-v2/client.js
CHANGED
|
@@ -57,11 +57,13 @@ export function pageDeployment(href) {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* The user verification a confirmation asks the passkey for: what the node's
|
|
60
|
-
* `412` details name, if it names one WebAuthn knows, else `
|
|
61
|
-
*
|
|
60
|
+
* `412` details name, if it names one WebAuthn knows, else `fallback` (the
|
|
61
|
+
* flow's own requirement: `required` for a step-up, which the node takes only
|
|
62
|
+
* with the user verified; `preferred` for the sign-up confirmation, which it
|
|
63
|
+
* takes with the user present).
|
|
62
64
|
*/
|
|
63
|
-
function userVerificationOf(named) {
|
|
64
|
-
return named === 'required' || named === 'preferred' || named === 'discouraged' ? named :
|
|
65
|
+
function userVerificationOf(named, fallback) {
|
|
66
|
+
return named === 'required' || named === 'preferred' || named === 'discouraged' ? named : fallback;
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
69
|
* What a failed confirmation prompt means to the caller. The browser or a
|
|
@@ -509,8 +511,14 @@ export class CalimeroClient {
|
|
|
509
511
|
const origin = this.pageOrigin(options);
|
|
510
512
|
const invite = options.invite ? { invite: options.invite } : {};
|
|
511
513
|
const begin = await this.begin('auth/passkey/register/begin', origin === undefined ? { name, ...invite } : { name, origin, ...invite }, origin);
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
+
const signal = options.signal;
|
|
515
|
+
const registration = await (signal ? passkey.create(begin.options, { signal }) : passkey.create(begin.options));
|
|
516
|
+
// The node checks the sign-up's confirmation with the user present, not
|
|
517
|
+
// verified (core `api.rs`: `rp(node)`, `require_uv: false`), as it made
|
|
518
|
+
// the passkey with `preferred`: asking `required` here would refuse a
|
|
519
|
+
// security key without a PIN after it has made the passkey, leaving a
|
|
520
|
+
// passkey with no account.
|
|
521
|
+
const session = await this.confirmed('POST', 'auth/passkey/register/finish', { challengeId: begin.challengeId, registration, ...invite }, passkey, undefined, 'preferred', signal);
|
|
514
522
|
this.useSession(session);
|
|
515
523
|
return session;
|
|
516
524
|
}
|
|
@@ -530,7 +538,10 @@ export class CalimeroClient {
|
|
|
530
538
|
const origin = this.pageOrigin(options);
|
|
531
539
|
const begin = await this.begin('auth/passkey/login/begin', origin === undefined ? undefined : { origin }, origin);
|
|
532
540
|
const rpId = begin.options.rpId ?? begin.options.rp?.id ?? '';
|
|
533
|
-
const
|
|
541
|
+
const challenge = fromB64url(begin.options.challenge);
|
|
542
|
+
const assertion = await (options.signal
|
|
543
|
+
? passkey.get(challenge, rpId, undefined, { signal: options.signal })
|
|
544
|
+
: passkey.get(challenge, rpId));
|
|
534
545
|
const session = await this.post('auth/passkey/login/finish', {
|
|
535
546
|
challengeId: begin.challengeId,
|
|
536
547
|
assertion,
|
|
@@ -610,16 +621,32 @@ export class CalimeroClient {
|
|
|
610
621
|
this.useSession(session);
|
|
611
622
|
return session;
|
|
612
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* End this session. The client is signed out whether or not the node
|
|
626
|
+
* answers: the revoke (`POST /auth/logout`) is attempted, and its failure
|
|
627
|
+
* (the node unreachable, say) is thrown after the session is dropped, so a
|
|
628
|
+
* caller can say the server may still hold it, but no request after this
|
|
629
|
+
* one carries its token.
|
|
630
|
+
*/
|
|
613
631
|
async signOut() {
|
|
614
|
-
await this.
|
|
615
|
-
this.auth = undefined;
|
|
616
|
-
this.current = undefined;
|
|
632
|
+
await this.signingOut('auth/logout');
|
|
617
633
|
}
|
|
618
|
-
/**
|
|
634
|
+
/**
|
|
635
|
+
* End every session of the signed-in account, on every device, this one
|
|
636
|
+
* too. Like {@link signOut}, this client is signed out even when the
|
|
637
|
+
* request fails, and the failure is thrown.
|
|
638
|
+
*/
|
|
619
639
|
async signOutEverywhere() {
|
|
620
|
-
await this.
|
|
621
|
-
|
|
622
|
-
|
|
640
|
+
await this.signingOut('auth/logout-all');
|
|
641
|
+
}
|
|
642
|
+
async signingOut(path) {
|
|
643
|
+
try {
|
|
644
|
+
await this.post(path);
|
|
645
|
+
}
|
|
646
|
+
finally {
|
|
647
|
+
this.auth = undefined;
|
|
648
|
+
this.current = undefined;
|
|
649
|
+
}
|
|
623
650
|
}
|
|
624
651
|
/** Who the credential is: the account, how it signed in, and the package (`app`) and `origin` a session is bound to. */
|
|
625
652
|
me() {
|
|
@@ -908,7 +935,7 @@ export class CalimeroClient {
|
|
|
908
935
|
deleteConfirmed(path, passkey, body = {}) {
|
|
909
936
|
return this.confirmed('DELETE', path, body, passkey);
|
|
910
937
|
}
|
|
911
|
-
async confirmed(method, path, body, passkey, send = (b) => this.json(method, path, b)) {
|
|
938
|
+
async confirmed(method, path, body, passkey, send = (b) => this.json(method, path, b), userVerification = 'required', signal) {
|
|
912
939
|
try {
|
|
913
940
|
return await send(body);
|
|
914
941
|
}
|
|
@@ -918,11 +945,13 @@ export class CalimeroClient {
|
|
|
918
945
|
const d = error.details;
|
|
919
946
|
let assertion;
|
|
920
947
|
try {
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
948
|
+
// A step-up is taken only with the user verified (PIN or biometric);
|
|
949
|
+
// `preferred` lets a password manager skip it, and the node then
|
|
950
|
+
// refuses. Only the sign-up confirmation passes `preferred`. A
|
|
951
|
+
// requirement the node names itself wins.
|
|
924
952
|
assertion = await passkey.get(fromB64url(d.challenge), d.rpId, d.allowCredentials, {
|
|
925
|
-
userVerification: userVerificationOf(d.userVerification),
|
|
953
|
+
userVerification: userVerificationOf(d.userVerification, userVerification),
|
|
954
|
+
...(signal ? { signal } : {}),
|
|
926
955
|
});
|
|
927
956
|
}
|
|
928
957
|
catch (declined) {
|
package/dist/cloud-v2/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { claimedRelease, fetchAttestedKey, freshCollateral, MOCK_QUOTE_HEADER }
|
|
|
8
8
|
export type { AttestationDoc, CloudPolicy, CloudReleasePolicy } from './attestation.js';
|
|
9
9
|
export { browserPasskey, b64url, fromB64url } from './passkey.js';
|
|
10
10
|
export { MAX_RECOVERY_NONCE_BYTES, RECOVERY_DOMAIN, newRecoverySeed, parseRecoverySeed, recoveryMessage, recoveryPublicKey, recoverySeedText, signRecovery, } from './recovery.js';
|
|
11
|
-
export type { AssertionJSON, PasskeyGetOptions, PasskeyProvider, PasskeyUserVerification, RegistrationJSON, } from './passkey.js';
|
|
11
|
+
export type { AssertionJSON, PasskeyCreateOptions, PasskeyGetOptions, PasskeyProvider, PasskeyUserVerification, RegistrationJSON, } from './passkey.js';
|
|
12
12
|
export { MAX_REPORT_PAGE, RELEASE_VERIFIER, compareVersions, confirmationCode, consentReturn, pageQuery, pendingApprovalsFrom, publisherBindingBytes, publisherBindingMessage, releaseConsent, releaseNotesUrl, stalledBy, upgradeConsent, verifyReleaseUrl, withdrawConsent, } from './approvals.js';
|
|
13
13
|
export type { ActorKind, AppsReport, Authority, ClusterRelease, ConsentReleaseModel, ConsentRequest, ConsentResult, ConsentScope, ConsentStarted, ConsentStatus, OpenConsentOptions, Governance, LastUpgrade, OpsAppsReport, OpsOrgsReport, PageOptions, PendingApprovals, PendingRelease, PendingUpgrade, PublisherBindingInfo, PublisherProof, ReleaseDeadline, ReleaseRules, ReportScope, ReportedApp, ReportedBundle, ReportedRelease, ReportedReplica, UpgradeAuthority, } from './approvals.js';
|
|
14
14
|
export { DEFAULT_ORG_CAPABILITIES, ORG_CAPABILITIES, orgCapabilities } from './capabilities.js';
|
|
@@ -19,21 +19,39 @@ export type PasskeyUserVerification = "required" | "preferred" | "discouraged";
|
|
|
19
19
|
/** What a confirmation asks of the passkey beyond the challenge. */
|
|
20
20
|
export interface PasskeyGetOptions {
|
|
21
21
|
/**
|
|
22
|
-
* `"required"` for a confirmation (`412 CONFIRM_REQUIRED`): the
|
|
23
|
-
* accepts only an assertion with the user-verified flag (PIN or
|
|
24
|
-
* biometric).
|
|
22
|
+
* `"required"` for a step-up confirmation (`412 CONFIRM_REQUIRED`): the
|
|
23
|
+
* node accepts only an assertion with the user-verified flag (PIN or
|
|
24
|
+
* biometric). `"preferred"` for the sign-up's confirmation, which the node
|
|
25
|
+
* takes with the user present. Absent for a plain sign-in, where
|
|
26
|
+
* `"preferred"` is enough.
|
|
25
27
|
*/
|
|
26
28
|
userVerification?: PasskeyUserVerification;
|
|
29
|
+
/**
|
|
30
|
+
* Closes the open prompt when it aborts (a Cancel button, a page leaving).
|
|
31
|
+
* The browser provider then rejects as a dismissed prompt does
|
|
32
|
+
* (`NotAllowedError`), so the caller treats it as a cancel.
|
|
33
|
+
*/
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
/** What a passkey creation takes beyond the node's options. */
|
|
37
|
+
export interface PasskeyCreateOptions {
|
|
38
|
+
/** As {@link PasskeyGetOptions.signal}: aborting closes the prompt, rejecting as a cancel (`NotAllowedError`). */
|
|
39
|
+
signal?: AbortSignal;
|
|
27
40
|
}
|
|
28
41
|
/** Something that holds a passkey. */
|
|
29
42
|
export interface PasskeyProvider {
|
|
30
|
-
/**
|
|
31
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Answer `navigator.credentials.create` for the node's options.
|
|
45
|
+
* `createOptions.signal` closes the prompt when it aborts; a provider with
|
|
46
|
+
* no prompt to close may ignore it.
|
|
47
|
+
*/
|
|
48
|
+
create(options: Record<string, unknown>, createOptions?: PasskeyCreateOptions): Promise<RegistrationJSON>;
|
|
32
49
|
/**
|
|
33
50
|
* Sign `challenge` (an assertion), optionally limited to
|
|
34
51
|
* `allowCredentials`. `options.userVerification` is `"required"` when the
|
|
35
|
-
* client answers a confirmation
|
|
36
|
-
* may ignore it.
|
|
52
|
+
* client answers a step-up confirmation (`"preferred"` for the sign-up's);
|
|
53
|
+
* a provider that always verifies the user may ignore it.
|
|
54
|
+
* `options.signal` closes the prompt when it aborts.
|
|
37
55
|
*/
|
|
38
56
|
get(challenge: Uint8Array, rpId: string, allowCredentials?: string[], options?: PasskeyGetOptions): Promise<AssertionJSON>;
|
|
39
57
|
}
|
package/dist/cloud-v2/passkey.js
CHANGED
|
@@ -25,11 +25,22 @@ function cancelled(message) {
|
|
|
25
25
|
error.name = "NotAllowedError";
|
|
26
26
|
return error;
|
|
27
27
|
}
|
|
28
|
-
async function ask(method, options) {
|
|
28
|
+
async function ask(method, options, signal, what) {
|
|
29
29
|
const container = navigator.credentials;
|
|
30
30
|
const own = globalThis.CredentialsContainer?.prototype?.[method];
|
|
31
31
|
const call = own ?? container[method];
|
|
32
|
-
|
|
32
|
+
try {
|
|
33
|
+
return await call.call(container, signal ? { ...options, signal } : options);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
// An aborted `signal` rejects with its reason (an `AbortError` unless the
|
|
37
|
+
// caller gave another): the prompt was closed on purpose, so it reads as
|
|
38
|
+
// a dismissed one.
|
|
39
|
+
if (signal?.aborted || error?.name === "AbortError") {
|
|
40
|
+
throw Object.assign(cancelled(`${what} was cancelled`), { cause: error });
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
33
44
|
}
|
|
34
45
|
/**
|
|
35
46
|
* The node's algorithms with ES256 alone when it is among them. Some password
|
|
@@ -48,7 +59,7 @@ export function es256First(params) {
|
|
|
48
59
|
/** The platform authenticator (Touch ID, Face ID, Windows Hello, a security key, a synced passkey). */
|
|
49
60
|
export function browserPasskey() {
|
|
50
61
|
return {
|
|
51
|
-
async create(options) {
|
|
62
|
+
async create(options, createOptions) {
|
|
52
63
|
const o = options;
|
|
53
64
|
const cred = (await ask("create", {
|
|
54
65
|
publicKey: {
|
|
@@ -64,7 +75,7 @@ export function browserPasskey() {
|
|
|
64
75
|
attestation: "none",
|
|
65
76
|
timeout: o.timeout,
|
|
66
77
|
},
|
|
67
|
-
}));
|
|
78
|
+
}, createOptions?.signal, "passkey creation"));
|
|
68
79
|
if (!cred)
|
|
69
80
|
throw cancelled("passkey creation was cancelled");
|
|
70
81
|
const r = cred.response;
|
|
@@ -84,7 +95,7 @@ export function browserPasskey() {
|
|
|
84
95
|
id: buf(fromB64url(id)),
|
|
85
96
|
})),
|
|
86
97
|
},
|
|
87
|
-
}));
|
|
98
|
+
}, options?.signal, "passkey sign-in"));
|
|
88
99
|
if (!cred)
|
|
89
100
|
throw cancelled("passkey sign-in was cancelled");
|
|
90
101
|
const r = cred.response;
|