@renown/sdk 6.2.2-dev.11 → 6.2.2-dev.13
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 +103 -1
- package/dist/crypto.d.ts +2 -2
- package/dist/index-25fTa4yI.d.ts +127 -0
- package/dist/index-25fTa4yI.d.ts.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -7
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +54 -11
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +93 -11
- package/dist/node.js.map +1 -1
- package/dist/renown-builder-Cm5JNOul.js +839 -0
- package/dist/renown-builder-Cm5JNOul.js.map +1 -0
- package/dist/types-B5VOm-9h.d.ts +48 -0
- package/dist/types-B5VOm-9h.d.ts.map +1 -0
- package/dist/types-DG816Qa8.js +27 -0
- package/dist/types-DG816Qa8.js.map +1 -0
- package/dist/{signer-DYKGoYHB.d.ts → types-DsF91dDF.d.ts} +121 -115
- package/dist/types-DsF91dDF.d.ts.map +1 -0
- package/dist/{utils-C9ZgMs_C.d.ts → utils-OOrVXHof.d.ts} +38 -96
- package/dist/utils-OOrVXHof.d.ts.map +1 -0
- package/dist/wallet/index.d.ts +28 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/index.js +55 -0
- package/dist/wallet/index.js.map +1 -0
- package/dist/wallet/mock/index.d.ts +18 -0
- package/dist/wallet/mock/index.d.ts.map +1 -0
- package/dist/wallet/mock/index.js +38 -0
- package/dist/wallet/mock/index.js.map +1 -0
- package/dist/wallet/privy/index.d.ts +56 -0
- package/dist/wallet/privy/index.d.ts.map +1 -0
- package/dist/wallet/privy/index.js +198 -0
- package/dist/wallet/privy/index.js.map +1 -0
- package/dist/wallet/rainbow/index.d.ts +13 -0
- package/dist/wallet/rainbow/index.d.ts.map +1 -0
- package/dist/wallet/rainbow/index.js +217 -0
- package/dist/wallet/rainbow/index.js.map +1 -0
- package/package.json +57 -3
- package/dist/browser-key-storage-DSkDBAhU.d.ts +0 -14
- package/dist/browser-key-storage-DSkDBAhU.d.ts.map +0 -1
- package/dist/renown-builder-Sn_Y2Cb2.js +0 -496
- package/dist/renown-builder-Sn_Y2Cb2.js.map +0 -1
- package/dist/signer-DYKGoYHB.d.ts.map +0 -1
- package/dist/utils-C9ZgMs_C.d.ts.map +0 -1
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import { m as verifyAuthBearerToken, p as parsePkhDid, s as RenownCryptoBuilder, t as RenownCryptoSigner } from "./signer-CgYhXTV7.js";
|
|
2
|
+
import { recoverTypedDataAddress } from "viem";
|
|
3
|
+
import { createAction } from "@powerhousedao/shared/document-model";
|
|
4
|
+
//#region src/constants.ts
|
|
5
|
+
const DEFAULT_RENOWN_URL = "https://www.renown.id";
|
|
6
|
+
const DEFAULT_RENOWN_NETWORK_ID = "eip155";
|
|
7
|
+
const DEFAULT_RENOWN_CHAIN_ID = "1";
|
|
8
|
+
/** Default cookie name for a server-readable Renown session (bearer token). */
|
|
9
|
+
const RENOWN_SESSION_COOKIE = "renown_session";
|
|
10
|
+
const DOMAIN_TYPE = [{
|
|
11
|
+
name: "version",
|
|
12
|
+
type: "string"
|
|
13
|
+
}, {
|
|
14
|
+
name: "chainId",
|
|
15
|
+
type: "uint256"
|
|
16
|
+
}];
|
|
17
|
+
const VERIFIABLE_CREDENTIAL_EIP712_TYPE = [
|
|
18
|
+
{
|
|
19
|
+
name: "@context",
|
|
20
|
+
type: "string[]"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "type",
|
|
24
|
+
type: "string[]"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "id",
|
|
28
|
+
type: "string"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "issuer",
|
|
32
|
+
type: "Issuer"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "credentialSubject",
|
|
36
|
+
type: "CredentialSubject"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "credentialSchema",
|
|
40
|
+
type: "CredentialSchema"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "issuanceDate",
|
|
44
|
+
type: "string"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "expirationDate",
|
|
48
|
+
type: "string"
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
const CREDENTIAL_SCHEMA_EIP712_TYPE = [{
|
|
52
|
+
name: "id",
|
|
53
|
+
type: "string"
|
|
54
|
+
}, {
|
|
55
|
+
name: "type",
|
|
56
|
+
type: "string"
|
|
57
|
+
}];
|
|
58
|
+
const CREDENTIAL_SUBJECT_TYPE = [{
|
|
59
|
+
name: "app",
|
|
60
|
+
type: "string"
|
|
61
|
+
}, {
|
|
62
|
+
name: "id",
|
|
63
|
+
type: "string"
|
|
64
|
+
}];
|
|
65
|
+
const ISSUER_TYPE = [{
|
|
66
|
+
name: "id",
|
|
67
|
+
type: "string"
|
|
68
|
+
}, {
|
|
69
|
+
name: "ethereumAddress",
|
|
70
|
+
type: "string"
|
|
71
|
+
}];
|
|
72
|
+
const CREDENTIAL_TYPES = {
|
|
73
|
+
EIP712Domain: DOMAIN_TYPE,
|
|
74
|
+
VerifiableCredential: VERIFIABLE_CREDENTIAL_EIP712_TYPE,
|
|
75
|
+
CredentialSchema: CREDENTIAL_SCHEMA_EIP712_TYPE,
|
|
76
|
+
CredentialSubject: CREDENTIAL_SUBJECT_TYPE,
|
|
77
|
+
Issuer: ISSUER_TYPE
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/discovery.ts
|
|
81
|
+
const DEFAULT_DISCOVERY_TIMEOUT_MS = 3e3;
|
|
82
|
+
async function resolveSwitchboardEndpoint(options) {
|
|
83
|
+
const { switchboardUrl, baseUrl, timeoutMs } = options;
|
|
84
|
+
if (switchboardUrl) return switchboardUrl;
|
|
85
|
+
if (!baseUrl) return;
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS);
|
|
88
|
+
try {
|
|
89
|
+
const url = new URL("/api/switchboard", baseUrl);
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
method: "GET",
|
|
92
|
+
signal: controller.signal
|
|
93
|
+
});
|
|
94
|
+
if (!response.ok) return;
|
|
95
|
+
return (await response.json()).endpoint || void 0;
|
|
96
|
+
} catch {
|
|
97
|
+
return;
|
|
98
|
+
} finally {
|
|
99
|
+
clearTimeout(timeout);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/switchboard.ts
|
|
104
|
+
function userDriveId(address) {
|
|
105
|
+
return `renown-${address.toLowerCase()}`;
|
|
106
|
+
}
|
|
107
|
+
const CREDENTIALS_QUERY = `
|
|
108
|
+
query RenownCredentials($input: RenownCredentialsInput!) {
|
|
109
|
+
renownCredentials(input: $input) {
|
|
110
|
+
documentId
|
|
111
|
+
credentialId
|
|
112
|
+
context
|
|
113
|
+
type
|
|
114
|
+
issuerId
|
|
115
|
+
issuerEthereumAddress
|
|
116
|
+
issuanceDate
|
|
117
|
+
expirationDate
|
|
118
|
+
credentialSubjectId
|
|
119
|
+
credentialSubjectApp
|
|
120
|
+
credentialStatusId
|
|
121
|
+
credentialStatusType
|
|
122
|
+
credentialSchemaId
|
|
123
|
+
credentialSchemaType
|
|
124
|
+
proofVerificationMethod
|
|
125
|
+
proofEthereumAddress
|
|
126
|
+
proofCreated
|
|
127
|
+
proofPurpose
|
|
128
|
+
proofType
|
|
129
|
+
proofValue
|
|
130
|
+
proofEip712Domain
|
|
131
|
+
proofEip712PrimaryType
|
|
132
|
+
revoked
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
const USERS_QUERY = `
|
|
137
|
+
query RenownUsers($input: RenownUsersInput!) {
|
|
138
|
+
renownUsers(input: $input) {
|
|
139
|
+
documentId
|
|
140
|
+
username
|
|
141
|
+
ethAddress
|
|
142
|
+
userImage
|
|
143
|
+
createdAt
|
|
144
|
+
updatedAt
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
const CREATE_EMPTY_DOCUMENT_MUTATION = `
|
|
149
|
+
mutation CreateEmptyDocument(
|
|
150
|
+
$documentType: String!
|
|
151
|
+
$parentIdentifier: String
|
|
152
|
+
) {
|
|
153
|
+
createEmptyDocument(
|
|
154
|
+
documentType: $documentType
|
|
155
|
+
parentIdentifier: $parentIdentifier
|
|
156
|
+
) {
|
|
157
|
+
id
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const MUTATE_DOCUMENT_MUTATION = `
|
|
162
|
+
mutation MutateDocument(
|
|
163
|
+
$documentIdentifier: String!
|
|
164
|
+
$actions: [JSONObject!]!
|
|
165
|
+
) {
|
|
166
|
+
mutateDocument(documentIdentifier: $documentIdentifier, actions: $actions) {
|
|
167
|
+
id
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
const RENOWN_CREDENTIAL_DOC_TYPE = "powerhouse/renown-credential";
|
|
172
|
+
const RENOWN_USER_DOC_TYPE = "powerhouse/renown-user";
|
|
173
|
+
function reshapeCredential(row) {
|
|
174
|
+
const domain = JSON.parse(row.proofEip712Domain);
|
|
175
|
+
return {
|
|
176
|
+
"@context": row.context,
|
|
177
|
+
type: row.type,
|
|
178
|
+
id: row.credentialId,
|
|
179
|
+
issuer: {
|
|
180
|
+
id: row.issuerId,
|
|
181
|
+
ethereumAddress: row.issuerEthereumAddress
|
|
182
|
+
},
|
|
183
|
+
credentialSubject: {
|
|
184
|
+
id: row.credentialSubjectId ?? "",
|
|
185
|
+
app: row.credentialSubjectApp
|
|
186
|
+
},
|
|
187
|
+
credentialSchema: {
|
|
188
|
+
id: row.credentialSchemaId,
|
|
189
|
+
type: row.credentialSchemaType
|
|
190
|
+
},
|
|
191
|
+
...row.credentialStatusId ? { credentialStatus: {
|
|
192
|
+
id: row.credentialStatusId,
|
|
193
|
+
type: row.credentialStatusType ?? ""
|
|
194
|
+
} } : {},
|
|
195
|
+
issuanceDate: row.issuanceDate,
|
|
196
|
+
expirationDate: row.expirationDate ?? "",
|
|
197
|
+
proof: {
|
|
198
|
+
type: row.proofType,
|
|
199
|
+
created: row.proofCreated,
|
|
200
|
+
verificationMethod: row.proofVerificationMethod,
|
|
201
|
+
proofPurpose: row.proofPurpose,
|
|
202
|
+
proofValue: row.proofValue,
|
|
203
|
+
ethereumAddress: row.proofEthereumAddress,
|
|
204
|
+
eip712: {
|
|
205
|
+
domain,
|
|
206
|
+
types: CREDENTIAL_TYPES,
|
|
207
|
+
primaryType: "VerifiableCredential"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function issuerChainId(issuerId) {
|
|
213
|
+
return issuerId.split(":")[3];
|
|
214
|
+
}
|
|
215
|
+
var SwitchboardClient = class {
|
|
216
|
+
#endpoint;
|
|
217
|
+
constructor(endpoint) {
|
|
218
|
+
this.#endpoint = endpoint;
|
|
219
|
+
}
|
|
220
|
+
get endpoint() {
|
|
221
|
+
return this.#endpoint;
|
|
222
|
+
}
|
|
223
|
+
async #request(query, variables) {
|
|
224
|
+
const response = await fetch(this.#endpoint, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: { "Content-Type": "application/json" },
|
|
227
|
+
body: JSON.stringify({
|
|
228
|
+
query,
|
|
229
|
+
variables
|
|
230
|
+
})
|
|
231
|
+
});
|
|
232
|
+
if (!response.ok) throw new Error(`Switchboard request failed: ${response.status}`);
|
|
233
|
+
const body = await response.json();
|
|
234
|
+
if (body.errors?.length) throw new Error(body.errors.map((e) => e.message).join("; "));
|
|
235
|
+
if (!body.data) throw new Error("Switchboard returned no data");
|
|
236
|
+
return body.data;
|
|
237
|
+
}
|
|
238
|
+
async getCredential(params) {
|
|
239
|
+
const { address, chainId, appDid } = params;
|
|
240
|
+
const { renownCredentials } = await this.#request(CREDENTIALS_QUERY, { input: {
|
|
241
|
+
driveId: userDriveId(address),
|
|
242
|
+
ethAddress: address.toLowerCase(),
|
|
243
|
+
did: appDid,
|
|
244
|
+
includeRevoked: false
|
|
245
|
+
} });
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
const mostRecent = renownCredentials.filter((row) => issuerChainId(row.issuerId) === String(chainId)).filter((row) => {
|
|
248
|
+
if (!row.expirationDate) return true;
|
|
249
|
+
const expiresAt = Date.parse(row.expirationDate);
|
|
250
|
+
return !Number.isNaN(expiresAt) && expiresAt > now;
|
|
251
|
+
}).sort((a, b) => Date.parse(b.issuanceDate) - Date.parse(a.issuanceDate)).at(0);
|
|
252
|
+
return mostRecent ? reshapeCredential(mostRecent) : void 0;
|
|
253
|
+
}
|
|
254
|
+
async getProfileByAddress(address) {
|
|
255
|
+
const { renownUsers } = await this.#request(USERS_QUERY, { input: {
|
|
256
|
+
driveId: userDriveId(address),
|
|
257
|
+
ethAddresses: [address.toLowerCase()]
|
|
258
|
+
} });
|
|
259
|
+
const user = renownUsers.at(0);
|
|
260
|
+
if (!user) return void 0;
|
|
261
|
+
return {
|
|
262
|
+
documentId: user.documentId,
|
|
263
|
+
username: user.username,
|
|
264
|
+
ethAddress: user.ethAddress,
|
|
265
|
+
userImage: user.userImage,
|
|
266
|
+
createdAt: user.createdAt,
|
|
267
|
+
updatedAt: user.updatedAt
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async createEmptyDocument(documentType, parentIdentifier) {
|
|
271
|
+
const { createEmptyDocument } = await this.#request(CREATE_EMPTY_DOCUMENT_MUTATION, {
|
|
272
|
+
documentType,
|
|
273
|
+
parentIdentifier
|
|
274
|
+
});
|
|
275
|
+
return createEmptyDocument.id;
|
|
276
|
+
}
|
|
277
|
+
async mutateDocument(documentIdentifier, actions) {
|
|
278
|
+
const { mutateDocument } = await this.#request(MUTATE_DOCUMENT_MUTATION, {
|
|
279
|
+
documentIdentifier,
|
|
280
|
+
actions
|
|
281
|
+
});
|
|
282
|
+
return mutateDocument.id;
|
|
283
|
+
}
|
|
284
|
+
async issueCredential(credential) {
|
|
285
|
+
const documentId = await this.createEmptyDocument(RENOWN_CREDENTIAL_DOC_TYPE);
|
|
286
|
+
const input = {
|
|
287
|
+
id: credential.id,
|
|
288
|
+
context: credential["@context"],
|
|
289
|
+
type: credential.type,
|
|
290
|
+
issuer: {
|
|
291
|
+
id: credential.issuer.id,
|
|
292
|
+
ethereumAddress: credential.issuer.ethereumAddress
|
|
293
|
+
},
|
|
294
|
+
credentialSubject: {
|
|
295
|
+
id: credential.credentialSubject.id,
|
|
296
|
+
app: credential.credentialSubject.app
|
|
297
|
+
},
|
|
298
|
+
credentialSchema: {
|
|
299
|
+
id: credential.credentialSchema.id,
|
|
300
|
+
type: credential.credentialSchema.type
|
|
301
|
+
},
|
|
302
|
+
issuanceDate: credential.issuanceDate,
|
|
303
|
+
expirationDate: credential.expirationDate || void 0,
|
|
304
|
+
proof: {
|
|
305
|
+
type: credential.proof.type,
|
|
306
|
+
created: credential.proof.created,
|
|
307
|
+
verificationMethod: credential.proof.verificationMethod,
|
|
308
|
+
proofPurpose: credential.proof.proofPurpose,
|
|
309
|
+
proofValue: credential.proof.proofValue,
|
|
310
|
+
ethereumAddress: credential.proof.ethereumAddress,
|
|
311
|
+
eip712: {
|
|
312
|
+
domain: {
|
|
313
|
+
version: credential.proof.eip712.domain.version,
|
|
314
|
+
chainId: credential.proof.eip712.domain.chainId
|
|
315
|
+
},
|
|
316
|
+
primaryType: credential.proof.eip712.primaryType
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
await this.mutateDocument(documentId, [createAction("INIT", input)]);
|
|
321
|
+
return documentId;
|
|
322
|
+
}
|
|
323
|
+
async findOrCreateUser(address, profile = {}) {
|
|
324
|
+
const updates = [];
|
|
325
|
+
if (profile.username !== void 0) updates.push(createAction("SET_USERNAME", { username: profile.username }));
|
|
326
|
+
if (profile.userImage !== void 0) updates.push(createAction("SET_USER_IMAGE", { userImage: profile.userImage }));
|
|
327
|
+
const existing = await this.getProfileByAddress(address);
|
|
328
|
+
if (existing) {
|
|
329
|
+
if (updates.length) await this.mutateDocument(existing.documentId, updates);
|
|
330
|
+
return existing.documentId;
|
|
331
|
+
}
|
|
332
|
+
const documentId = await this.createEmptyDocument(RENOWN_USER_DOC_TYPE);
|
|
333
|
+
await this.mutateDocument(documentId, [createAction("SET_ETH_ADDRESS", { ethAddress: address }), ...updates]);
|
|
334
|
+
return documentId;
|
|
335
|
+
}
|
|
336
|
+
async revokeCredential(documentId, reason) {
|
|
337
|
+
await this.mutateDocument(documentId, [createAction("REVOKE", {
|
|
338
|
+
revokedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
|
+
reason: reason ?? null
|
|
340
|
+
})]);
|
|
341
|
+
}
|
|
342
|
+
profileFetcher = (user) => this.getProfileByAddress(user.address).catch(() => void 0);
|
|
343
|
+
};
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/credential.ts
|
|
346
|
+
/** Assemble a Renown delegation VC and sign it with the provided signer. */
|
|
347
|
+
async function buildAndSignCredential(params) {
|
|
348
|
+
const { signTypedData, address, chainId, app, appId, expiresInDays = 7 } = params;
|
|
349
|
+
const issuerId = `did:pkh:${DEFAULT_RENOWN_NETWORK_ID}:${chainId}:${address.toLowerCase()}`;
|
|
350
|
+
const credentialId = `urn:uuid:${globalThis.crypto.randomUUID()}`;
|
|
351
|
+
const now = /* @__PURE__ */ new Date();
|
|
352
|
+
const expirationDate = new Date(now.getTime() + expiresInDays * 24 * 60 * 60 * 1e3);
|
|
353
|
+
const message = {
|
|
354
|
+
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
|
355
|
+
type: ["VerifiableCredential", "RenownCredential"],
|
|
356
|
+
id: credentialId,
|
|
357
|
+
issuer: {
|
|
358
|
+
id: issuerId,
|
|
359
|
+
ethereumAddress: address
|
|
360
|
+
},
|
|
361
|
+
credentialSubject: {
|
|
362
|
+
id: appId,
|
|
363
|
+
app
|
|
364
|
+
},
|
|
365
|
+
credentialSchema: {
|
|
366
|
+
id: "https://renown.id/schemas/renown-credential/v1",
|
|
367
|
+
type: "JsonSchemaValidator2018"
|
|
368
|
+
},
|
|
369
|
+
issuanceDate: now.toISOString(),
|
|
370
|
+
expirationDate: expirationDate.toISOString()
|
|
371
|
+
};
|
|
372
|
+
const domain = {
|
|
373
|
+
version: "1",
|
|
374
|
+
chainId
|
|
375
|
+
};
|
|
376
|
+
const { EIP712Domain, ...signTypes } = CREDENTIAL_TYPES;
|
|
377
|
+
const signature = await signTypedData({
|
|
378
|
+
domain,
|
|
379
|
+
types: signTypes,
|
|
380
|
+
primaryType: "VerifiableCredential",
|
|
381
|
+
message
|
|
382
|
+
});
|
|
383
|
+
return {
|
|
384
|
+
...message,
|
|
385
|
+
proof: {
|
|
386
|
+
type: "EthereumEip712Signature2021",
|
|
387
|
+
created: message.issuanceDate,
|
|
388
|
+
verificationMethod: issuerId,
|
|
389
|
+
proofPurpose: "assertionMethod",
|
|
390
|
+
proofValue: signature,
|
|
391
|
+
ethereumAddress: address,
|
|
392
|
+
eip712: {
|
|
393
|
+
domain,
|
|
394
|
+
types: CREDENTIAL_TYPES,
|
|
395
|
+
primaryType: "VerifiableCredential"
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
/** Recover the address that signed a credential's EIP-712 proof. */
|
|
401
|
+
async function recoverCredentialSigner(credential) {
|
|
402
|
+
const { proof, ...message } = credential;
|
|
403
|
+
const { EIP712Domain, ...types } = CREDENTIAL_TYPES;
|
|
404
|
+
return recoverTypedDataAddress({
|
|
405
|
+
domain: proof.eip712.domain,
|
|
406
|
+
types,
|
|
407
|
+
primaryType: "VerifiableCredential",
|
|
408
|
+
message,
|
|
409
|
+
signature: proof.proofValue
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/** True when the proof was signed by the credential's declared issuer address. */
|
|
413
|
+
async function verifyCredentialSignature(credential) {
|
|
414
|
+
try {
|
|
415
|
+
return (await recoverCredentialSigner(credential)).toLowerCase() === credential.issuer.ethereumAddress.toLowerCase();
|
|
416
|
+
} catch {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function withEip712Domain(credential, chainId) {
|
|
421
|
+
if (credential.proof.eip712?.domain) return credential;
|
|
422
|
+
return {
|
|
423
|
+
...credential,
|
|
424
|
+
proof: {
|
|
425
|
+
...credential.proof,
|
|
426
|
+
eip712: {
|
|
427
|
+
domain: {
|
|
428
|
+
version: "1",
|
|
429
|
+
chainId
|
|
430
|
+
},
|
|
431
|
+
types: CREDENTIAL_TYPES,
|
|
432
|
+
primaryType: "VerifiableCredential"
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
async function fetchDelegationCredentialRest(params) {
|
|
438
|
+
const { address, chainId, appDid, baseUrl } = params;
|
|
439
|
+
const url = new URL(`/api/auth/credential?address=${encodeURIComponent(address)}&chainId=${encodeURIComponent(chainId)}&connectId=${encodeURIComponent(appDid)}&appId=${encodeURIComponent(appDid)}`, baseUrl);
|
|
440
|
+
const response = await fetch(url, { method: "GET" });
|
|
441
|
+
if (!response.ok) return void 0;
|
|
442
|
+
const { credential } = await response.json();
|
|
443
|
+
if (!credential) return void 0;
|
|
444
|
+
const [, , , issuerChainId, issuerAddress] = credential.issuer.id.split(":");
|
|
445
|
+
if (credential.credentialSubject.id !== appDid || issuerChainId !== String(chainId) || issuerAddress.toLowerCase() !== address.toLowerCase() || credential.issuer.ethereumAddress.toLowerCase() !== address.toLowerCase()) return;
|
|
446
|
+
if (credential.expirationDate) {
|
|
447
|
+
const expiresAt = Date.parse(credential.expirationDate);
|
|
448
|
+
if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) return;
|
|
449
|
+
}
|
|
450
|
+
return credential;
|
|
451
|
+
}
|
|
452
|
+
async function finalizeCredential(credential, chainId, verifySignature) {
|
|
453
|
+
if (!verifySignature) return credential;
|
|
454
|
+
const withDomain = withEip712Domain(credential, chainId);
|
|
455
|
+
if (withDomain.proof.eip712.domain.chainId !== chainId || !await verifyCredentialSignature(withDomain)) return;
|
|
456
|
+
return withDomain;
|
|
457
|
+
}
|
|
458
|
+
async function fetchDelegationCredential(options) {
|
|
459
|
+
const { address, chainId, appDid, switchboardUrl, discover = true, verifySignature = true } = options;
|
|
460
|
+
const explicitBaseUrl = options.baseUrl;
|
|
461
|
+
const baseUrl = explicitBaseUrl ?? "https://www.renown.id";
|
|
462
|
+
try {
|
|
463
|
+
const endpoint = switchboardUrl ? switchboardUrl : discover && explicitBaseUrl ? await resolveSwitchboardEndpoint({ baseUrl }) : void 0;
|
|
464
|
+
const credential = endpoint ? await new SwitchboardClient(endpoint).getCredential({
|
|
465
|
+
address,
|
|
466
|
+
chainId,
|
|
467
|
+
appDid
|
|
468
|
+
}) : await fetchDelegationCredentialRest({
|
|
469
|
+
address,
|
|
470
|
+
chainId,
|
|
471
|
+
appDid,
|
|
472
|
+
baseUrl
|
|
473
|
+
});
|
|
474
|
+
if (!credential) return void 0;
|
|
475
|
+
return await finalizeCredential(credential, chainId, verifySignature);
|
|
476
|
+
} catch {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
async function verifyAuthCredential(jwt, options = {}) {
|
|
481
|
+
const verified = await verifyAuthBearerToken(jwt, { audience: options.audience });
|
|
482
|
+
if (!verified) return void 0;
|
|
483
|
+
const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
|
|
484
|
+
const appDid = verified.issuer;
|
|
485
|
+
const credential = await fetchDelegationCredential({
|
|
486
|
+
address,
|
|
487
|
+
chainId,
|
|
488
|
+
appDid,
|
|
489
|
+
baseUrl: options.renownUrl,
|
|
490
|
+
switchboardUrl: options.switchboardUrl,
|
|
491
|
+
verifySignature: options.verifySignature
|
|
492
|
+
});
|
|
493
|
+
if (!credential) return void 0;
|
|
494
|
+
return {
|
|
495
|
+
address,
|
|
496
|
+
chainId,
|
|
497
|
+
networkId,
|
|
498
|
+
did: credential.issuer.id,
|
|
499
|
+
appDid,
|
|
500
|
+
credential
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region src/storage/common.ts
|
|
505
|
+
var BaseStorage = class {};
|
|
506
|
+
var MemoryStorage = class extends BaseStorage {
|
|
507
|
+
data = /* @__PURE__ */ new Map();
|
|
508
|
+
get(key) {
|
|
509
|
+
return this.data.get(key);
|
|
510
|
+
}
|
|
511
|
+
set(key, value) {
|
|
512
|
+
if (value === void 0) this.data.delete(key);
|
|
513
|
+
else this.data.set(key, value);
|
|
514
|
+
}
|
|
515
|
+
delete(key) {
|
|
516
|
+
this.data.delete(key);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
//#endregion
|
|
520
|
+
//#region src/common.ts
|
|
521
|
+
var RenownMemoryStorage = class extends MemoryStorage {};
|
|
522
|
+
var Renown = class {
|
|
523
|
+
#baseUrl;
|
|
524
|
+
#store;
|
|
525
|
+
#eventEmitter;
|
|
526
|
+
#appName;
|
|
527
|
+
#crypto;
|
|
528
|
+
#signer;
|
|
529
|
+
#profileFetcher;
|
|
530
|
+
#switchboard;
|
|
531
|
+
#status = "initial";
|
|
532
|
+
constructor(store, eventEmitter, crypto, appName, baseUrl = DEFAULT_RENOWN_URL, profileFetcher, switchboard) {
|
|
533
|
+
this.#store = store;
|
|
534
|
+
this.#eventEmitter = eventEmitter;
|
|
535
|
+
this.#baseUrl = baseUrl;
|
|
536
|
+
this.#crypto = crypto;
|
|
537
|
+
this.#appName = appName;
|
|
538
|
+
this.#profileFetcher = profileFetcher;
|
|
539
|
+
this.#switchboard = switchboard;
|
|
540
|
+
this.#signer = new RenownCryptoSigner(crypto, this.#appName, this.user);
|
|
541
|
+
this.on("user", (user) => {
|
|
542
|
+
this.#signer.user = user;
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
get baseUrl() {
|
|
546
|
+
return this.#baseUrl;
|
|
547
|
+
}
|
|
548
|
+
get user() {
|
|
549
|
+
return this.#store.get("user");
|
|
550
|
+
}
|
|
551
|
+
get status() {
|
|
552
|
+
return this.#status;
|
|
553
|
+
}
|
|
554
|
+
get signer() {
|
|
555
|
+
return this.#signer;
|
|
556
|
+
}
|
|
557
|
+
get crypto() {
|
|
558
|
+
return this.#crypto;
|
|
559
|
+
}
|
|
560
|
+
get did() {
|
|
561
|
+
return this.#crypto.did;
|
|
562
|
+
}
|
|
563
|
+
get profileFetcher() {
|
|
564
|
+
return this.#profileFetcher;
|
|
565
|
+
}
|
|
566
|
+
#updateStatus(status) {
|
|
567
|
+
this.#status = status;
|
|
568
|
+
this.#eventEmitter.emit("status", status);
|
|
569
|
+
}
|
|
570
|
+
#updateUser(user) {
|
|
571
|
+
if (user) this.#store.set("user", user);
|
|
572
|
+
else this.#store.delete("user");
|
|
573
|
+
this.#eventEmitter.emit("user", user);
|
|
574
|
+
}
|
|
575
|
+
async login(userDid) {
|
|
576
|
+
this.#updateStatus("checking");
|
|
577
|
+
try {
|
|
578
|
+
const result = parsePkhDid(userDid);
|
|
579
|
+
const credential = await this.#getCredential(result.address, result.chainId, this.#crypto.did);
|
|
580
|
+
if (!credential) {
|
|
581
|
+
this.#updateUser(void 0);
|
|
582
|
+
throw new Error("Credential not found");
|
|
583
|
+
}
|
|
584
|
+
return await this.#completeLogin(userDid, result, credential);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
this.#updateUser(void 0);
|
|
587
|
+
this.#updateStatus("not-authorized");
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async signIn(params) {
|
|
592
|
+
if (!this.#switchboard) throw new Error("signIn requires a switchboard endpoint. Set switchboardUrl or a discoverable baseUrl.");
|
|
593
|
+
const { address, chainId, signTypedData, username, userImage, expiresInDays } = params;
|
|
594
|
+
this.#updateStatus("checking");
|
|
595
|
+
try {
|
|
596
|
+
const credential = await buildAndSignCredential({
|
|
597
|
+
signTypedData,
|
|
598
|
+
address,
|
|
599
|
+
chainId,
|
|
600
|
+
app: this.#appName,
|
|
601
|
+
appId: this.did,
|
|
602
|
+
expiresInDays
|
|
603
|
+
});
|
|
604
|
+
await this.#switchboard.issueCredential(credential);
|
|
605
|
+
await this.#switchboard.findOrCreateUser(address, {
|
|
606
|
+
username,
|
|
607
|
+
userImage
|
|
608
|
+
});
|
|
609
|
+
const userDid = `did:pkh:${DEFAULT_RENOWN_NETWORK_ID}:${chainId}:${address.toLowerCase()}`;
|
|
610
|
+
return await this.#completeLogin(userDid, parsePkhDid(userDid), credential);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
this.#updateUser(void 0);
|
|
613
|
+
this.#updateStatus("not-authorized");
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async #completeLogin(userDid, result, credential) {
|
|
618
|
+
if (!(credential.issuer.id === userDid && credential.credentialSubject.id === this.did)) throw new Error("Invalid credential");
|
|
619
|
+
const verifiableCredential = credential.proof.eip712?.domain ? credential : {
|
|
620
|
+
...credential,
|
|
621
|
+
proof: {
|
|
622
|
+
...credential.proof,
|
|
623
|
+
eip712: {
|
|
624
|
+
domain: {
|
|
625
|
+
version: "1",
|
|
626
|
+
chainId: result.chainId
|
|
627
|
+
},
|
|
628
|
+
types: CREDENTIAL_TYPES,
|
|
629
|
+
primaryType: "VerifiableCredential"
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
if (credential.issuer.ethereumAddress.toLowerCase() !== result.address.toLowerCase() || verifiableCredential.proof.eip712.domain.chainId !== result.chainId || !await verifyCredentialSignature(verifiableCredential)) throw new Error("Invalid credential signature");
|
|
634
|
+
const user = {
|
|
635
|
+
...result,
|
|
636
|
+
address: credential.issuer.ethereumAddress,
|
|
637
|
+
did: userDid,
|
|
638
|
+
credential: verifiableCredential
|
|
639
|
+
};
|
|
640
|
+
this.#updateUser(user);
|
|
641
|
+
this.#updateStatus("authorized");
|
|
642
|
+
if (this.#profileFetcher) this.#profileFetcher(user, this.#baseUrl).then((profile) => {
|
|
643
|
+
if (profile && this.user?.address === user.address && this.user.chainId === user.chainId) this.#updateUser({
|
|
644
|
+
...this.user,
|
|
645
|
+
profile,
|
|
646
|
+
ens: {
|
|
647
|
+
name: profile.username ?? void 0,
|
|
648
|
+
avatarUrl: profile.userImage ?? void 0
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
}).catch(console.error);
|
|
652
|
+
return user;
|
|
653
|
+
}
|
|
654
|
+
logout() {
|
|
655
|
+
this.#updateUser(void 0);
|
|
656
|
+
this.#updateStatus("initial");
|
|
657
|
+
return Promise.resolve();
|
|
658
|
+
}
|
|
659
|
+
async revalidate() {
|
|
660
|
+
const user = this.user;
|
|
661
|
+
if (!user) return false;
|
|
662
|
+
try {
|
|
663
|
+
if (!await this.#getCredential(user.address, user.chainId, this.#crypto.did)) {
|
|
664
|
+
await this.logout();
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
} catch {
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
on(event, listener) {
|
|
673
|
+
return this.#eventEmitter.on(event, listener);
|
|
674
|
+
}
|
|
675
|
+
async #getCredential(address, chainId, appDid) {
|
|
676
|
+
if (this.#switchboard) return this.#switchboard.getCredential({
|
|
677
|
+
address,
|
|
678
|
+
chainId,
|
|
679
|
+
appDid
|
|
680
|
+
});
|
|
681
|
+
if (!this.#baseUrl) throw new Error("RENOWN_URL is not set");
|
|
682
|
+
const url = new URL(`/api/auth/credential?address=${encodeURIComponent(address)}&chainId=${encodeURIComponent(chainId)}&connectId=${encodeURIComponent(appDid)}&appId=${encodeURIComponent(appDid)}`, this.#baseUrl);
|
|
683
|
+
const response = await fetch(url, { method: "GET" });
|
|
684
|
+
if (response.ok) return (await response.json()).credential;
|
|
685
|
+
else throw new Error(`Failed to get credential: ${response.status}`);
|
|
686
|
+
}
|
|
687
|
+
async verifyBearerToken(token) {
|
|
688
|
+
return verifyAuthBearerToken(token);
|
|
689
|
+
}
|
|
690
|
+
async getBearerToken(options) {
|
|
691
|
+
if (!this.user) throw new Error("User not found");
|
|
692
|
+
return this.#crypto.getBearerToken(this.user.address, options);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/event/memory.ts
|
|
697
|
+
var MemoryEventEmitter = class {
|
|
698
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
699
|
+
on(event, listener) {
|
|
700
|
+
if (!this.#listeners.has(event)) this.#listeners.set(event, /* @__PURE__ */ new Set());
|
|
701
|
+
this.#listeners.get(event).add(listener);
|
|
702
|
+
return () => {
|
|
703
|
+
this.#listeners.get(event)?.delete(listener);
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
emit(event, data) {
|
|
707
|
+
this.#listeners.get(event)?.forEach((listener) => listener(data));
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region src/profile.ts
|
|
712
|
+
const fetchRenownProfile = async (user, baseUrl) => {
|
|
713
|
+
try {
|
|
714
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/profile`, {
|
|
715
|
+
method: "POST",
|
|
716
|
+
headers: { "Content-Type": "application/json" },
|
|
717
|
+
body: JSON.stringify({ ethAddress: user.address })
|
|
718
|
+
});
|
|
719
|
+
if (!response.ok) return;
|
|
720
|
+
return (await response.json()).profile ?? void 0;
|
|
721
|
+
} catch {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region src/renown-builder.ts
|
|
727
|
+
/**
|
|
728
|
+
* Base builder for creating Renown instances.
|
|
729
|
+
* Use platform-specific builders (RenownBuilder from init.browser.js or init.node.js)
|
|
730
|
+
* for pre-configured defaults.
|
|
731
|
+
*/
|
|
732
|
+
var BaseRenownBuilder = class BaseRenownBuilder {
|
|
733
|
+
#appName;
|
|
734
|
+
#storage;
|
|
735
|
+
#eventEmitter;
|
|
736
|
+
#crypto;
|
|
737
|
+
#keyPairStorage;
|
|
738
|
+
#baseUrl;
|
|
739
|
+
#switchboardUrl;
|
|
740
|
+
#profileFetcher;
|
|
741
|
+
/**
|
|
742
|
+
* @param appName - Application name used for signing context
|
|
743
|
+
*/
|
|
744
|
+
constructor(appName) {
|
|
745
|
+
this.#appName = appName;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Set custom storage for user data persistence.
|
|
749
|
+
* Defaults to in-memory storage if not set.
|
|
750
|
+
*/
|
|
751
|
+
withStorage(storage) {
|
|
752
|
+
this.#storage = storage;
|
|
753
|
+
return this;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Set custom event emitter for user state changes.
|
|
757
|
+
* Defaults to in-memory event emitter if not set.
|
|
758
|
+
*/
|
|
759
|
+
withEventEmitter(eventEmitter) {
|
|
760
|
+
this.#eventEmitter = eventEmitter;
|
|
761
|
+
return this;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Set a pre-built crypto instance.
|
|
765
|
+
* Either crypto or keyPairStorage must be provided.
|
|
766
|
+
*/
|
|
767
|
+
withCrypto(crypto) {
|
|
768
|
+
this.#crypto = crypto;
|
|
769
|
+
return this;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Set key pair storage for cryptographic keys.
|
|
773
|
+
* A crypto instance will be built from this storage.
|
|
774
|
+
* Either crypto or keyPairStorage must be provided.
|
|
775
|
+
*/
|
|
776
|
+
withKeyPairStorage(keyPairStorage) {
|
|
777
|
+
this.#keyPairStorage = keyPairStorage;
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Set the Renown server URL for credential verification.
|
|
782
|
+
* Defaults to https://www.renown.id
|
|
783
|
+
*/
|
|
784
|
+
withBaseUrl(baseUrl) {
|
|
785
|
+
this.#baseUrl = baseUrl;
|
|
786
|
+
return this;
|
|
787
|
+
}
|
|
788
|
+
withSwitchboardUrl(switchboardUrl) {
|
|
789
|
+
this.#switchboardUrl = switchboardUrl;
|
|
790
|
+
return this;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Set a profile fetcher strategy for enriching user data after login.
|
|
794
|
+
* The fetcher receives the authenticated user and the base URL,
|
|
795
|
+
* and returns a RenownProfile. Called in the background after each login.
|
|
796
|
+
* Defaults to fetchRenownProfile which calls the Renown API.
|
|
797
|
+
*/
|
|
798
|
+
withProfileFetcher(profileFetcher) {
|
|
799
|
+
this.#profileFetcher = profileFetcher;
|
|
800
|
+
return this;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Build and initialize the Renown instance.
|
|
804
|
+
* If a user is stored, attempts to re-authenticate them.
|
|
805
|
+
* @throws Error if neither crypto nor keyPairStorage is provided
|
|
806
|
+
*/
|
|
807
|
+
async build() {
|
|
808
|
+
if (!this.#crypto && !this.#keyPairStorage) throw new Error("Either crypto or keyPairStorage is required. Use withCrypto() or withKeyPairStorage() to set one.");
|
|
809
|
+
const crypto = this.#crypto ?? await new RenownCryptoBuilder().withKeyPairStorage(this.#keyPairStorage).build();
|
|
810
|
+
const storage = this.#storage ?? new RenownMemoryStorage();
|
|
811
|
+
const eventEmitter = this.#eventEmitter ?? new MemoryEventEmitter();
|
|
812
|
+
const baseUrl = this.#baseUrl ?? "https://www.renown.id";
|
|
813
|
+
const switchboardEndpoint = await resolveSwitchboardEndpoint({
|
|
814
|
+
switchboardUrl: this.#switchboardUrl,
|
|
815
|
+
baseUrl
|
|
816
|
+
});
|
|
817
|
+
const switchboard = switchboardEndpoint ? new SwitchboardClient(switchboardEndpoint) : void 0;
|
|
818
|
+
const profileFetcher = this.#profileFetcher ?? (switchboard ? switchboard.profileFetcher : fetchRenownProfile);
|
|
819
|
+
return new Renown(storage, eventEmitter, crypto, this.#appName, baseUrl, profileFetcher, switchboard);
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Create a BaseRenownBuilder from options object for a more concise API
|
|
823
|
+
*/
|
|
824
|
+
static from(options) {
|
|
825
|
+
const builder = new BaseRenownBuilder(options.appName);
|
|
826
|
+
if (options.storage) builder.withStorage(options.storage);
|
|
827
|
+
if (options.eventEmitter) builder.withEventEmitter(options.eventEmitter);
|
|
828
|
+
if (options.crypto) builder.withCrypto(options.crypto);
|
|
829
|
+
if (options.keyPairStorage) builder.withKeyPairStorage(options.keyPairStorage);
|
|
830
|
+
if (options.baseUrl) builder.withBaseUrl(options.baseUrl);
|
|
831
|
+
if (options.switchboardUrl) builder.withSwitchboardUrl(options.switchboardUrl);
|
|
832
|
+
if (options.profileFetcher) builder.withProfileFetcher(options.profileFetcher);
|
|
833
|
+
return builder;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
//#endregion
|
|
837
|
+
export { VERIFIABLE_CREDENTIAL_EIP712_TYPE as S, DEFAULT_RENOWN_NETWORK_ID as _, BaseStorage as a, ISSUER_TYPE as b, recoverCredentialSigner as c, SwitchboardClient as d, resolveSwitchboardEndpoint as f, DEFAULT_RENOWN_CHAIN_ID as g, CREDENTIAL_TYPES as h, RenownMemoryStorage as i, verifyAuthCredential as l, CREDENTIAL_SUBJECT_TYPE as m, fetchRenownProfile as n, buildAndSignCredential as o, CREDENTIAL_SCHEMA_EIP712_TYPE as p, Renown as r, fetchDelegationCredential as s, BaseRenownBuilder as t, verifyCredentialSignature as u, DEFAULT_RENOWN_URL as v, RENOWN_SESSION_COOKIE as x, DOMAIN_TYPE as y };
|
|
838
|
+
|
|
839
|
+
//# sourceMappingURL=renown-builder-Cm5JNOul.js.map
|