@textrp/briij-js-sdk 43.2.1 → 44.0.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/CHANGELOG.md +7 -1
- package/README.md +18 -0
- package/lib/@types/auth.d.ts +38 -0
- package/lib/@types/auth.d.ts.map +1 -1
- package/lib/@types/auth.js.map +1 -1
- package/lib/@types/snarkjs.d.js +0 -0
- package/lib/@types/snarkjs.d.js.map +1 -0
- package/lib/auth/credential.d.ts +25 -0
- package/lib/auth/credential.d.ts.map +1 -0
- package/lib/auth/credential.js +48 -0
- package/lib/auth/credential.js.map +1 -0
- package/lib/auth/did.d.ts +22 -0
- package/lib/auth/did.d.ts.map +1 -0
- package/lib/auth/did.js +66 -0
- package/lib/auth/did.js.map +1 -0
- package/lib/auth/wallet.d.ts +22 -0
- package/lib/auth/wallet.d.ts.map +1 -0
- package/lib/auth/wallet.js +27 -0
- package/lib/auth/wallet.js.map +1 -0
- package/lib/auth/zkpE2EE.d.ts +22 -0
- package/lib/auth/zkpE2EE.d.ts.map +1 -0
- package/lib/auth/zkpE2EE.js +69 -0
- package/lib/auth/zkpE2EE.js.map +1 -0
- package/lib/briij.d.ts +4 -0
- package/lib/briij.d.ts.map +1 -1
- package/lib/briij.js +4 -0
- package/lib/briij.js.map +1 -1
- package/lib/client.d.ts +40 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +259 -113
- package/lib/client.js.map +1 -1
- package/package.json +3 -1
- package/src/@types/auth.ts +44 -0
- package/src/@types/snarkjs.d.ts +17 -0
- package/src/auth/credential.ts +63 -0
- package/src/auth/did.ts +89 -0
- package/src/auth/wallet.ts +50 -0
- package/src/auth/zkpE2EE.ts +88 -0
- package/src/briij.ts +12 -0
- package/src/client.ts +187 -0
- package/src/components/LoginStepper.tsx +50 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@textrp/briij-js-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "44.0.0",
|
|
4
4
|
"description": "Briij Client-Server SDK for JavaScript and TypeScript",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"another-json": "^0.2.0",
|
|
40
40
|
"bs58": "^6.0.0",
|
|
41
41
|
"content-type": "^1.0.4",
|
|
42
|
+
"did-resolver": "^4.1.0",
|
|
42
43
|
"jwt-decode": "^4.0.0",
|
|
43
44
|
"loglevel": "^1.9.2",
|
|
44
45
|
"matrix-events-sdk": "0.0.1",
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"oidc-client-ts": "^3.0.1",
|
|
47
48
|
"p-retry": "7",
|
|
48
49
|
"sdp-transform": "^3.0.0",
|
|
50
|
+
"snarkjs": "^0.7.6",
|
|
49
51
|
"unhomoglyph": "^1.0.6",
|
|
50
52
|
"uuid": "13",
|
|
51
53
|
"xrpl": "^4.6.0"
|
package/src/@types/auth.ts
CHANGED
|
@@ -270,6 +270,50 @@ export interface WalletIdentityAccountData {
|
|
|
270
270
|
public_key?: string | null;
|
|
271
271
|
network?: string | null;
|
|
272
272
|
key_type?: string | null;
|
|
273
|
+
did_uri?: string | null;
|
|
274
|
+
credential_id?: string | null;
|
|
275
|
+
e2ee_pubkey_commitment?: string | null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
interface DidVerificationMethod {
|
|
279
|
+
id: string;
|
|
280
|
+
type: string;
|
|
281
|
+
controller: string;
|
|
282
|
+
publicKeyMultibase?: string;
|
|
283
|
+
blockchainAccountId?: string;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface DidResolutionResult {
|
|
287
|
+
did_uri: string;
|
|
288
|
+
resolution_type?: string;
|
|
289
|
+
did_document: {
|
|
290
|
+
id: string;
|
|
291
|
+
verificationMethod: DidVerificationMethod[];
|
|
292
|
+
authentication: string[];
|
|
293
|
+
service?: Array<Record<string, string>>;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface CredentialCreateResult {
|
|
298
|
+
credential_id: string;
|
|
299
|
+
status: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface CredentialVerifyResult {
|
|
303
|
+
credential_id: string;
|
|
304
|
+
valid: boolean;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface ZkpVerifyResult {
|
|
308
|
+
valid: boolean;
|
|
309
|
+
verified_at?: number;
|
|
310
|
+
reason?: string;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface DidCredentialMetadata {
|
|
314
|
+
didUri: string;
|
|
315
|
+
credentialId: string;
|
|
316
|
+
issuedAt: number;
|
|
273
317
|
}
|
|
274
318
|
|
|
275
319
|
export interface XrplWalletLoginRequest extends Omit<LoginRequest, "type"> {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare module "snarkjs" {
|
|
2
|
+
export const groth16: {
|
|
3
|
+
fullProve: (
|
|
4
|
+
input: Record<string, string>,
|
|
5
|
+
wasmPath: string,
|
|
6
|
+
zkeyPath: string,
|
|
7
|
+
) => Promise<{
|
|
8
|
+
proof: Record<string, unknown>;
|
|
9
|
+
publicSignals: string[] | Record<string, string>;
|
|
10
|
+
}>;
|
|
11
|
+
verify: (
|
|
12
|
+
verificationKey: Record<string, unknown>,
|
|
13
|
+
publicSignals: string[] | Record<string, string>,
|
|
14
|
+
proof: Record<string, unknown>,
|
|
15
|
+
) => Promise<boolean>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 Xurge Digital Lab
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface CredentialCreateRequest {
|
|
12
|
+
did_uri: string;
|
|
13
|
+
subject: string;
|
|
14
|
+
e2ee_pubkey_commitment: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CredentialCreateResponse {
|
|
18
|
+
credential_id: string;
|
|
19
|
+
status: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CredentialVerifyResponse {
|
|
23
|
+
credential_id: string;
|
|
24
|
+
valid: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DidCredentialMetadata {
|
|
28
|
+
didUri: string;
|
|
29
|
+
credentialId: string;
|
|
30
|
+
issuedAt: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const METADATA_KEY = "briij.did_credential_metadata";
|
|
34
|
+
const memoryMetadata = new Map<string, DidCredentialMetadata>();
|
|
35
|
+
|
|
36
|
+
export async function requestCredentialCreate(
|
|
37
|
+
requester: (path: string, method: "POST", body: CredentialCreateRequest) => Promise<CredentialCreateResponse>,
|
|
38
|
+
payload: CredentialCreateRequest,
|
|
39
|
+
): Promise<CredentialCreateResponse> {
|
|
40
|
+
return requester("/credential/create", "POST", payload);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function verifyCredential(
|
|
44
|
+
requester: (path: string, method: "POST", body: { credential_id: string }) => Promise<CredentialVerifyResponse>,
|
|
45
|
+
credentialId: string,
|
|
46
|
+
): Promise<CredentialVerifyResponse> {
|
|
47
|
+
return requester("/credential/verify", "POST", { credential_id: credentialId });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function storeDidCredentialMetadata(userId: string, metadata: DidCredentialMetadata): void {
|
|
51
|
+
memoryMetadata.set(userId, metadata);
|
|
52
|
+
if (globalThis.localStorage) {
|
|
53
|
+
globalThis.localStorage.setItem(`${METADATA_KEY}:${userId}`, JSON.stringify(metadata));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function loadDidCredentialMetadata(userId: string): DidCredentialMetadata | null {
|
|
58
|
+
const fromMemory = memoryMetadata.get(userId);
|
|
59
|
+
if (fromMemory) return fromMemory;
|
|
60
|
+
const raw = globalThis.localStorage?.getItem(`${METADATA_KEY}:${userId}`);
|
|
61
|
+
if (!raw) return null;
|
|
62
|
+
return JSON.parse(raw) as DidCredentialMetadata;
|
|
63
|
+
}
|
package/src/auth/did.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 Xurge Digital Lab
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Resolver } from "did-resolver";
|
|
12
|
+
|
|
13
|
+
export interface DidVerificationMethod {
|
|
14
|
+
id: string;
|
|
15
|
+
type: string;
|
|
16
|
+
controller: string;
|
|
17
|
+
publicKeyMultibase?: string;
|
|
18
|
+
blockchainAccountId?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DidDocumentShape {
|
|
22
|
+
id: string;
|
|
23
|
+
verificationMethod: DidVerificationMethod[];
|
|
24
|
+
authentication: string[];
|
|
25
|
+
service?: Array<Record<string, string>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DidResolutionResponse {
|
|
29
|
+
did_uri: string;
|
|
30
|
+
resolution_type?: string;
|
|
31
|
+
did_document: DidDocumentShape;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fallbackResolver = new Resolver({});
|
|
35
|
+
|
|
36
|
+
export function deriveXrplDid(address: string, network: "testnet" | "mainnet" = "testnet"): string {
|
|
37
|
+
if (!address || !address.startsWith("r")) {
|
|
38
|
+
throw new Error("Invalid XRPL address");
|
|
39
|
+
}
|
|
40
|
+
return `did:xrpl:${network}:${address}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createMinimalDidDocument(didUri: string, e2eePubkeyCommitment: string): DidDocumentShape {
|
|
44
|
+
return {
|
|
45
|
+
id: didUri,
|
|
46
|
+
verificationMethod: [
|
|
47
|
+
{
|
|
48
|
+
id: `${didUri}#owner`,
|
|
49
|
+
type: "EcdsaSecp256k1RecoveryMethod2020",
|
|
50
|
+
controller: didUri,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: `${didUri}#e2ee`,
|
|
54
|
+
type: "E2EEPublicKeyCommitment",
|
|
55
|
+
controller: didUri,
|
|
56
|
+
publicKeyMultibase: e2eePubkeyCommitment,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
authentication: [`${didUri}#owner`],
|
|
60
|
+
service: [
|
|
61
|
+
{
|
|
62
|
+
id: `${didUri}#did-resolution`,
|
|
63
|
+
type: "BriijDidResolution",
|
|
64
|
+
serviceEndpoint: "/_matrix/client/v3/did/resolve",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function resolveDidViaHomeserver(
|
|
71
|
+
requester: (path: string, method: "GET") => Promise<DidResolutionResponse>,
|
|
72
|
+
account: string,
|
|
73
|
+
): Promise<DidResolutionResponse> {
|
|
74
|
+
const direct = await requester(`/did/resolve?account=${encodeURIComponent(account)}`, "GET");
|
|
75
|
+
if (direct?.did_document?.id) {
|
|
76
|
+
return direct;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Keep a resolver instance available for future DID-method plugins.
|
|
80
|
+
const fallback = await fallbackResolver.resolve(`did:xrpl:testnet:${account}`);
|
|
81
|
+
if (fallback.didDocument) {
|
|
82
|
+
return {
|
|
83
|
+
did_uri: fallback.didDocument.id,
|
|
84
|
+
did_document: fallback.didDocument as DidDocumentShape,
|
|
85
|
+
resolution_type: "resolver-fallback",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
throw new Error("Failed to resolve DID document");
|
|
89
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 Xurge Digital Lab
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { XRPL_WALLET_LOGIN_TYPE } from "../@types/auth.ts";
|
|
12
|
+
|
|
13
|
+
export interface WalletProofResult {
|
|
14
|
+
address: string;
|
|
15
|
+
signature: string;
|
|
16
|
+
publicKey?: string;
|
|
17
|
+
network?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WalletLoginSubmission {
|
|
21
|
+
type: typeof XRPL_WALLET_LOGIN_TYPE;
|
|
22
|
+
address: string;
|
|
23
|
+
signature: string;
|
|
24
|
+
public_key?: string;
|
|
25
|
+
network: string;
|
|
26
|
+
session: string;
|
|
27
|
+
username?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type WalletProofProvider = (challenge: string, network: string) => Promise<WalletProofResult>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes wallet proof payload to the login body used by the homeserver.
|
|
34
|
+
*/
|
|
35
|
+
export function buildWalletLoginSubmission(
|
|
36
|
+
proof: WalletProofResult,
|
|
37
|
+
session: string,
|
|
38
|
+
network: string,
|
|
39
|
+
username?: string,
|
|
40
|
+
): WalletLoginSubmission {
|
|
41
|
+
return {
|
|
42
|
+
type: XRPL_WALLET_LOGIN_TYPE,
|
|
43
|
+
session,
|
|
44
|
+
address: proof.address,
|
|
45
|
+
signature: proof.signature,
|
|
46
|
+
public_key: proof.publicKey,
|
|
47
|
+
network: proof.network ?? network,
|
|
48
|
+
username,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 Xurge Digital Lab
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface E2eeZkInput {
|
|
12
|
+
didUri: string;
|
|
13
|
+
xrplAddress: string;
|
|
14
|
+
e2eePrivateKey: string;
|
|
15
|
+
credentialId: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface E2eeZkProofPayload {
|
|
19
|
+
proof: Record<string, unknown>;
|
|
20
|
+
publicSignals: string[] | Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface E2eeZkGenerateOptions {
|
|
24
|
+
wasmPath: string;
|
|
25
|
+
zkeyPath: string;
|
|
26
|
+
fullProve?: (
|
|
27
|
+
input: Record<string, string>,
|
|
28
|
+
wasmPath: string,
|
|
29
|
+
zkeyPath: string,
|
|
30
|
+
) => Promise<E2eeZkProofPayload>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface E2eeZkVerifyOptions {
|
|
34
|
+
verificationKey: Record<string, unknown>;
|
|
35
|
+
verify?: (
|
|
36
|
+
verificationKey: Record<string, unknown>,
|
|
37
|
+
publicSignals: E2eeZkProofPayload["publicSignals"],
|
|
38
|
+
proof: E2eeZkProofPayload["proof"],
|
|
39
|
+
) => Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function sha256Hex(input: string): Promise<string> {
|
|
43
|
+
if (!globalThis.crypto?.subtle) {
|
|
44
|
+
return input;
|
|
45
|
+
}
|
|
46
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
|
|
47
|
+
return Array.from(new Uint8Array(digest))
|
|
48
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
49
|
+
.join("");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function generateE2eeZkProof(
|
|
53
|
+
input: E2eeZkInput,
|
|
54
|
+
options: E2eeZkGenerateOptions,
|
|
55
|
+
): Promise<E2eeZkProofPayload> {
|
|
56
|
+
const fullProve =
|
|
57
|
+
options.fullProve ??
|
|
58
|
+
(async (signals, wasmPath, zkeyPath) => {
|
|
59
|
+
const snarkjs = await import("snarkjs");
|
|
60
|
+
return snarkjs.groth16.fullProve(signals, wasmPath, zkeyPath);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const e2eePrivateKeyHash = await sha256Hex(input.e2eePrivateKey);
|
|
64
|
+
return fullProve(
|
|
65
|
+
{
|
|
66
|
+
did_uri: input.didUri,
|
|
67
|
+
xrpl_address: input.xrplAddress,
|
|
68
|
+
credential_id: input.credentialId,
|
|
69
|
+
e2ee_privkey_hash: e2eePrivateKeyHash,
|
|
70
|
+
},
|
|
71
|
+
options.wasmPath,
|
|
72
|
+
options.zkeyPath,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function verifyE2eeZkProof(
|
|
77
|
+
payload: E2eeZkProofPayload,
|
|
78
|
+
options: E2eeZkVerifyOptions,
|
|
79
|
+
): Promise<boolean> {
|
|
80
|
+
const verifier =
|
|
81
|
+
options.verify ??
|
|
82
|
+
(async (verificationKey, publicSignals, proof) => {
|
|
83
|
+
const snarkjs = await import("snarkjs");
|
|
84
|
+
return snarkjs.groth16.verify(verificationKey, publicSignals, proof);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return verifier(options.verificationKey, payload.publicSignals, payload.proof);
|
|
88
|
+
}
|
package/src/briij.ts
CHANGED
|
@@ -49,6 +49,18 @@ export * from "./scheduler.ts";
|
|
|
49
49
|
export * from "./filter.ts";
|
|
50
50
|
export * from "./timeline-window.ts";
|
|
51
51
|
export * from "./interactive-auth.ts";
|
|
52
|
+
export * from "./auth/did.ts";
|
|
53
|
+
export {
|
|
54
|
+
type CredentialCreateRequest,
|
|
55
|
+
type CredentialCreateResponse,
|
|
56
|
+
type CredentialVerifyResponse,
|
|
57
|
+
loadDidCredentialMetadata,
|
|
58
|
+
requestCredentialCreate,
|
|
59
|
+
storeDidCredentialMetadata,
|
|
60
|
+
verifyCredential,
|
|
61
|
+
} from "./auth/credential.ts";
|
|
62
|
+
export * from "./auth/wallet.ts";
|
|
63
|
+
export * from "./auth/zkpE2EE.ts";
|
|
52
64
|
export * from "./xrpl/identity.ts";
|
|
53
65
|
export * from "./xrpl/trust.ts";
|
|
54
66
|
export * from "./xrpl/verification.ts";
|
package/src/client.ts
CHANGED
|
@@ -204,6 +204,11 @@ import {
|
|
|
204
204
|
} from "./webrtc/groupCall.ts";
|
|
205
205
|
import { MediaHandler } from "./webrtc/mediaHandler.ts";
|
|
206
206
|
import {
|
|
207
|
+
type CredentialCreateResult,
|
|
208
|
+
type CredentialVerifyResult,
|
|
209
|
+
type DidCredentialMetadata,
|
|
210
|
+
type DidResolutionResult,
|
|
211
|
+
type ZkpVerifyResult,
|
|
207
212
|
type ILoginFlowsResponse,
|
|
208
213
|
type IRefreshTokenResponse,
|
|
209
214
|
type LoginRequest,
|
|
@@ -220,6 +225,15 @@ import {
|
|
|
220
225
|
type XrplAuthCompleteRequest,
|
|
221
226
|
type XrplWalletChallengePayload,
|
|
222
227
|
} from "./@types/auth.ts";
|
|
228
|
+
import { createMinimalDidDocument, deriveXrplDid, resolveDidViaHomeserver } from "./auth/did.ts";
|
|
229
|
+
import {
|
|
230
|
+
loadDidCredentialMetadata,
|
|
231
|
+
requestCredentialCreate,
|
|
232
|
+
storeDidCredentialMetadata,
|
|
233
|
+
verifyCredential,
|
|
234
|
+
} from "./auth/credential.ts";
|
|
235
|
+
import { generateE2eeZkProof } from "./auth/zkpE2EE.ts";
|
|
236
|
+
import { buildWalletLoginSubmission, type WalletProofProvider, type WalletProofResult } from "./auth/wallet.ts";
|
|
223
237
|
import { TypedEventEmitter } from "./models/typed-event-emitter.ts";
|
|
224
238
|
import { MAIN_ROOM_TIMELINE, ReceiptType } from "./@types/read_receipts.ts";
|
|
225
239
|
import { type MSC3575SlidingSyncRequest, type MSC3575SlidingSyncResponse, type SlidingSync } from "./sliding-sync.ts";
|
|
@@ -6997,6 +7011,179 @@ export class BriijClient extends TypedEventEmitter<EmittedEvents, ClientEventHan
|
|
|
6997
7011
|
});
|
|
6998
7012
|
}
|
|
6999
7013
|
|
|
7014
|
+
/**
|
|
7015
|
+
* XRPL DID + credential login flow:
|
|
7016
|
+
* 1) challenge request, 2) wallet proof submission, 3) DID resolve, 4) credential request.
|
|
7017
|
+
*/
|
|
7018
|
+
public async loginWithXrplDidCredential(params: {
|
|
7019
|
+
address: string;
|
|
7020
|
+
username?: string;
|
|
7021
|
+
network?: string;
|
|
7022
|
+
didNetwork?: "testnet" | "mainnet";
|
|
7023
|
+
e2eePubkeyCommitment?: string;
|
|
7024
|
+
longevityMode?: boolean;
|
|
7025
|
+
walletProofProvider: WalletProofProvider;
|
|
7026
|
+
zkpLongevityMode?: {
|
|
7027
|
+
enabled: boolean;
|
|
7028
|
+
e2eePrivateKey?: string;
|
|
7029
|
+
strict?: boolean;
|
|
7030
|
+
generateProof?: (context: {
|
|
7031
|
+
didUri: string;
|
|
7032
|
+
xrplAddress: string;
|
|
7033
|
+
credentialId: string;
|
|
7034
|
+
e2eePubkeyCommitment: string;
|
|
7035
|
+
}) => Promise<{ proof: Record<string, unknown>; publicSignals: string[] | Record<string, string> }>;
|
|
7036
|
+
};
|
|
7037
|
+
}): Promise<
|
|
7038
|
+
LoginResponse & {
|
|
7039
|
+
did: DidResolutionResult;
|
|
7040
|
+
didDocument: ReturnType<typeof createMinimalDidDocument>;
|
|
7041
|
+
credential: CredentialCreateResult;
|
|
7042
|
+
credentialVerification: CredentialVerifyResult;
|
|
7043
|
+
metadata: DidCredentialMetadata;
|
|
7044
|
+
zkp?: ZkpVerifyResult;
|
|
7045
|
+
}
|
|
7046
|
+
> {
|
|
7047
|
+
const network = params.network ?? "xrpl";
|
|
7048
|
+
const didNetwork = params.didNetwork ?? "testnet";
|
|
7049
|
+
|
|
7050
|
+
const challenge = await this.getXrplAuthChallenge({
|
|
7051
|
+
address: params.address,
|
|
7052
|
+
network,
|
|
7053
|
+
username: params.username,
|
|
7054
|
+
preferred_localpart: params.username,
|
|
7055
|
+
});
|
|
7056
|
+
|
|
7057
|
+
const walletProof: WalletProofResult = await params.walletProofProvider(challenge.challenge, network);
|
|
7058
|
+
if (walletProof.address !== params.address) {
|
|
7059
|
+
throw new Error("Wallet proof address mismatch");
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
const loginResponse = await this.completeXrplAuth(
|
|
7063
|
+
buildWalletLoginSubmission(walletProof, challenge.session, network, params.username),
|
|
7064
|
+
);
|
|
7065
|
+
await this.applyLoginResponse(loginResponse, XRPL_WALLET_LOGIN_TYPE);
|
|
7066
|
+
|
|
7067
|
+
const didUri = deriveXrplDid(walletProof.address, didNetwork);
|
|
7068
|
+
const commitment = params.e2eePubkeyCommitment ?? (await this.createE2eeCommitment(loginResponse.user_id));
|
|
7069
|
+
const didDocument = createMinimalDidDocument(didUri, commitment);
|
|
7070
|
+
|
|
7071
|
+
const did = await resolveDidViaHomeserver(async (path: string) => {
|
|
7072
|
+
return this.http.authedRequest<DidResolutionResult>(Method.Get, path);
|
|
7073
|
+
}, walletProof.address);
|
|
7074
|
+
|
|
7075
|
+
const credential = await requestCredentialCreate(
|
|
7076
|
+
async (path, method, body) => this.http.authedRequest<CredentialCreateResult>(Method.Post, path, undefined, body),
|
|
7077
|
+
{
|
|
7078
|
+
subject: walletProof.address,
|
|
7079
|
+
did_uri: did.did_uri ?? didUri,
|
|
7080
|
+
e2ee_pubkey_commitment: commitment,
|
|
7081
|
+
},
|
|
7082
|
+
);
|
|
7083
|
+
|
|
7084
|
+
const credentialVerification = await verifyCredential(
|
|
7085
|
+
async (path, method, body) =>
|
|
7086
|
+
this.http.authedRequest<CredentialVerifyResult>(Method.Post, path, undefined, body),
|
|
7087
|
+
credential.credential_id,
|
|
7088
|
+
);
|
|
7089
|
+
|
|
7090
|
+
const zkpModeEnabled = params.longevityMode ?? params.zkpLongevityMode?.enabled ?? false;
|
|
7091
|
+
let zkp: ZkpVerifyResult | undefined;
|
|
7092
|
+
if (zkpModeEnabled) {
|
|
7093
|
+
try {
|
|
7094
|
+
const generatedProof =
|
|
7095
|
+
(await params.zkpLongevityMode?.generateProof?.({
|
|
7096
|
+
didUri: did.did_uri ?? didUri,
|
|
7097
|
+
xrplAddress: walletProof.address,
|
|
7098
|
+
credentialId: credential.credential_id,
|
|
7099
|
+
e2eePubkeyCommitment: commitment,
|
|
7100
|
+
})) ??
|
|
7101
|
+
(await generateE2eeZkProof(
|
|
7102
|
+
{
|
|
7103
|
+
didUri: did.did_uri ?? didUri,
|
|
7104
|
+
xrplAddress: walletProof.address,
|
|
7105
|
+
credentialId: credential.credential_id,
|
|
7106
|
+
e2eePrivateKey: params.zkpLongevityMode?.e2eePrivateKey ?? commitment,
|
|
7107
|
+
},
|
|
7108
|
+
{
|
|
7109
|
+
wasmPath: "/circuits/e2ee_credential.wasm",
|
|
7110
|
+
zkeyPath: "/circuits/e2ee_credential.zkey",
|
|
7111
|
+
},
|
|
7112
|
+
));
|
|
7113
|
+
|
|
7114
|
+
zkp = await this.http.authedRequest<ZkpVerifyResult>(Method.Post, "/zkp/verify", undefined, {
|
|
7115
|
+
proof: generatedProof.proof,
|
|
7116
|
+
public_signals: generatedProof.publicSignals,
|
|
7117
|
+
e2ee_pubkey_commitment: commitment,
|
|
7118
|
+
});
|
|
7119
|
+
} catch (error) {
|
|
7120
|
+
if (params.zkpLongevityMode?.strict) {
|
|
7121
|
+
throw error;
|
|
7122
|
+
}
|
|
7123
|
+
zkp = { valid: false, reason: "zkp_verification_skipped" };
|
|
7124
|
+
}
|
|
7125
|
+
}
|
|
7126
|
+
|
|
7127
|
+
const metadata: DidCredentialMetadata = {
|
|
7128
|
+
didUri: did.did_uri ?? didUri,
|
|
7129
|
+
credentialId: credential.credential_id,
|
|
7130
|
+
issuedAt: Date.now(),
|
|
7131
|
+
};
|
|
7132
|
+
storeDidCredentialMetadata(loginResponse.user_id, metadata);
|
|
7133
|
+
await this.persistDidDeviceBinding({
|
|
7134
|
+
didUri: metadata.didUri,
|
|
7135
|
+
credentialId: metadata.credentialId,
|
|
7136
|
+
e2eePubkeyCommitment: commitment,
|
|
7137
|
+
xrplAddress: walletProof.address,
|
|
7138
|
+
network,
|
|
7139
|
+
});
|
|
7140
|
+
|
|
7141
|
+
return {
|
|
7142
|
+
...loginResponse,
|
|
7143
|
+
did,
|
|
7144
|
+
didDocument,
|
|
7145
|
+
credential,
|
|
7146
|
+
credentialVerification,
|
|
7147
|
+
metadata,
|
|
7148
|
+
zkp,
|
|
7149
|
+
};
|
|
7150
|
+
}
|
|
7151
|
+
|
|
7152
|
+
public getStoredDidCredentialMetadata(userId: string): DidCredentialMetadata | null {
|
|
7153
|
+
return loadDidCredentialMetadata(userId);
|
|
7154
|
+
}
|
|
7155
|
+
|
|
7156
|
+
private async createE2eeCommitment(seed: string): Promise<string> {
|
|
7157
|
+
if (!globalThis.crypto?.subtle) {
|
|
7158
|
+
return encodeUnpaddedBase64Url(new TextEncoder().encode(seed));
|
|
7159
|
+
}
|
|
7160
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(seed));
|
|
7161
|
+
return Array.from(new Uint8Array(digest))
|
|
7162
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
7163
|
+
.join("");
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
private async persistDidDeviceBinding(binding: {
|
|
7167
|
+
didUri: string;
|
|
7168
|
+
credentialId: string;
|
|
7169
|
+
e2eePubkeyCommitment: string;
|
|
7170
|
+
xrplAddress: string;
|
|
7171
|
+
network: string;
|
|
7172
|
+
}): Promise<void> {
|
|
7173
|
+
try {
|
|
7174
|
+
await this.setAccountData(WALLET_IDENTITY_ACCOUNT_DATA_TYPE, {
|
|
7175
|
+
chain_id: "xrpl",
|
|
7176
|
+
account_id: binding.xrplAddress,
|
|
7177
|
+
network: binding.network,
|
|
7178
|
+
did_uri: binding.didUri,
|
|
7179
|
+
credential_id: binding.credentialId,
|
|
7180
|
+
e2ee_pubkey_commitment: binding.e2eePubkeyCommitment,
|
|
7181
|
+
});
|
|
7182
|
+
} catch (error) {
|
|
7183
|
+
logger.warn("Failed to persist DID/E2EE device binding metadata", error);
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
7186
|
+
|
|
7000
7187
|
/**
|
|
7001
7188
|
* Store a chain-agnostic wallet recovery envelope in account data.
|
|
7002
7189
|
*
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 Xurge Digital Lab
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* Mermaid flow reference:
|
|
12
|
+
flowchart LR
|
|
13
|
+
wallet[Connect Wallet] --> login[Submit wallet proof to /login]
|
|
14
|
+
login --> did[Resolve/Create DID]
|
|
15
|
+
did --> cred[Request CredentialCreate]
|
|
16
|
+
cred --> done[Persist DID + credential metadata]
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface LoginStepperProps {
|
|
20
|
+
currentStep: 1 | 2 | 3 | 4 | 5;
|
|
21
|
+
longevityModeEnabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Lightweight JSX component for apps consuming the SDK.
|
|
26
|
+
*/
|
|
27
|
+
export function LoginStepper({ currentStep, longevityModeEnabled = false }: LoginStepperProps): string {
|
|
28
|
+
const longevityToggle = `${longevityModeEnabled ? "[x]" : "[ ]"} Enable Longevity Mode (ZKP)`;
|
|
29
|
+
const warning = longevityModeEnabled
|
|
30
|
+
? "ZKP path enabled: strongest DID-bound E2EE continuity."
|
|
31
|
+
: "Warning: continuing without ZKP uses wallet-signature fallback only.";
|
|
32
|
+
const steps = [
|
|
33
|
+
"1. Wallet Proof",
|
|
34
|
+
"2. DID Resolve/Create",
|
|
35
|
+
"3. Credential Create",
|
|
36
|
+
"4. Persist Metadata",
|
|
37
|
+
"5. ZKP Longevity Verify (optional)",
|
|
38
|
+
];
|
|
39
|
+
const renderedSteps = steps
|
|
40
|
+
.map((step, idx) => {
|
|
41
|
+
if (idx === 4 && !longevityModeEnabled) {
|
|
42
|
+
return `[ ] ${step}`;
|
|
43
|
+
}
|
|
44
|
+
const active = idx + 1 <= currentStep ? "[x]" : "[ ]";
|
|
45
|
+
return `${active} ${step}`;
|
|
46
|
+
})
|
|
47
|
+
.join("\n");
|
|
48
|
+
|
|
49
|
+
return `${longevityToggle}\n${warning}\n${renderedSteps}`;
|
|
50
|
+
}
|