@renown/sdk 6.2.3-dev.5 → 6.2.3-dev.50
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/{browser-key-storage-e3RQsnLS.js → browser-key-storage-wjSwTnhs.js} +3 -2
- package/dist/browser-key-storage-wjSwTnhs.js.map +1 -0
- package/dist/{constants-BexAR53F.js → constants-DVOF17NB.js} +1 -1
- package/dist/{constants-BexAR53F.js.map → constants-DVOF17NB.js.map} +1 -1
- package/dist/crypto.d.ts +4 -3
- package/dist/crypto.js +2 -2
- package/dist/{index-BLIpkdF7.d.ts → index-DbiBjqs6.d.ts} +11 -20
- package/dist/index-DbiBjqs6.d.ts.map +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/{meta-D-5kvwx8.js → meta-4NmijIbE.js} +2 -2
- package/dist/{meta-D-5kvwx8.js.map → meta-4NmijIbE.js.map} +1 -1
- package/dist/{meta-CHgd9Qd0.js → meta-B2fHYlPD.js} +2 -2
- package/dist/{meta-CHgd9Qd0.js.map → meta-B2fHYlPD.js.map} +1 -1
- package/dist/{meta-BesJP74N.d.ts → meta-Dpmn2Ywp.d.ts} +3 -3
- package/dist/meta-Dpmn2Ywp.d.ts.map +1 -0
- package/dist/{meta-bV7L-1yb.d.ts → meta-qadFdiX7.d.ts} +3 -3
- package/dist/meta-qadFdiX7.d.ts.map +1 -0
- package/dist/node.d.ts +42 -25
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +23 -7
- package/dist/node.js.map +1 -1
- package/dist/signed-message-Cp5oRPLN.js +468 -0
- package/dist/signed-message-Cp5oRPLN.js.map +1 -0
- package/dist/{signer-DrjINLqt.js → signer-GzTlZLjR.js} +38 -106
- package/dist/signer-GzTlZLjR.js.map +1 -0
- package/dist/signer-trust-FmbMTn39.d.ts +53 -0
- package/dist/signer-trust-FmbMTn39.d.ts.map +1 -0
- package/dist/signer-trust-H1rNzUcd.js +708 -0
- package/dist/signer-trust-H1rNzUcd.js.map +1 -0
- package/dist/trust.d.ts +3 -0
- package/dist/trust.js +2 -0
- package/dist/{types-C-hJrsQg.d.ts → types-7xgrEnx7.d.ts} +3 -3
- package/dist/types-7xgrEnx7.d.ts.map +1 -0
- package/dist/{types-BGQKPoUr.d.ts → types-Ceiz2dkk.d.ts} +36 -12
- package/dist/types-Ceiz2dkk.d.ts.map +1 -0
- package/dist/{types-DInveFLz.d.ts → types-DRnq2URw.d.ts} +3 -3
- package/dist/types-DRnq2URw.d.ts.map +1 -0
- package/dist/{types-C_qc_62W.js → types-Dwy6khXb.js} +1 -1
- package/dist/{types-C_qc_62W.js.map → types-Dwy6khXb.js.map} +1 -1
- package/dist/utils-DLQ5VVqT.js +75 -0
- package/dist/utils-DLQ5VVqT.js.map +1 -0
- package/dist/{utils-DtWREYeO.d.ts → utils-DkVxP7Ug.d.ts} +19 -12
- package/dist/utils-DkVxP7Ug.d.ts.map +1 -0
- package/dist/wallet/index.d.ts +5 -5
- package/dist/wallet/index.d.ts.map +1 -1
- package/dist/wallet/index.js +1 -1
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/mock/factory.d.ts +4 -5
- package/dist/wallet/mock/factory.d.ts.map +1 -1
- package/dist/wallet/mock/factory.js +1 -1
- package/dist/wallet/mock/factory.js.map +1 -1
- package/dist/wallet/mock/index.d.ts +5 -5
- package/dist/wallet/mock/index.d.ts.map +1 -1
- package/dist/wallet/mock/index.js +1 -1
- package/dist/wallet/mock/index.js.map +1 -1
- package/dist/wallet/privy/factory.d.ts +9 -11
- package/dist/wallet/privy/factory.d.ts.map +1 -1
- package/dist/wallet/privy/factory.js +2 -2
- package/dist/wallet/privy/factory.js.map +1 -1
- package/dist/wallet/privy/index.d.ts +5 -5
- package/dist/wallet/privy/index.d.ts.map +1 -1
- package/dist/wallet/privy/index.js +1 -1
- package/dist/wallet/privy/index.js.map +1 -1
- package/dist/wallet/rainbow/factory.d.ts +5 -4
- package/dist/wallet/rainbow/factory.d.ts.map +1 -1
- package/dist/wallet/rainbow/factory.js +2 -2
- package/dist/wallet/rainbow/factory.js.map +1 -1
- package/dist/wallet/rainbow/index.d.ts +5 -5
- package/dist/wallet/rainbow/index.d.ts.map +1 -1
- package/dist/wallet/rainbow/index.js +1 -1
- package/dist/wallet/rainbow/index.js.map +1 -1
- package/package.json +12 -5
- package/dist/browser-key-storage-e3RQsnLS.js.map +0 -1
- package/dist/index-BLIpkdF7.d.ts.map +0 -1
- package/dist/local-credential-verifier-CU8hmFlR.js +0 -907
- package/dist/local-credential-verifier-CU8hmFlR.js.map +0 -1
- package/dist/meta-BesJP74N.d.ts.map +0 -1
- package/dist/meta-bV7L-1yb.d.ts.map +0 -1
- package/dist/signer-DrjINLqt.js.map +0 -1
- package/dist/types-BGQKPoUr.d.ts.map +0 -1
- package/dist/types-C-hJrsQg.d.ts.map +0 -1
- package/dist/types-DInveFLz.d.ts.map +0 -1
- package/dist/utils-DtWREYeO.d.ts.map +0 -1
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
import { a as DEFAULT_RENOWN_NETWORK_ID, r as CREDENTIAL_TYPES } from "./constants-DVOF17NB.js";
|
|
2
|
+
import { a as verifyAuthBearerToken } from "./utils-DLQ5VVqT.js";
|
|
3
|
+
import { recoverTypedDataAddress } from "viem";
|
|
4
|
+
import { createAction } from "@powerhousedao/shared/document-model";
|
|
5
|
+
//#region src/discovery.ts
|
|
6
|
+
const DEFAULT_DISCOVERY_TIMEOUT_MS = 3e3;
|
|
7
|
+
async function resolveSwitchboardEndpoint(options) {
|
|
8
|
+
const { switchboardUrl, baseUrl, timeoutMs } = options;
|
|
9
|
+
if (switchboardUrl) return switchboardUrl;
|
|
10
|
+
if (!baseUrl) return;
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS);
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL("/api/switchboard", baseUrl);
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
method: "GET",
|
|
17
|
+
signal: controller.signal
|
|
18
|
+
});
|
|
19
|
+
if (!response.ok) return;
|
|
20
|
+
return (await response.json()).endpoint || void 0;
|
|
21
|
+
} catch {
|
|
22
|
+
return;
|
|
23
|
+
} finally {
|
|
24
|
+
clearTimeout(timeout);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/switchboard.ts
|
|
29
|
+
/** Thrown by `signIn` when the instance has no switchboard to write the
|
|
30
|
+
* credential to, so a host can fall back to the Renown redirect flow. */
|
|
31
|
+
var MissingSwitchboardError = class MissingSwitchboardError extends Error {
|
|
32
|
+
constructor() {
|
|
33
|
+
super("signIn requires a switchboard endpoint. Set switchboardUrl or a discoverable baseUrl.");
|
|
34
|
+
this.name = "MissingSwitchboardError";
|
|
35
|
+
}
|
|
36
|
+
/** `instanceof` fails across duplicate SDK copies, so match by name too. */
|
|
37
|
+
static is(error) {
|
|
38
|
+
return error instanceof MissingSwitchboardError || error instanceof Error && error.name === "MissingSwitchboardError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function userDriveId(address) {
|
|
42
|
+
return `renown-${address.toLowerCase()}`;
|
|
43
|
+
}
|
|
44
|
+
const CREDENTIALS_QUERY = `
|
|
45
|
+
query RenownCredentials($input: RenownCredentialsInput!) {
|
|
46
|
+
renownCredentials(input: $input) {
|
|
47
|
+
documentId
|
|
48
|
+
credentialId
|
|
49
|
+
context
|
|
50
|
+
type
|
|
51
|
+
issuerId
|
|
52
|
+
issuerEthereumAddress
|
|
53
|
+
issuanceDate
|
|
54
|
+
expirationDate
|
|
55
|
+
credentialSubjectId
|
|
56
|
+
credentialSubjectApp
|
|
57
|
+
credentialStatusId
|
|
58
|
+
credentialStatusType
|
|
59
|
+
credentialSchemaId
|
|
60
|
+
credentialSchemaType
|
|
61
|
+
proofVerificationMethod
|
|
62
|
+
proofEthereumAddress
|
|
63
|
+
proofCreated
|
|
64
|
+
proofPurpose
|
|
65
|
+
proofType
|
|
66
|
+
proofValue
|
|
67
|
+
proofEip712Domain
|
|
68
|
+
proofEip712PrimaryType
|
|
69
|
+
revoked
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
const USERS_QUERY = `
|
|
74
|
+
query RenownUsers($input: RenownUsersInput!) {
|
|
75
|
+
renownUsers(input: $input) {
|
|
76
|
+
documentId
|
|
77
|
+
username
|
|
78
|
+
ethAddress
|
|
79
|
+
userImage
|
|
80
|
+
createdAt
|
|
81
|
+
updatedAt
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
const CREATE_EMPTY_DOCUMENT_MUTATION = `
|
|
86
|
+
mutation CreateEmptyDocument(
|
|
87
|
+
$documentType: String!
|
|
88
|
+
$parentIdentifier: String
|
|
89
|
+
) {
|
|
90
|
+
createEmptyDocument(
|
|
91
|
+
documentType: $documentType
|
|
92
|
+
parentIdentifier: $parentIdentifier
|
|
93
|
+
) {
|
|
94
|
+
id
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const MUTATE_DOCUMENT_MUTATION = `
|
|
99
|
+
mutation MutateDocument(
|
|
100
|
+
$documentIdentifier: String!
|
|
101
|
+
$actions: [ActionInput!]!
|
|
102
|
+
) {
|
|
103
|
+
mutateDocument: execute(
|
|
104
|
+
documentIdentifier: $documentIdentifier
|
|
105
|
+
actions: $actions
|
|
106
|
+
) {
|
|
107
|
+
id
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
const LEGACY_MUTATE_DOCUMENT_MUTATION = `
|
|
112
|
+
mutation MutateDocument(
|
|
113
|
+
$documentIdentifier: String!
|
|
114
|
+
$actions: [JSONObject!]!
|
|
115
|
+
) {
|
|
116
|
+
mutateDocument(documentIdentifier: $documentIdentifier, actions: $actions) {
|
|
117
|
+
id
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
function isUnknownExecuteField(error) {
|
|
122
|
+
return error instanceof Error && /Cannot query field "execute" on type "Mutation"/.test(error.message);
|
|
123
|
+
}
|
|
124
|
+
const ISSUE_CREDENTIAL_MUTATION = `
|
|
125
|
+
mutation RenownIssueCredential(
|
|
126
|
+
$input: RenownCredential_InitInput!
|
|
127
|
+
$username: String
|
|
128
|
+
$userImage: String
|
|
129
|
+
$userDocId: PHID
|
|
130
|
+
) {
|
|
131
|
+
renown_issueCredential(
|
|
132
|
+
input: $input
|
|
133
|
+
username: $username
|
|
134
|
+
userImage: $userImage
|
|
135
|
+
userDocId: $userDocId
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
const UPSERT_PROFILE_MUTATION = `
|
|
140
|
+
mutation RenownUpsertProfile(
|
|
141
|
+
$address: String!
|
|
142
|
+
$username: String
|
|
143
|
+
$userImage: String
|
|
144
|
+
$signature: String
|
|
145
|
+
$timestamp: String
|
|
146
|
+
) {
|
|
147
|
+
renown_upsertProfile(
|
|
148
|
+
address: $address
|
|
149
|
+
username: $username
|
|
150
|
+
userImage: $userImage
|
|
151
|
+
signature: $signature
|
|
152
|
+
timestamp: $timestamp
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
const REVOKE_CREDENTIAL_MUTATION = `
|
|
157
|
+
mutation RenownRevokeCredential(
|
|
158
|
+
$credentialId: String!
|
|
159
|
+
$signature: String
|
|
160
|
+
$timestamp: String
|
|
161
|
+
) {
|
|
162
|
+
renown_revokeCredential(
|
|
163
|
+
credentialId: $credentialId
|
|
164
|
+
signature: $signature
|
|
165
|
+
timestamp: $timestamp
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
function authParts(auth) {
|
|
170
|
+
return "token" in auth ? {
|
|
171
|
+
token: auth.token,
|
|
172
|
+
signed: {}
|
|
173
|
+
} : { signed: {
|
|
174
|
+
signature: auth.signature,
|
|
175
|
+
timestamp: auth.timestamp
|
|
176
|
+
} };
|
|
177
|
+
}
|
|
178
|
+
const RENOWN_CREDENTIAL_DOC_TYPE = "powerhouse/renown-credential";
|
|
179
|
+
const RENOWN_USER_DOC_TYPE = "powerhouse/renown-user";
|
|
180
|
+
function quoted(name) {
|
|
181
|
+
return `\\\\?"${name}\\\\?"`;
|
|
182
|
+
}
|
|
183
|
+
function isUnknownSchemaError(error, field, ...types) {
|
|
184
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
185
|
+
if (new RegExp(`Cannot query field ${quoted(field)}`).test(message)) return true;
|
|
186
|
+
return !message.includes("got invalid value") && types.some((type) => new RegExp(`Unknown type ${quoted(type)}`).test(message));
|
|
187
|
+
}
|
|
188
|
+
function reshapeCredential(row) {
|
|
189
|
+
const domain = JSON.parse(row.proofEip712Domain);
|
|
190
|
+
return {
|
|
191
|
+
"@context": row.context,
|
|
192
|
+
type: row.type,
|
|
193
|
+
id: row.credentialId,
|
|
194
|
+
issuer: {
|
|
195
|
+
id: row.issuerId,
|
|
196
|
+
ethereumAddress: row.issuerEthereumAddress
|
|
197
|
+
},
|
|
198
|
+
credentialSubject: {
|
|
199
|
+
id: row.credentialSubjectId ?? "",
|
|
200
|
+
app: row.credentialSubjectApp
|
|
201
|
+
},
|
|
202
|
+
credentialSchema: {
|
|
203
|
+
id: row.credentialSchemaId,
|
|
204
|
+
type: row.credentialSchemaType
|
|
205
|
+
},
|
|
206
|
+
...row.credentialStatusId ? { credentialStatus: {
|
|
207
|
+
id: row.credentialStatusId,
|
|
208
|
+
type: row.credentialStatusType ?? ""
|
|
209
|
+
} } : {},
|
|
210
|
+
issuanceDate: row.issuanceDate,
|
|
211
|
+
expirationDate: row.expirationDate ?? "",
|
|
212
|
+
proof: {
|
|
213
|
+
type: row.proofType,
|
|
214
|
+
created: row.proofCreated,
|
|
215
|
+
verificationMethod: row.proofVerificationMethod,
|
|
216
|
+
proofPurpose: row.proofPurpose,
|
|
217
|
+
proofValue: row.proofValue,
|
|
218
|
+
ethereumAddress: row.proofEthereumAddress,
|
|
219
|
+
eip712: {
|
|
220
|
+
domain,
|
|
221
|
+
types: CREDENTIAL_TYPES,
|
|
222
|
+
primaryType: "VerifiableCredential"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function issuerChainId(issuerId) {
|
|
228
|
+
return issuerId.split(":")[3];
|
|
229
|
+
}
|
|
230
|
+
function issuerDidAddress(issuerId) {
|
|
231
|
+
return issuerId.split(":")[4];
|
|
232
|
+
}
|
|
233
|
+
function bindsTo$1(row, address, chainId, appDid) {
|
|
234
|
+
return !row.revoked && issuedTo(row, address, chainId, appDid);
|
|
235
|
+
}
|
|
236
|
+
function issuedTo(row, address, chainId, appDid) {
|
|
237
|
+
const expected = address.toLowerCase();
|
|
238
|
+
return row.credentialSubjectId === appDid && issuerChainId(row.issuerId) === String(chainId) && issuerDidAddress(row.issuerId)?.toLowerCase() === expected && row.issuerEthereumAddress.toLowerCase() === expected;
|
|
239
|
+
}
|
|
240
|
+
var SwitchboardClient = class {
|
|
241
|
+
#endpoint;
|
|
242
|
+
#transport;
|
|
243
|
+
#legacyMutate = false;
|
|
244
|
+
constructor(source) {
|
|
245
|
+
if (typeof source === "string") {
|
|
246
|
+
this.#endpoint = source;
|
|
247
|
+
this.#transport = (query, variables, options) => this.#post(source, query, variables, options?.token);
|
|
248
|
+
} else this.#transport = source;
|
|
249
|
+
}
|
|
250
|
+
/** The HTTP endpoint, or undefined when reading through a local executor. */
|
|
251
|
+
get endpoint() {
|
|
252
|
+
return this.#endpoint;
|
|
253
|
+
}
|
|
254
|
+
async #post(endpoint, query, variables, token) {
|
|
255
|
+
const response = await fetch(endpoint, {
|
|
256
|
+
method: "POST",
|
|
257
|
+
headers: {
|
|
258
|
+
"Content-Type": "application/json",
|
|
259
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
260
|
+
},
|
|
261
|
+
body: JSON.stringify({
|
|
262
|
+
query,
|
|
263
|
+
variables
|
|
264
|
+
})
|
|
265
|
+
});
|
|
266
|
+
const body = await response.json().catch(() => void 0);
|
|
267
|
+
if (body?.errors?.length) throw new Error(body.errors.map((e) => e.message).join("; "));
|
|
268
|
+
if (!response.ok || !body) throw new Error(`Switchboard request failed: ${response.status}`);
|
|
269
|
+
return body.data;
|
|
270
|
+
}
|
|
271
|
+
async #request(query, variables, token) {
|
|
272
|
+
const data = await (token ? this.#transport(query, variables, { token }) : this.#transport(query, variables));
|
|
273
|
+
if (!data) throw new Error("Switchboard returned no data");
|
|
274
|
+
return data;
|
|
275
|
+
}
|
|
276
|
+
async getCredential(params) {
|
|
277
|
+
const { address, chainId, appDid } = params;
|
|
278
|
+
const { renownCredentials } = await this.#request(CREDENTIALS_QUERY, { input: {
|
|
279
|
+
driveId: userDriveId(address),
|
|
280
|
+
ethAddress: address.toLowerCase(),
|
|
281
|
+
did: appDid,
|
|
282
|
+
includeRevoked: false
|
|
283
|
+
} });
|
|
284
|
+
const now = Date.now();
|
|
285
|
+
const mostRecent = renownCredentials.filter((row) => bindsTo$1(row, address, chainId, appDid)).filter((row) => {
|
|
286
|
+
if (!row.expirationDate) return true;
|
|
287
|
+
const expiresAt = Date.parse(row.expirationDate);
|
|
288
|
+
return !Number.isNaN(expiresAt) && expiresAt > now;
|
|
289
|
+
}).sort((a, b) => Date.parse(b.issuanceDate) - Date.parse(a.issuanceDate)).at(0);
|
|
290
|
+
return mostRecent ? reshapeCredential(mostRecent) : void 0;
|
|
291
|
+
}
|
|
292
|
+
async getIssuedCredentials(params) {
|
|
293
|
+
const { address, chainId, appDid } = params;
|
|
294
|
+
const { renownCredentials } = await this.#request(CREDENTIALS_QUERY, { input: {
|
|
295
|
+
driveId: userDriveId(address),
|
|
296
|
+
ethAddress: address.toLowerCase(),
|
|
297
|
+
did: appDid,
|
|
298
|
+
includeRevoked: true
|
|
299
|
+
} });
|
|
300
|
+
const credentials = [];
|
|
301
|
+
for (const row of renownCredentials) {
|
|
302
|
+
if (!issuedTo(row, address, chainId, appDid)) continue;
|
|
303
|
+
try {
|
|
304
|
+
credentials.push(reshapeCredential(row));
|
|
305
|
+
} catch {}
|
|
306
|
+
}
|
|
307
|
+
return credentials;
|
|
308
|
+
}
|
|
309
|
+
async getProfileByAddress(address) {
|
|
310
|
+
const { renownUsers } = await this.#request(USERS_QUERY, { input: {
|
|
311
|
+
driveId: userDriveId(address),
|
|
312
|
+
ethAddresses: [address.toLowerCase()]
|
|
313
|
+
} });
|
|
314
|
+
const user = renownUsers.at(0);
|
|
315
|
+
if (!user) return void 0;
|
|
316
|
+
return {
|
|
317
|
+
documentId: user.documentId,
|
|
318
|
+
username: user.username,
|
|
319
|
+
ethAddress: user.ethAddress,
|
|
320
|
+
userImage: user.userImage,
|
|
321
|
+
createdAt: user.createdAt,
|
|
322
|
+
updatedAt: user.updatedAt
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async createEmptyDocument(documentType, parentIdentifier, token) {
|
|
326
|
+
const { createEmptyDocument } = await this.#request(CREATE_EMPTY_DOCUMENT_MUTATION, {
|
|
327
|
+
documentType,
|
|
328
|
+
parentIdentifier
|
|
329
|
+
}, token);
|
|
330
|
+
return createEmptyDocument.id;
|
|
331
|
+
}
|
|
332
|
+
async mutateDocument(documentIdentifier, actions, token) {
|
|
333
|
+
const variables = {
|
|
334
|
+
documentIdentifier,
|
|
335
|
+
actions
|
|
336
|
+
};
|
|
337
|
+
if (!this.#legacyMutate) try {
|
|
338
|
+
const { mutateDocument } = await this.#request(MUTATE_DOCUMENT_MUTATION, variables, token);
|
|
339
|
+
return mutateDocument.id;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (!isUnknownExecuteField(error)) throw error;
|
|
342
|
+
this.#legacyMutate = true;
|
|
343
|
+
}
|
|
344
|
+
const { mutateDocument } = await this.#request(LEGACY_MUTATE_DOCUMENT_MUTATION, variables, token);
|
|
345
|
+
return mutateDocument.id;
|
|
346
|
+
}
|
|
347
|
+
async issueCredential(credential, parentIdentifier) {
|
|
348
|
+
const input = {
|
|
349
|
+
id: credential.id,
|
|
350
|
+
context: credential["@context"],
|
|
351
|
+
type: credential.type,
|
|
352
|
+
issuer: {
|
|
353
|
+
id: credential.issuer.id,
|
|
354
|
+
ethereumAddress: credential.issuer.ethereumAddress
|
|
355
|
+
},
|
|
356
|
+
credentialSubject: {
|
|
357
|
+
id: credential.credentialSubject.id,
|
|
358
|
+
app: credential.credentialSubject.app
|
|
359
|
+
},
|
|
360
|
+
credentialSchema: {
|
|
361
|
+
id: credential.credentialSchema.id,
|
|
362
|
+
type: credential.credentialSchema.type
|
|
363
|
+
},
|
|
364
|
+
issuanceDate: credential.issuanceDate,
|
|
365
|
+
expirationDate: credential.expirationDate || void 0,
|
|
366
|
+
proof: {
|
|
367
|
+
type: credential.proof.type,
|
|
368
|
+
created: credential.proof.created,
|
|
369
|
+
verificationMethod: credential.proof.verificationMethod,
|
|
370
|
+
proofPurpose: credential.proof.proofPurpose,
|
|
371
|
+
proofValue: credential.proof.proofValue,
|
|
372
|
+
ethereumAddress: credential.proof.ethereumAddress,
|
|
373
|
+
eip712: {
|
|
374
|
+
domain: {
|
|
375
|
+
version: credential.proof.eip712.domain.version,
|
|
376
|
+
chainId: credential.proof.eip712.domain.chainId
|
|
377
|
+
},
|
|
378
|
+
primaryType: credential.proof.eip712.primaryType
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
try {
|
|
383
|
+
const { renown_issueCredential } = await this.#request(ISSUE_CREDENTIAL_MUTATION, { input });
|
|
384
|
+
return renown_issueCredential;
|
|
385
|
+
} catch (error) {
|
|
386
|
+
if (!isUnknownSchemaError(error, "renown_issueCredential", "RenownCredential_InitInput")) throw error;
|
|
387
|
+
const documentId = await this.createEmptyDocument(RENOWN_CREDENTIAL_DOC_TYPE, parentIdentifier);
|
|
388
|
+
await this.mutateDocument(documentId, [createAction("INIT", input)]);
|
|
389
|
+
return documentId;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async upsertUserProfile(address, profile, auth) {
|
|
393
|
+
const { token, signed } = authParts(auth);
|
|
394
|
+
try {
|
|
395
|
+
const { renown_upsertProfile } = await this.#request(UPSERT_PROFILE_MUTATION, {
|
|
396
|
+
address,
|
|
397
|
+
username: profile.username,
|
|
398
|
+
userImage: profile.userImage,
|
|
399
|
+
...signed
|
|
400
|
+
}, token);
|
|
401
|
+
return renown_upsertProfile;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (!isUnknownSchemaError(error, "renown_upsertProfile")) throw error;
|
|
404
|
+
return this.#legacyUpsertUserProfile(address, profile, token);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async #legacyUpsertUserProfile(address, profile, token) {
|
|
408
|
+
const updates = [];
|
|
409
|
+
if (profile.username != null) updates.push(createAction("SET_USERNAME", { username: profile.username }));
|
|
410
|
+
if (profile.userImage != null) updates.push(createAction("SET_USER_IMAGE", { userImage: profile.userImage }));
|
|
411
|
+
const existing = await this.getProfileByAddress(address);
|
|
412
|
+
if (existing) {
|
|
413
|
+
if (updates.length) await this.mutateDocument(existing.documentId, updates, token);
|
|
414
|
+
return existing.documentId;
|
|
415
|
+
}
|
|
416
|
+
const documentId = await this.createEmptyDocument(RENOWN_USER_DOC_TYPE, void 0, token);
|
|
417
|
+
await this.mutateDocument(documentId, [createAction("SET_ETH_ADDRESS", { ethAddress: address }), ...updates], token);
|
|
418
|
+
return documentId;
|
|
419
|
+
}
|
|
420
|
+
async revokeCredential(credentialId, auth) {
|
|
421
|
+
const { token, signed } = authParts(auth);
|
|
422
|
+
try {
|
|
423
|
+
await this.#request(REVOKE_CREDENTIAL_MUTATION, {
|
|
424
|
+
credentialId,
|
|
425
|
+
...signed
|
|
426
|
+
}, token);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (!isUnknownSchemaError(error, "renown_revokeCredential")) throw error;
|
|
429
|
+
await this.#legacyRevokeCredential(credentialId, token);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async #legacyRevokeCredential(credentialId, token) {
|
|
433
|
+
const { renownCredentials } = await this.#request(CREDENTIALS_QUERY, { input: { includeRevoked: false } }, token);
|
|
434
|
+
const documentIds = renownCredentials.filter((row) => row.credentialId === credentialId).map((row) => row.documentId);
|
|
435
|
+
if (!documentIds.length) throw new Error(`Credential not found: ${credentialId}`);
|
|
436
|
+
const revokedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
437
|
+
for (const documentId of documentIds) await this.mutateDocument(documentId, [createAction("REVOKE", {
|
|
438
|
+
revokedAt,
|
|
439
|
+
reason: null
|
|
440
|
+
})], token);
|
|
441
|
+
}
|
|
442
|
+
profileFetcher = (user) => this.getProfileByAddress(user.address).catch(() => void 0);
|
|
443
|
+
};
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/credential.ts
|
|
446
|
+
/** Assemble a Renown delegation VC and sign it with the provided signer. */
|
|
447
|
+
async function buildAndSignCredential(params) {
|
|
448
|
+
const { signTypedData, address, chainId, app, appId, expiresInDays = 7 } = params;
|
|
449
|
+
const issuerId = `did:pkh:${DEFAULT_RENOWN_NETWORK_ID}:${chainId}:${address.toLowerCase()}`;
|
|
450
|
+
const credentialId = `urn:uuid:${globalThis.crypto.randomUUID()}`;
|
|
451
|
+
const now = /* @__PURE__ */ new Date();
|
|
452
|
+
const expirationDate = new Date(now.getTime() + expiresInDays * 24 * 60 * 60 * 1e3);
|
|
453
|
+
const message = {
|
|
454
|
+
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
|
455
|
+
type: ["VerifiableCredential", "RenownCredential"],
|
|
456
|
+
id: credentialId,
|
|
457
|
+
issuer: {
|
|
458
|
+
id: issuerId,
|
|
459
|
+
ethereumAddress: address
|
|
460
|
+
},
|
|
461
|
+
credentialSubject: {
|
|
462
|
+
id: appId,
|
|
463
|
+
app
|
|
464
|
+
},
|
|
465
|
+
credentialSchema: {
|
|
466
|
+
id: "https://renown.id/schemas/renown-credential/v1",
|
|
467
|
+
type: "JsonSchemaValidator2018"
|
|
468
|
+
},
|
|
469
|
+
issuanceDate: now.toISOString(),
|
|
470
|
+
expirationDate: expirationDate.toISOString()
|
|
471
|
+
};
|
|
472
|
+
const domain = {
|
|
473
|
+
version: "1",
|
|
474
|
+
chainId
|
|
475
|
+
};
|
|
476
|
+
const { EIP712Domain, ...signTypes } = CREDENTIAL_TYPES;
|
|
477
|
+
const signature = await signTypedData({
|
|
478
|
+
domain,
|
|
479
|
+
types: signTypes,
|
|
480
|
+
primaryType: "VerifiableCredential",
|
|
481
|
+
message
|
|
482
|
+
});
|
|
483
|
+
return {
|
|
484
|
+
...message,
|
|
485
|
+
proof: {
|
|
486
|
+
type: "EthereumEip712Signature2021",
|
|
487
|
+
created: message.issuanceDate,
|
|
488
|
+
verificationMethod: issuerId,
|
|
489
|
+
proofPurpose: "assertionMethod",
|
|
490
|
+
proofValue: signature,
|
|
491
|
+
ethereumAddress: address,
|
|
492
|
+
eip712: {
|
|
493
|
+
domain,
|
|
494
|
+
types: CREDENTIAL_TYPES,
|
|
495
|
+
primaryType: "VerifiableCredential"
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/** Recover the address that signed a credential's EIP-712 proof. */
|
|
501
|
+
async function recoverCredentialSigner(credential) {
|
|
502
|
+
const { proof, ...message } = credential;
|
|
503
|
+
const { EIP712Domain, ...types } = CREDENTIAL_TYPES;
|
|
504
|
+
return recoverTypedDataAddress({
|
|
505
|
+
domain: proof.eip712.domain,
|
|
506
|
+
types,
|
|
507
|
+
primaryType: "VerifiableCredential",
|
|
508
|
+
message,
|
|
509
|
+
signature: proof.proofValue
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
/** True when the proof was signed by the credential's declared issuer address. */
|
|
513
|
+
async function verifyCredentialSignature(credential) {
|
|
514
|
+
try {
|
|
515
|
+
return (await recoverCredentialSigner(credential)).toLowerCase() === credential.issuer.ethereumAddress.toLowerCase();
|
|
516
|
+
} catch {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function withEip712Domain(credential, chainId) {
|
|
521
|
+
if (credential.proof.eip712?.domain) return credential;
|
|
522
|
+
return {
|
|
523
|
+
...credential,
|
|
524
|
+
proof: {
|
|
525
|
+
...credential.proof,
|
|
526
|
+
eip712: {
|
|
527
|
+
domain: {
|
|
528
|
+
version: "1",
|
|
529
|
+
chainId
|
|
530
|
+
},
|
|
531
|
+
types: CREDENTIAL_TYPES,
|
|
532
|
+
primaryType: "VerifiableCredential"
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
async function fetchDelegationCredentialRest(params) {
|
|
538
|
+
const { address, chainId, appDid, baseUrl } = params;
|
|
539
|
+
const url = new URL(`/api/auth/credential?address=${encodeURIComponent(address)}&chainId=${encodeURIComponent(chainId)}&connectId=${encodeURIComponent(appDid)}&appId=${encodeURIComponent(appDid)}`, baseUrl);
|
|
540
|
+
const response = await fetch(url, { method: "GET" });
|
|
541
|
+
if (!response.ok) return void 0;
|
|
542
|
+
const { credential } = await response.json();
|
|
543
|
+
if (!credential) return void 0;
|
|
544
|
+
const [, , , issuerChainId, issuerAddress] = credential.issuer.id.split(":");
|
|
545
|
+
if (credential.credentialSubject.id !== appDid || issuerChainId !== String(chainId) || issuerAddress.toLowerCase() !== address.toLowerCase() || credential.issuer.ethereumAddress.toLowerCase() !== address.toLowerCase()) return;
|
|
546
|
+
if (credential.expirationDate) {
|
|
547
|
+
const expiresAt = Date.parse(credential.expirationDate);
|
|
548
|
+
if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) return;
|
|
549
|
+
}
|
|
550
|
+
return credential;
|
|
551
|
+
}
|
|
552
|
+
/** True when the credential's EIP-712 proof, on `chainId`, was signed by its
|
|
553
|
+
* issuer address. Binding and expiry are the caller's to check. */
|
|
554
|
+
async function verifyDelegationProof(credential, chainId) {
|
|
555
|
+
const withDomain = withEip712Domain(credential, chainId);
|
|
556
|
+
return withDomain.proof.eip712.domain.chainId === chainId && await verifyCredentialSignature(withDomain);
|
|
557
|
+
}
|
|
558
|
+
async function finalizeCredential(credential, chainId, verifySignature) {
|
|
559
|
+
if (!verifySignature) return credential;
|
|
560
|
+
if (!await verifyDelegationProof(credential, chainId)) return;
|
|
561
|
+
return withEip712Domain(credential, chainId);
|
|
562
|
+
}
|
|
563
|
+
async function fetchDelegationCredential(options) {
|
|
564
|
+
const { address, chainId, appDid, switchboardUrl, switchboardRequest, discover = true, verifySignature = true } = options;
|
|
565
|
+
const explicitBaseUrl = options.baseUrl;
|
|
566
|
+
const baseUrl = explicitBaseUrl ?? "https://www.renown.id";
|
|
567
|
+
try {
|
|
568
|
+
const source = switchboardRequest ?? (switchboardUrl ? switchboardUrl : discover && explicitBaseUrl ? await resolveSwitchboardEndpoint({ baseUrl }) : void 0);
|
|
569
|
+
const credential = source ? await new SwitchboardClient(source).getCredential({
|
|
570
|
+
address,
|
|
571
|
+
chainId,
|
|
572
|
+
appDid
|
|
573
|
+
}) : await fetchDelegationCredentialRest({
|
|
574
|
+
address,
|
|
575
|
+
chainId,
|
|
576
|
+
appDid,
|
|
577
|
+
baseUrl
|
|
578
|
+
});
|
|
579
|
+
if (!credential) return void 0;
|
|
580
|
+
return await finalizeCredential(credential, chainId, verifySignature);
|
|
581
|
+
} catch {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
async function verifyAuthCredential(jwt, options = {}) {
|
|
586
|
+
const verified = await verifyAuthBearerToken(jwt, { audience: options.audience });
|
|
587
|
+
if (!verified) return void 0;
|
|
588
|
+
const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
|
|
589
|
+
const appDid = verified.issuer;
|
|
590
|
+
const credential = await fetchDelegationCredential({
|
|
591
|
+
address,
|
|
592
|
+
chainId,
|
|
593
|
+
appDid,
|
|
594
|
+
baseUrl: options.renownUrl,
|
|
595
|
+
switchboardUrl: options.switchboardUrl,
|
|
596
|
+
switchboardRequest: options.switchboardRequest,
|
|
597
|
+
verifySignature: options.verifySignature
|
|
598
|
+
});
|
|
599
|
+
if (!credential) return void 0;
|
|
600
|
+
return {
|
|
601
|
+
address,
|
|
602
|
+
chainId,
|
|
603
|
+
networkId,
|
|
604
|
+
did: credential.issuer.id,
|
|
605
|
+
appDid,
|
|
606
|
+
credential
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/signer-trust.ts
|
|
611
|
+
const DEFAULT_MAX_ACCEPTED = 1e4;
|
|
612
|
+
const DEFAULT_REFUSAL_TTL_MS = 6e4;
|
|
613
|
+
/**
|
|
614
|
+
* Accepts a key the user's EIP-712 Renown credential delegates to, ignoring
|
|
615
|
+
* expiry and revocation. Verdicts are cached per (address, key); this node's
|
|
616
|
+
* own key is checked before the cache and never cached.
|
|
617
|
+
*/
|
|
618
|
+
function createRenownTrustPolicy(options = {}) {
|
|
619
|
+
const maxAccepted = options.maxAccepted ?? DEFAULT_MAX_ACCEPTED;
|
|
620
|
+
const refusalTtlMs = options.refusalTtlMs ?? DEFAULT_REFUSAL_TTL_MS;
|
|
621
|
+
const client = options.switchboard ? new SwitchboardClient(options.switchboard) : void 0;
|
|
622
|
+
const renownUrl = options.renownUrl ?? "https://www.renown.id";
|
|
623
|
+
const accepted = /* @__PURE__ */ new Set();
|
|
624
|
+
const refusedUntil = /* @__PURE__ */ new Map();
|
|
625
|
+
const pending = /* @__PURE__ */ new Map();
|
|
626
|
+
const issued = (user, key) => client ? client.getIssuedCredentials({
|
|
627
|
+
address: user.address,
|
|
628
|
+
chainId: user.chainId,
|
|
629
|
+
appDid: key
|
|
630
|
+
}) : fetchIssuedCredentialRest(user, key, renownUrl);
|
|
631
|
+
async function lookup(user, key) {
|
|
632
|
+
for (const credential of await issued(user, key)) if (bindsTo(credential, user, key) && await verifyDelegationProof(credential, user.chainId)) return true;
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
function remember(entry, verdict) {
|
|
636
|
+
if (verdict) {
|
|
637
|
+
if (accepted.size >= maxAccepted) {
|
|
638
|
+
const oldest = accepted.values().next().value;
|
|
639
|
+
if (oldest !== void 0) accepted.delete(oldest);
|
|
640
|
+
}
|
|
641
|
+
accepted.add(entry);
|
|
642
|
+
refusedUntil.delete(entry);
|
|
643
|
+
} else refusedUntil.set(entry, Date.now() + refusalTtlMs);
|
|
644
|
+
}
|
|
645
|
+
return { authorizeSigner(signer, key) {
|
|
646
|
+
const user = signer.user;
|
|
647
|
+
const self = ownIdentity(options);
|
|
648
|
+
if (self && isSelf(self, user, key)) return Promise.resolve(true);
|
|
649
|
+
if (!user || !isLookupable(user, key)) return Promise.resolve(false);
|
|
650
|
+
const entry = `${user.networkId}|${user.chainId}|${user.address.toLowerCase()}|${key}`;
|
|
651
|
+
if (accepted.has(entry)) return Promise.resolve(true);
|
|
652
|
+
const until = refusedUntil.get(entry);
|
|
653
|
+
if (until !== void 0 && until > Date.now()) return Promise.resolve(false);
|
|
654
|
+
const inFlight = pending.get(entry);
|
|
655
|
+
if (inFlight) return inFlight;
|
|
656
|
+
const verdict = lookup(user, key).then((result) => {
|
|
657
|
+
pending.delete(entry);
|
|
658
|
+
remember(entry, result);
|
|
659
|
+
return result;
|
|
660
|
+
}, (error) => {
|
|
661
|
+
pending.delete(entry);
|
|
662
|
+
throw error;
|
|
663
|
+
});
|
|
664
|
+
pending.set(entry, verdict);
|
|
665
|
+
return verdict;
|
|
666
|
+
} };
|
|
667
|
+
}
|
|
668
|
+
function ownIdentity(options) {
|
|
669
|
+
if (!options.ownSigner) return options.self;
|
|
670
|
+
const { app, user } = options.ownSigner;
|
|
671
|
+
return {
|
|
672
|
+
key: app?.key ?? "",
|
|
673
|
+
user: {
|
|
674
|
+
address: user?.address ?? "",
|
|
675
|
+
networkId: user?.networkId ?? "",
|
|
676
|
+
chainId: user?.chainId ?? 0
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function isSelf(self, user, key) {
|
|
681
|
+
return self.key !== "" && key === self.key && (user?.address ?? "").toLowerCase() === self.user.address.toLowerCase() && (user?.networkId ?? "") === self.user.networkId && (user?.chainId ?? 0) === self.user.chainId;
|
|
682
|
+
}
|
|
683
|
+
function isLookupable(user, key) {
|
|
684
|
+
return key.startsWith("did:key:") && /^0x[0-9a-fA-F]{40}$/.test(user.address) && user.networkId !== "" && Number.isInteger(user.chainId) && user.chainId > 0;
|
|
685
|
+
}
|
|
686
|
+
function bindsTo(credential, user, key) {
|
|
687
|
+
const address = user.address.toLowerCase();
|
|
688
|
+
const issuer = credential.issuer.id.split(":");
|
|
689
|
+
if (issuer.length !== 5) return false;
|
|
690
|
+
const [scheme, method, networkId, chainId, issuerAddress] = issuer;
|
|
691
|
+
return credential.credentialSubject.id === key && scheme === "did" && method === "pkh" && networkId === user.networkId && chainId === String(user.chainId) && issuerAddress.toLowerCase() === address && credential.issuer.ethereumAddress.toLowerCase() === address;
|
|
692
|
+
}
|
|
693
|
+
async function fetchIssuedCredentialRest(user, key, renownUrl) {
|
|
694
|
+
const url = new URL("/api/auth/credential", renownUrl);
|
|
695
|
+
url.searchParams.set("address", user.address);
|
|
696
|
+
url.searchParams.set("chainId", String(user.chainId));
|
|
697
|
+
url.searchParams.set("connectId", key);
|
|
698
|
+
url.searchParams.set("appId", key);
|
|
699
|
+
const response = await fetch(url, { method: "GET" });
|
|
700
|
+
if (response.status === 404) return [];
|
|
701
|
+
if (!response.ok) throw new Error(`Renown credential lookup failed: ${response.status}`);
|
|
702
|
+
const { credential } = await response.json();
|
|
703
|
+
return credential ? [credential] : [];
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
706
|
+
export { verifyAuthCredential as a, MissingSwitchboardError as c, recoverCredentialSigner as i, SwitchboardClient as l, buildAndSignCredential as n, verifyCredentialSignature as o, fetchDelegationCredential as r, verifyDelegationProof as s, createRenownTrustPolicy as t, resolveSwitchboardEndpoint as u };
|
|
707
|
+
|
|
708
|
+
//# sourceMappingURL=signer-trust-H1rNzUcd.js.map
|