decane-connect-kit 2.17.0 → 2.19.0
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/index.cjs +48 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +48 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -592,7 +592,12 @@ declare class ApiClient {
|
|
|
592
592
|
metadata?: Record<string, unknown>;
|
|
593
593
|
}): Promise<TransactionRecord>;
|
|
594
594
|
getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
|
|
595
|
-
|
|
595
|
+
/**
|
|
596
|
+
* `label` is what the user's password manager will show for this passkey.
|
|
597
|
+
* Without one they see the account's opaque id. Sent, used, and forgotten —
|
|
598
|
+
* the backend echoes it into the options and stores nothing.
|
|
599
|
+
*/
|
|
600
|
+
getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string, label?: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
|
|
596
601
|
postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
|
|
597
602
|
/**
|
|
598
603
|
* Exchange a token from the developer's own auth system for a Decane
|
package/dist/index.d.ts
CHANGED
|
@@ -592,7 +592,12 @@ declare class ApiClient {
|
|
|
592
592
|
metadata?: Record<string, unknown>;
|
|
593
593
|
}): Promise<TransactionRecord>;
|
|
594
594
|
getTransactions(jwt: string, limit?: number): Promise<TransactionRecord[]>;
|
|
595
|
-
|
|
595
|
+
/**
|
|
596
|
+
* `label` is what the user's password manager will show for this passkey.
|
|
597
|
+
* Without one they see the account's opaque id. Sent, used, and forgotten —
|
|
598
|
+
* the backend echoes it into the options and stores nothing.
|
|
599
|
+
*/
|
|
600
|
+
getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string, label?: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
|
|
596
601
|
postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
|
|
597
602
|
/**
|
|
598
603
|
* Exchange a token from the developer's own auth system for a Decane
|
package/dist/index.js
CHANGED
|
@@ -2085,8 +2085,14 @@ var ApiClient = class {
|
|
|
2085
2085
|
return body.transactions;
|
|
2086
2086
|
}
|
|
2087
2087
|
// ── WebAuthn registration (additive to the PRF-only passkey flow) ─────────
|
|
2088
|
-
|
|
2089
|
-
|
|
2088
|
+
/**
|
|
2089
|
+
* `label` is what the user's password manager will show for this passkey.
|
|
2090
|
+
* Without one they see the account's opaque id. Sent, used, and forgotten —
|
|
2091
|
+
* the backend echoes it into the options and stores nothing.
|
|
2092
|
+
*/
|
|
2093
|
+
async getWebAuthnRegisterOptions(jwt, rpId, rpName, label) {
|
|
2094
|
+
const query = `rpId=${encodeURIComponent(rpId)}&rpName=${encodeURIComponent(rpName)}` + (label ? `&label=${encodeURIComponent(label)}` : "");
|
|
2095
|
+
return this._request(`/webauthn/register-options?${query}`, {
|
|
2090
2096
|
headers: { Authorization: `Bearer ${jwt}` }
|
|
2091
2097
|
});
|
|
2092
2098
|
}
|
|
@@ -2970,7 +2976,13 @@ function bufFromBytes(u) {
|
|
|
2970
2976
|
return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
|
|
2971
2977
|
}
|
|
2972
2978
|
async function registerWebAuthnCredential(opts) {
|
|
2973
|
-
const baseOptions = await opts.api.getWebAuthnRegisterOptions(
|
|
2979
|
+
const baseOptions = await opts.api.getWebAuthnRegisterOptions(
|
|
2980
|
+
opts.jwt,
|
|
2981
|
+
opts.rpId,
|
|
2982
|
+
opts.rpName,
|
|
2983
|
+
opts.label
|
|
2984
|
+
);
|
|
2985
|
+
if (opts.allowDuplicate) delete baseOptions.excludeCredentials;
|
|
2974
2986
|
const optionsJSON = {
|
|
2975
2987
|
...baseOptions,
|
|
2976
2988
|
extensions: {
|
|
@@ -2979,11 +2991,7 @@ async function registerWebAuthnCredential(opts) {
|
|
|
2979
2991
|
}
|
|
2980
2992
|
};
|
|
2981
2993
|
const { credentialId, prfOutput, response } = await registerPasskey({ optionsJSON });
|
|
2982
|
-
|
|
2983
|
-
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
2984
|
-
} catch (e) {
|
|
2985
|
-
console.warn("decane-connect-kit: WebAuthn server-side registration failed; PRF-based unlock still works, but rotateShares()/exportPortableBackup() will not until this is retried.", e);
|
|
2986
|
-
}
|
|
2994
|
+
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
2987
2995
|
return { credentialId, prfOutput };
|
|
2988
2996
|
}
|
|
2989
2997
|
|
|
@@ -3773,21 +3781,24 @@ async function createDecaneConnect(config) {
|
|
|
3773
3781
|
const pin = await config.promptPin();
|
|
3774
3782
|
return deriveKeyFromPin(pin, stored.pinSalt);
|
|
3775
3783
|
}
|
|
3776
|
-
async function registerPasskeyForDevice(userId, jwt, deviceShare) {
|
|
3784
|
+
async function registerPasskeyForDevice(userId, jwt, deviceShare, opts = {}) {
|
|
3777
3785
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
3778
3786
|
const prfSalt = generateRandomBytes(32);
|
|
3779
3787
|
let credentialId;
|
|
3780
3788
|
let prfOutput;
|
|
3781
3789
|
try {
|
|
3790
|
+
const label = (await storage.getShare(userId))?.profile?.email;
|
|
3782
3791
|
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
3783
3792
|
api,
|
|
3784
3793
|
jwt,
|
|
3785
3794
|
rpId,
|
|
3786
3795
|
rpName,
|
|
3787
|
-
prfSalt
|
|
3796
|
+
prfSalt,
|
|
3797
|
+
...label ? { label } : {},
|
|
3798
|
+
...opts.allowDuplicate ? { allowDuplicate: true } : {}
|
|
3788
3799
|
}));
|
|
3789
3800
|
} catch (e) {
|
|
3790
|
-
if (e?.name !== "InvalidStateError") throw e;
|
|
3801
|
+
if (e?.name !== "InvalidStateError" || opts.allowDuplicate) throw e;
|
|
3791
3802
|
return adoptExistingPasskey(userId, jwt, deviceShare);
|
|
3792
3803
|
}
|
|
3793
3804
|
let key = prfOutput;
|
|
@@ -3839,7 +3850,12 @@ async function createDecaneConnect(config) {
|
|
|
3839
3850
|
}
|
|
3840
3851
|
async function adoptExistingPasskey(userId, jwt, deviceShare) {
|
|
3841
3852
|
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3842
|
-
if (!options)
|
|
3853
|
+
if (!options) {
|
|
3854
|
+
console.warn(
|
|
3855
|
+
"decane-connect-kit: this authenticator holds a credential the server does not know; registering a fresh one."
|
|
3856
|
+
);
|
|
3857
|
+
return registerPasskeyForDevice(userId, jwt, deviceShare, { allowDuplicate: true });
|
|
3858
|
+
}
|
|
3843
3859
|
const prfSalt = generateRandomBytes(32);
|
|
3844
3860
|
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3845
3861
|
optionsJSON: options,
|
|
@@ -3884,19 +3900,26 @@ async function createDecaneConnect(config) {
|
|
|
3884
3900
|
);
|
|
3885
3901
|
return;
|
|
3886
3902
|
}
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3903
|
+
try {
|
|
3904
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3905
|
+
const iv = generateRandomBytes(12);
|
|
3906
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3907
|
+
await storage.putShare({
|
|
3908
|
+
userId,
|
|
3909
|
+
wrappedDeviceShare,
|
|
3910
|
+
iv,
|
|
3911
|
+
unlockMethod: "password",
|
|
3912
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3913
|
+
passwordIterations: meta.passwordIterations,
|
|
3914
|
+
version: 1
|
|
3915
|
+
});
|
|
3916
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3917
|
+
} catch (e) {
|
|
3918
|
+
console.warn(
|
|
3919
|
+
"decane-connect-kit: no unlock password was set, so this device stored no share. The session works; the next visit will provision again.",
|
|
3920
|
+
e
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3900
3923
|
}
|
|
3901
3924
|
async function firstTimeSetup(userId, jwt) {
|
|
3902
3925
|
prewarmAttestation(teeUrl);
|