decane-connect-kit 2.12.0 → 2.13.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 +39 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +39 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -506,11 +506,13 @@ interface DecaneConnect {
|
|
|
506
506
|
off(event: SdkEvent, handler: (...args: unknown[]) => void): void;
|
|
507
507
|
}
|
|
508
508
|
interface WrappedShareMeta {
|
|
509
|
-
unlockMethod: 'passkey' | 'pin';
|
|
509
|
+
unlockMethod: 'passkey' | 'pin' | 'password';
|
|
510
510
|
credentialId?: string;
|
|
511
511
|
prfSalt?: string;
|
|
512
512
|
pinSalt?: string;
|
|
513
513
|
pinIterations?: number;
|
|
514
|
+
passwordSalt?: string;
|
|
515
|
+
passwordIterations?: number;
|
|
514
516
|
}
|
|
515
517
|
/**
|
|
516
518
|
* Who the user is, as the identity provider just described them.
|
|
@@ -755,6 +757,17 @@ interface SocialConfig {
|
|
|
755
757
|
*/
|
|
756
758
|
kingsChatScopes?: string[];
|
|
757
759
|
promptPin?: () => Promise<string>;
|
|
760
|
+
/**
|
|
761
|
+
* Called when the SDK needs the device unlock password — to choose one
|
|
762
|
+
* (`kind: 'set'`, ask twice) or to enter it (`kind: 'unlock'`).
|
|
763
|
+
*
|
|
764
|
+
* Required on any device using "unlock with your password". Unlike promptPin,
|
|
765
|
+
* this secret can open a session on its own, so never log or persist it and
|
|
766
|
+
* let a password manager fill it.
|
|
767
|
+
*/
|
|
768
|
+
promptUnlockPassword?: (ctx: {
|
|
769
|
+
kind: 'set' | 'unlock';
|
|
770
|
+
}) => Promise<string>;
|
|
758
771
|
apiBaseUrl?: string;
|
|
759
772
|
chains?: string[];
|
|
760
773
|
rpcUrls?: Record<string, string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -506,11 +506,13 @@ interface DecaneConnect {
|
|
|
506
506
|
off(event: SdkEvent, handler: (...args: unknown[]) => void): void;
|
|
507
507
|
}
|
|
508
508
|
interface WrappedShareMeta {
|
|
509
|
-
unlockMethod: 'passkey' | 'pin';
|
|
509
|
+
unlockMethod: 'passkey' | 'pin' | 'password';
|
|
510
510
|
credentialId?: string;
|
|
511
511
|
prfSalt?: string;
|
|
512
512
|
pinSalt?: string;
|
|
513
513
|
pinIterations?: number;
|
|
514
|
+
passwordSalt?: string;
|
|
515
|
+
passwordIterations?: number;
|
|
514
516
|
}
|
|
515
517
|
/**
|
|
516
518
|
* Who the user is, as the identity provider just described them.
|
|
@@ -755,6 +757,17 @@ interface SocialConfig {
|
|
|
755
757
|
*/
|
|
756
758
|
kingsChatScopes?: string[];
|
|
757
759
|
promptPin?: () => Promise<string>;
|
|
760
|
+
/**
|
|
761
|
+
* Called when the SDK needs the device unlock password — to choose one
|
|
762
|
+
* (`kind: 'set'`, ask twice) or to enter it (`kind: 'unlock'`).
|
|
763
|
+
*
|
|
764
|
+
* Required on any device using "unlock with your password". Unlike promptPin,
|
|
765
|
+
* this secret can open a session on its own, so never log or persist it and
|
|
766
|
+
* let a password manager fill it.
|
|
767
|
+
*/
|
|
768
|
+
promptUnlockPassword?: (ctx: {
|
|
769
|
+
kind: 'set' | 'unlock';
|
|
770
|
+
}) => Promise<string>;
|
|
758
771
|
apiBaseUrl?: string;
|
|
759
772
|
chains?: string[];
|
|
760
773
|
rpcUrls?: Record<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -2886,9 +2886,6 @@ async function deriveKeyFromPin(pin, salt) {
|
|
|
2886
2886
|
["encrypt", "decrypt"]
|
|
2887
2887
|
);
|
|
2888
2888
|
}
|
|
2889
|
-
function validatePin(pin) {
|
|
2890
|
-
if (!/^\d{4,8}$/.test(pin)) throw new Error("PIN must be 4\u20138 digits");
|
|
2891
|
-
}
|
|
2892
2889
|
|
|
2893
2890
|
// src/social/crypto/passkey.ts
|
|
2894
2891
|
init_errors();
|
|
@@ -3702,21 +3699,38 @@ async function createDecaneConnect(config) {
|
|
|
3702
3699
|
}
|
|
3703
3700
|
}
|
|
3704
3701
|
events.on("session-expired", () => dropLiveSession());
|
|
3705
|
-
async function
|
|
3706
|
-
if (!config.
|
|
3707
|
-
const
|
|
3708
|
-
|
|
3709
|
-
const
|
|
3710
|
-
const
|
|
3702
|
+
async function passwordSetup() {
|
|
3703
|
+
if (!config.promptUnlockPassword) throw new PromptPasswordNotConfiguredError();
|
|
3704
|
+
const password = await config.promptUnlockPassword({ kind: "set" });
|
|
3705
|
+
validateUnlockPassword(password);
|
|
3706
|
+
const passwordSalt = generateRandomBytes(32);
|
|
3707
|
+
const { shareKey, tokenKey } = await derivePasswordKeys(password, passwordSalt);
|
|
3711
3708
|
return {
|
|
3712
|
-
wrappingKey,
|
|
3709
|
+
wrappingKey: shareKey,
|
|
3710
|
+
tokenKey,
|
|
3713
3711
|
meta: {
|
|
3714
|
-
unlockMethod: "
|
|
3715
|
-
|
|
3716
|
-
|
|
3712
|
+
unlockMethod: "password",
|
|
3713
|
+
passwordSalt: bufferToB64u(passwordSalt),
|
|
3714
|
+
passwordIterations: PBKDF2_ITERATIONS
|
|
3717
3715
|
}
|
|
3718
3716
|
};
|
|
3719
3717
|
}
|
|
3718
|
+
async function attachUnlockToken(userId, jwt, tokenKey) {
|
|
3719
|
+
try {
|
|
3720
|
+
const stored = await storage.getShare(userId);
|
|
3721
|
+
if (!stored) return;
|
|
3722
|
+
const { token } = await api.issueUnlockToken(jwt);
|
|
3723
|
+
const unlockTokenIv = generateRandomBytes(12);
|
|
3724
|
+
const wrappedUnlockToken = await aesGcmEncrypt(
|
|
3725
|
+
tokenKey,
|
|
3726
|
+
new TextEncoder().encode(token),
|
|
3727
|
+
unlockTokenIv
|
|
3728
|
+
);
|
|
3729
|
+
await storage.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
|
|
3730
|
+
} catch (e) {
|
|
3731
|
+
console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3720
3734
|
async function persistRotatedDeviceShare(userId, deviceShare) {
|
|
3721
3735
|
const stored = await storage.getShare(userId);
|
|
3722
3736
|
if (!stored) {
|
|
@@ -3810,6 +3824,7 @@ async function createDecaneConnect(config) {
|
|
|
3810
3824
|
let unlockMethod;
|
|
3811
3825
|
let wrappingKey;
|
|
3812
3826
|
let meta = {};
|
|
3827
|
+
let unlockTokenKey = null;
|
|
3813
3828
|
if (isPasskeySupported()) {
|
|
3814
3829
|
const prfSalt = generateRandomBytes(32);
|
|
3815
3830
|
try {
|
|
@@ -3823,18 +3838,18 @@ async function createDecaneConnect(config) {
|
|
|
3823
3838
|
prfSalt: bufferToB64u(prfSalt)
|
|
3824
3839
|
};
|
|
3825
3840
|
} else {
|
|
3826
|
-
({ wrappingKey, meta } = await
|
|
3827
|
-
unlockMethod = "
|
|
3841
|
+
({ wrappingKey, meta, tokenKey: unlockTokenKey } = await passwordSetup());
|
|
3842
|
+
unlockMethod = "password";
|
|
3828
3843
|
}
|
|
3829
3844
|
} catch (e) {
|
|
3830
3845
|
if (e.name === "UserCancelledError")
|
|
3831
3846
|
throw new UserCancelledError();
|
|
3832
|
-
({ wrappingKey, meta } = await
|
|
3833
|
-
unlockMethod = "
|
|
3847
|
+
({ wrappingKey, meta, tokenKey: unlockTokenKey } = await passwordSetup());
|
|
3848
|
+
unlockMethod = "password";
|
|
3834
3849
|
}
|
|
3835
3850
|
} else {
|
|
3836
|
-
({ wrappingKey, meta } = await
|
|
3837
|
-
unlockMethod = "
|
|
3851
|
+
({ wrappingKey, meta, tokenKey: unlockTokenKey } = await passwordSetup());
|
|
3852
|
+
unlockMethod = "password";
|
|
3838
3853
|
}
|
|
3839
3854
|
const provisioningStartedAt = Date.now();
|
|
3840
3855
|
const remainingBudget = () => PROVISIONING_BUDGET_MS - (Date.now() - provisioningStartedAt);
|
|
@@ -3866,8 +3881,11 @@ async function createDecaneConnect(config) {
|
|
|
3866
3881
|
prfSalt: meta.prfSalt ? b64uToBuffer(meta.prfSalt) : void 0,
|
|
3867
3882
|
pinSalt: meta.pinSalt ? b64uToBuffer(meta.pinSalt) : void 0,
|
|
3868
3883
|
pinIterations: meta.pinIterations,
|
|
3884
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3885
|
+
passwordIterations: meta.passwordIterations,
|
|
3869
3886
|
version: 1
|
|
3870
3887
|
});
|
|
3888
|
+
if (unlockTokenKey) await attachUnlockToken(userId, jwt, unlockTokenKey);
|
|
3871
3889
|
const serverIv = generateRandomBytes(12);
|
|
3872
3890
|
await withDeadline(api.putShare(jwt, {
|
|
3873
3891
|
ciphertext: shareSet.server,
|
|
@@ -6943,6 +6961,7 @@ function DecaneKit({ config, children }) {
|
|
|
6943
6961
|
apiKey,
|
|
6944
6962
|
authMethods,
|
|
6945
6963
|
promptPin,
|
|
6964
|
+
promptUnlockPassword,
|
|
6946
6965
|
apiBaseUrl,
|
|
6947
6966
|
chains,
|
|
6948
6967
|
rpcUrls,
|
|
@@ -6965,6 +6984,7 @@ function DecaneKit({ config, children }) {
|
|
|
6965
6984
|
chains: chains ?? ["evm:1", "solana:mainnet"],
|
|
6966
6985
|
rpcUrls,
|
|
6967
6986
|
promptPin,
|
|
6987
|
+
promptUnlockPassword,
|
|
6968
6988
|
requireAssertionPerSignature,
|
|
6969
6989
|
offerRecoveryAtSignup,
|
|
6970
6990
|
onRecoveryShareOffer,
|