@renown/sdk 6.2.3-dev.5 → 6.2.3-dev.51
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,468 @@
|
|
|
1
|
+
import { a as DEFAULT_RENOWN_NETWORK_ID, o as DEFAULT_RENOWN_URL, r as CREDENTIAL_TYPES } from "./constants-DVOF17NB.js";
|
|
2
|
+
import { c as MissingSwitchboardError, l as SwitchboardClient, n as buildAndSignCredential, o as verifyCredentialSignature, r as fetchDelegationCredential, u as resolveSwitchboardEndpoint } from "./signer-trust-H1rNzUcd.js";
|
|
3
|
+
import { a as verifyAuthBearerToken, i as parsePkhDid } from "./utils-DLQ5VVqT.js";
|
|
4
|
+
import { s as RenownCryptoBuilder, t as RenownCryptoSigner } from "./signer-GzTlZLjR.js";
|
|
5
|
+
//#region src/storage/common.ts
|
|
6
|
+
var BaseStorage = class {};
|
|
7
|
+
var MemoryStorage = class extends BaseStorage {
|
|
8
|
+
data = /* @__PURE__ */ new Map();
|
|
9
|
+
get(key) {
|
|
10
|
+
return this.data.get(key);
|
|
11
|
+
}
|
|
12
|
+
set(key, value) {
|
|
13
|
+
if (value === void 0) this.data.delete(key);
|
|
14
|
+
else this.data.set(key, value);
|
|
15
|
+
}
|
|
16
|
+
delete(key) {
|
|
17
|
+
this.data.delete(key);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/common.ts
|
|
22
|
+
var RenownMemoryStorage = class extends MemoryStorage {};
|
|
23
|
+
/**
|
|
24
|
+
* The acting identity an action's signature carries.
|
|
25
|
+
*
|
|
26
|
+
* `User` here is the whole session record — it also holds the DID, the
|
|
27
|
+
* credential, the fetched profile and ENS info — while `UserActionSigner` is
|
|
28
|
+
* what travels on a signed action, and it is deliberately just an address on a
|
|
29
|
+
* chain. The reactor's `ReactorSignerUserInput` declares exactly these three
|
|
30
|
+
* fields, and a GraphQL input object refuses a field it does not declare by
|
|
31
|
+
* rejecting the WHOLE request, so handing the session record to the signer
|
|
32
|
+
* makes every signed push fail before it reaches the reactor.
|
|
33
|
+
*
|
|
34
|
+
* Narrowed here rather than at the transport, because the session record has no
|
|
35
|
+
* business being on an operation in the first place: it is stored on every
|
|
36
|
+
* operation a local reactor writes, not only on the ones that go over a wire.
|
|
37
|
+
*
|
|
38
|
+
* The compiler cannot catch the difference — excess-property checks apply to
|
|
39
|
+
* fresh object literals, never to a variable of a wider type — which is why
|
|
40
|
+
* this is a function rather than a type annotation.
|
|
41
|
+
*/
|
|
42
|
+
function toActionSigner(user) {
|
|
43
|
+
if (!user) return void 0;
|
|
44
|
+
return {
|
|
45
|
+
address: user.address,
|
|
46
|
+
networkId: user.networkId,
|
|
47
|
+
chainId: user.chainId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
var Renown = class {
|
|
51
|
+
#baseUrl;
|
|
52
|
+
#store;
|
|
53
|
+
#eventEmitter;
|
|
54
|
+
#appName;
|
|
55
|
+
#crypto;
|
|
56
|
+
#signer;
|
|
57
|
+
#profileFetcher;
|
|
58
|
+
#switchboard;
|
|
59
|
+
#chainId;
|
|
60
|
+
#status = "initial";
|
|
61
|
+
constructor(store, eventEmitter, crypto, appName, baseUrl = DEFAULT_RENOWN_URL, profileFetcher, switchboard, chainId = 1) {
|
|
62
|
+
this.#store = store;
|
|
63
|
+
this.#eventEmitter = eventEmitter;
|
|
64
|
+
this.#baseUrl = baseUrl;
|
|
65
|
+
this.#crypto = crypto;
|
|
66
|
+
this.#appName = appName;
|
|
67
|
+
this.#profileFetcher = profileFetcher;
|
|
68
|
+
this.#switchboard = switchboard;
|
|
69
|
+
this.#chainId = chainId;
|
|
70
|
+
const restoredUser = this.user;
|
|
71
|
+
this.#signer = new RenownCryptoSigner(crypto, this.#appName, toActionSigner(restoredUser));
|
|
72
|
+
if (restoredUser) this.#status = "authorized";
|
|
73
|
+
this.on("user", (user) => {
|
|
74
|
+
this.#signer.user = toActionSigner(user);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
get baseUrl() {
|
|
78
|
+
return this.#baseUrl;
|
|
79
|
+
}
|
|
80
|
+
/** Chain id this app issues credentials on; a session from another chain is rejected by {@link signIn}. */
|
|
81
|
+
get chainId() {
|
|
82
|
+
return this.#chainId;
|
|
83
|
+
}
|
|
84
|
+
get user() {
|
|
85
|
+
return this.#store.get("user");
|
|
86
|
+
}
|
|
87
|
+
get status() {
|
|
88
|
+
return this.#status;
|
|
89
|
+
}
|
|
90
|
+
get signer() {
|
|
91
|
+
return this.#signer;
|
|
92
|
+
}
|
|
93
|
+
get crypto() {
|
|
94
|
+
return this.#crypto;
|
|
95
|
+
}
|
|
96
|
+
get did() {
|
|
97
|
+
return this.#crypto.did;
|
|
98
|
+
}
|
|
99
|
+
get profileFetcher() {
|
|
100
|
+
return this.#profileFetcher;
|
|
101
|
+
}
|
|
102
|
+
#updateStatus(status) {
|
|
103
|
+
this.#status = status;
|
|
104
|
+
this.#eventEmitter.emit("status", status);
|
|
105
|
+
}
|
|
106
|
+
#updateUser(user) {
|
|
107
|
+
if (user) this.#store.set("user", user);
|
|
108
|
+
else this.#store.delete("user");
|
|
109
|
+
this.#eventEmitter.emit("user", user);
|
|
110
|
+
}
|
|
111
|
+
async login(userDid) {
|
|
112
|
+
this.#updateStatus("checking");
|
|
113
|
+
try {
|
|
114
|
+
const result = parsePkhDid(userDid);
|
|
115
|
+
const credential = await this.#getCredential(result.address, result.chainId, this.#crypto.did);
|
|
116
|
+
if (!credential) {
|
|
117
|
+
this.#updateUser(void 0);
|
|
118
|
+
throw new Error("Credential not found");
|
|
119
|
+
}
|
|
120
|
+
return await this.#completeLogin(userDid, result, credential);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
this.#updateUser(void 0);
|
|
123
|
+
this.#updateStatus("not-authorized");
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async signIn(params) {
|
|
128
|
+
if (!this.#switchboard) throw new MissingSwitchboardError();
|
|
129
|
+
const { address, chainId, signTypedData, username, userImage, expiresInDays } = params;
|
|
130
|
+
if (chainId !== this.#chainId) throw new Error(`Renown signIn: wallet is on chain ${chainId}, but this app issues credentials on chain ${this.#chainId}. Switch networks and retry — signing here would create a separate identity for the same address.`);
|
|
131
|
+
this.#updateStatus("checking");
|
|
132
|
+
try {
|
|
133
|
+
const credential = await buildAndSignCredential({
|
|
134
|
+
signTypedData,
|
|
135
|
+
address,
|
|
136
|
+
chainId,
|
|
137
|
+
app: this.#appName,
|
|
138
|
+
appId: this.did,
|
|
139
|
+
expiresInDays
|
|
140
|
+
});
|
|
141
|
+
await this.#switchboard.issueCredential(credential);
|
|
142
|
+
const userDid = `did:pkh:${DEFAULT_RENOWN_NETWORK_ID}:${chainId}:${address.toLowerCase()}`;
|
|
143
|
+
const user = await this.#completeLogin(userDid, parsePkhDid(userDid), credential);
|
|
144
|
+
if (username !== void 0 || userImage !== void 0) this.#writeProfile(address, {
|
|
145
|
+
username,
|
|
146
|
+
userImage
|
|
147
|
+
}).catch(() => void 0);
|
|
148
|
+
return user;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.#updateUser(void 0);
|
|
151
|
+
this.#updateStatus("not-authorized");
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async #writeProfile(address, profile) {
|
|
156
|
+
if (!this.#switchboard) return;
|
|
157
|
+
const token = await this.#crypto.getBearerToken(address);
|
|
158
|
+
const attempts = 5;
|
|
159
|
+
for (let attempt = 1; attempt <= attempts; attempt++) try {
|
|
160
|
+
await this.#switchboard.upsertUserProfile(address, profile, { token });
|
|
161
|
+
return;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (attempt === attempts) throw error;
|
|
164
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async #completeLogin(userDid, result, credential) {
|
|
168
|
+
if (!(credential.issuer.id === userDid && credential.credentialSubject.id === this.did)) throw new Error("Invalid credential");
|
|
169
|
+
const verifiableCredential = credential.proof.eip712?.domain ? credential : {
|
|
170
|
+
...credential,
|
|
171
|
+
proof: {
|
|
172
|
+
...credential.proof,
|
|
173
|
+
eip712: {
|
|
174
|
+
domain: {
|
|
175
|
+
version: "1",
|
|
176
|
+
chainId: result.chainId
|
|
177
|
+
},
|
|
178
|
+
types: CREDENTIAL_TYPES,
|
|
179
|
+
primaryType: "VerifiableCredential"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
if (credential.issuer.ethereumAddress.toLowerCase() !== result.address.toLowerCase() || verifiableCredential.proof.eip712.domain.chainId !== result.chainId || !await verifyCredentialSignature(verifiableCredential)) throw new Error("Invalid credential signature");
|
|
184
|
+
const user = {
|
|
185
|
+
...result,
|
|
186
|
+
address: credential.issuer.ethereumAddress,
|
|
187
|
+
did: userDid,
|
|
188
|
+
credential: verifiableCredential
|
|
189
|
+
};
|
|
190
|
+
this.#updateUser(user);
|
|
191
|
+
this.#updateStatus("authorized");
|
|
192
|
+
if (this.#profileFetcher) this.#profileFetcher(user, this.#baseUrl).then((profile) => {
|
|
193
|
+
if (profile && this.user?.address === user.address && this.user.chainId === user.chainId) this.#updateUser({
|
|
194
|
+
...this.user,
|
|
195
|
+
profile,
|
|
196
|
+
ens: {
|
|
197
|
+
name: profile.username ?? void 0,
|
|
198
|
+
avatarUrl: profile.userImage ?? void 0
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}).catch(console.error);
|
|
202
|
+
return user;
|
|
203
|
+
}
|
|
204
|
+
logout() {
|
|
205
|
+
this.#updateUser(void 0);
|
|
206
|
+
this.#updateStatus("initial");
|
|
207
|
+
return Promise.resolve();
|
|
208
|
+
}
|
|
209
|
+
async revalidate() {
|
|
210
|
+
const user = this.user;
|
|
211
|
+
if (!user) return false;
|
|
212
|
+
try {
|
|
213
|
+
if (!await this.#getCredential(user.address, user.chainId, this.#crypto.did)) {
|
|
214
|
+
await this.logout();
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
await this.refreshProfile();
|
|
218
|
+
return true;
|
|
219
|
+
} catch {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async refreshProfile() {
|
|
224
|
+
const user = this.user;
|
|
225
|
+
if (!user || !this.#profileFetcher) return false;
|
|
226
|
+
try {
|
|
227
|
+
const profile = await this.#profileFetcher(user, this.#baseUrl);
|
|
228
|
+
if (!profile) return false;
|
|
229
|
+
const current = this.user;
|
|
230
|
+
if (!current || current.address !== user.address || current.chainId !== user.chainId) return false;
|
|
231
|
+
if (current.profile !== void 0 && current.profile.documentId === profile.documentId && current.profile.username === profile.username && current.profile.userImage === profile.userImage) return false;
|
|
232
|
+
this.#updateUser({
|
|
233
|
+
...current,
|
|
234
|
+
profile,
|
|
235
|
+
ens: {
|
|
236
|
+
name: profile.username ?? void 0,
|
|
237
|
+
avatarUrl: profile.userImage ?? void 0
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
return true;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
on(event, listener) {
|
|
246
|
+
return this.#eventEmitter.on(event, listener);
|
|
247
|
+
}
|
|
248
|
+
async #getCredential(address, chainId, appDid) {
|
|
249
|
+
if (this.#switchboard) return this.#switchboard.getCredential({
|
|
250
|
+
address,
|
|
251
|
+
chainId,
|
|
252
|
+
appDid
|
|
253
|
+
});
|
|
254
|
+
if (!this.#baseUrl) throw new Error("RENOWN_URL is not set");
|
|
255
|
+
const url = new URL(`/api/auth/credential?address=${encodeURIComponent(address)}&chainId=${encodeURIComponent(chainId)}&connectId=${encodeURIComponent(appDid)}&appId=${encodeURIComponent(appDid)}`, this.#baseUrl);
|
|
256
|
+
const response = await fetch(url, { method: "GET" });
|
|
257
|
+
if (response.ok) return (await response.json()).credential;
|
|
258
|
+
else throw new Error(`Failed to get credential: ${response.status}`);
|
|
259
|
+
}
|
|
260
|
+
async verifyBearerToken(token) {
|
|
261
|
+
return verifyAuthBearerToken(token);
|
|
262
|
+
}
|
|
263
|
+
async getBearerToken(options) {
|
|
264
|
+
if (!this.user) throw new Error("User not found");
|
|
265
|
+
return this.#crypto.getBearerToken(this.user.address, options);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/event/memory.ts
|
|
270
|
+
var MemoryEventEmitter = class {
|
|
271
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
272
|
+
on(event, listener) {
|
|
273
|
+
if (!this.#listeners.has(event)) this.#listeners.set(event, /* @__PURE__ */ new Set());
|
|
274
|
+
this.#listeners.get(event).add(listener);
|
|
275
|
+
return () => {
|
|
276
|
+
this.#listeners.get(event)?.delete(listener);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
emit(event, data) {
|
|
280
|
+
this.#listeners.get(event)?.forEach((listener) => listener(data));
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/profile.ts
|
|
285
|
+
const fetchRenownProfile = async (user, baseUrl) => {
|
|
286
|
+
try {
|
|
287
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/profile`, {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "Content-Type": "application/json" },
|
|
290
|
+
body: JSON.stringify({ ethAddress: user.address })
|
|
291
|
+
});
|
|
292
|
+
if (!response.ok) return;
|
|
293
|
+
return (await response.json()).profile ?? void 0;
|
|
294
|
+
} catch {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/renown-builder.ts
|
|
300
|
+
/**
|
|
301
|
+
* Base builder for creating Renown instances.
|
|
302
|
+
* Use platform-specific builders (RenownBuilder from init.browser.js or init.node.js)
|
|
303
|
+
* for pre-configured defaults.
|
|
304
|
+
*/
|
|
305
|
+
var BaseRenownBuilder = class BaseRenownBuilder {
|
|
306
|
+
#appName;
|
|
307
|
+
#storage;
|
|
308
|
+
#eventEmitter;
|
|
309
|
+
#crypto;
|
|
310
|
+
#keyPairStorage;
|
|
311
|
+
#baseUrl;
|
|
312
|
+
#switchboardUrl;
|
|
313
|
+
#profileFetcher;
|
|
314
|
+
#chainId;
|
|
315
|
+
/**
|
|
316
|
+
* @param appName - Application name used for signing context
|
|
317
|
+
*/
|
|
318
|
+
constructor(appName) {
|
|
319
|
+
this.#appName = appName;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Set custom storage for user data persistence.
|
|
323
|
+
* Defaults to in-memory storage if not set.
|
|
324
|
+
*/
|
|
325
|
+
withStorage(storage) {
|
|
326
|
+
this.#storage = storage;
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Set custom event emitter for user state changes.
|
|
331
|
+
* Defaults to in-memory event emitter if not set.
|
|
332
|
+
*/
|
|
333
|
+
withEventEmitter(eventEmitter) {
|
|
334
|
+
this.#eventEmitter = eventEmitter;
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Set a pre-built crypto instance.
|
|
339
|
+
* Either crypto or keyPairStorage must be provided.
|
|
340
|
+
*/
|
|
341
|
+
withCrypto(crypto) {
|
|
342
|
+
this.#crypto = crypto;
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Set key pair storage for cryptographic keys.
|
|
347
|
+
* A crypto instance will be built from this storage.
|
|
348
|
+
* Either crypto or keyPairStorage must be provided.
|
|
349
|
+
*/
|
|
350
|
+
withKeyPairStorage(keyPairStorage) {
|
|
351
|
+
this.#keyPairStorage = keyPairStorage;
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Set the Renown server URL for credential verification.
|
|
356
|
+
* Defaults to https://www.renown.id
|
|
357
|
+
*/
|
|
358
|
+
withBaseUrl(baseUrl) {
|
|
359
|
+
this.#baseUrl = baseUrl;
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
withSwitchboardUrl(switchboardUrl) {
|
|
363
|
+
this.#switchboardUrl = switchboardUrl;
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
withChainId(chainId) {
|
|
367
|
+
this.#chainId = chainId;
|
|
368
|
+
return this;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Set a profile fetcher strategy for enriching user data after login.
|
|
372
|
+
* The fetcher receives the authenticated user and the base URL,
|
|
373
|
+
* and returns a RenownProfile. Called in the background after each login.
|
|
374
|
+
* Defaults to fetchRenownProfile which calls the Renown API.
|
|
375
|
+
*/
|
|
376
|
+
withProfileFetcher(profileFetcher) {
|
|
377
|
+
this.#profileFetcher = profileFetcher;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Build and initialize the Renown instance.
|
|
382
|
+
* If a user is stored, attempts to re-authenticate them.
|
|
383
|
+
* @throws Error if neither crypto nor keyPairStorage is provided
|
|
384
|
+
*/
|
|
385
|
+
async build() {
|
|
386
|
+
if (!this.#crypto && !this.#keyPairStorage) throw new Error("Either crypto or keyPairStorage is required. Use withCrypto() or withKeyPairStorage() to set one.");
|
|
387
|
+
const chainId = this.#chainId ?? 1;
|
|
388
|
+
const crypto = this.#crypto ?? await new RenownCryptoBuilder().withKeyPairStorage(this.#keyPairStorage).withChainId(chainId).build();
|
|
389
|
+
if (crypto.chainId !== void 0 && crypto.chainId !== chainId) console.warn(`Renown: the supplied crypto signs bearer tokens for chain ${crypto.chainId}, but credentials are issued on chain ${chainId}.`);
|
|
390
|
+
const storage = this.#storage ?? new RenownMemoryStorage();
|
|
391
|
+
const eventEmitter = this.#eventEmitter ?? new MemoryEventEmitter();
|
|
392
|
+
const baseUrl = this.#baseUrl ?? "https://www.renown.id";
|
|
393
|
+
const switchboardEndpoint = await resolveSwitchboardEndpoint({
|
|
394
|
+
switchboardUrl: this.#switchboardUrl,
|
|
395
|
+
baseUrl
|
|
396
|
+
});
|
|
397
|
+
const switchboard = switchboardEndpoint ? new SwitchboardClient(switchboardEndpoint) : void 0;
|
|
398
|
+
const profileFetcher = this.#profileFetcher ?? (switchboard ? switchboard.profileFetcher : fetchRenownProfile);
|
|
399
|
+
return new Renown(storage, eventEmitter, crypto, this.#appName, baseUrl, profileFetcher, switchboard, chainId);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create a BaseRenownBuilder from options object for a more concise API
|
|
403
|
+
*/
|
|
404
|
+
static from(options) {
|
|
405
|
+
const builder = new BaseRenownBuilder(options.appName);
|
|
406
|
+
if (options.storage) builder.withStorage(options.storage);
|
|
407
|
+
if (options.eventEmitter) builder.withEventEmitter(options.eventEmitter);
|
|
408
|
+
if (options.crypto) builder.withCrypto(options.crypto);
|
|
409
|
+
if (options.keyPairStorage) builder.withKeyPairStorage(options.keyPairStorage);
|
|
410
|
+
if (options.baseUrl) builder.withBaseUrl(options.baseUrl);
|
|
411
|
+
if (options.switchboardUrl) builder.withSwitchboardUrl(options.switchboardUrl);
|
|
412
|
+
if (options.profileFetcher) builder.withProfileFetcher(options.profileFetcher);
|
|
413
|
+
if (options.chainId !== void 0) builder.withChainId(options.chainId);
|
|
414
|
+
return builder;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/local-credential-verifier.ts
|
|
419
|
+
/** A credential check that reads a Switchboard's own renown read model through
|
|
420
|
+
* `execute`, rather than any Renown or Switchboard over HTTP. */
|
|
421
|
+
function createLocalCredentialVerifier(execute, options = {}) {
|
|
422
|
+
const request = async (query, variables) => {
|
|
423
|
+
try {
|
|
424
|
+
return await execute(query, variables);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
options.onError?.(error);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
return async ({ address, chainId, appId }) => {
|
|
431
|
+
return await fetchDelegationCredential({
|
|
432
|
+
address,
|
|
433
|
+
chainId,
|
|
434
|
+
appDid: appId,
|
|
435
|
+
switchboardRequest: request
|
|
436
|
+
}) !== void 0;
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/signed-message.ts
|
|
441
|
+
const SIGNATURE_WINDOW_MS = 6e5;
|
|
442
|
+
function toHex(bytes) {
|
|
443
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
444
|
+
}
|
|
445
|
+
async function sha256hex(input) {
|
|
446
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
|
|
447
|
+
return toHex(new Uint8Array(digest));
|
|
448
|
+
}
|
|
449
|
+
/** Canonical message a caller signs to authorize revoking `credentialId`. */
|
|
450
|
+
function revokeMessage(credentialId, timestamp) {
|
|
451
|
+
return `Revoke Renown credential ${credentialId} at ${timestamp}`;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Canonical message a caller signs to authorize a profile upsert. The payload
|
|
455
|
+
* is hashed (rather than embedded raw) so the signed message has a fixed
|
|
456
|
+
* shape regardless of field content.
|
|
457
|
+
*/
|
|
458
|
+
async function profileMessage(address, profile, timestamp) {
|
|
459
|
+
const hash = await sha256hex(JSON.stringify({
|
|
460
|
+
username: profile.username ?? null,
|
|
461
|
+
userImage: profile.userImage ?? null
|
|
462
|
+
}));
|
|
463
|
+
return `Update Renown profile ${address.toLowerCase()} ${hash} at ${timestamp}`;
|
|
464
|
+
}
|
|
465
|
+
//#endregion
|
|
466
|
+
export { BaseRenownBuilder as a, RenownMemoryStorage as c, createLocalCredentialVerifier as i, BaseStorage as l, profileMessage as n, fetchRenownProfile as o, revokeMessage as r, Renown as s, SIGNATURE_WINDOW_MS as t };
|
|
467
|
+
|
|
468
|
+
//# sourceMappingURL=signed-message-Cp5oRPLN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-message-Cp5oRPLN.js","names":[],"sources":["../src/storage/common.ts","../src/common.ts","../src/event/memory.ts","../src/profile.ts","../src/renown-builder.ts","../src/local-credential-verifier.ts","../src/signed-message.ts"],"sourcesContent":["export interface IStorage<\n T extends Record<string, unknown> = Record<string, unknown>,\n Key extends keyof T = keyof T,\n> {\n get(key: Key): T[Key] | undefined;\n set(key: Key, value?: T[Key]): void;\n delete(key: Key): void;\n}\n\nexport abstract class BaseStorage<\n T extends Record<string, unknown> = Record<string, unknown>,\n Key extends keyof T = keyof T,\n> implements IStorage<T> {\n abstract get(key: Key): T[Key] | undefined;\n abstract set(key: Key, value?: T[Key]): void;\n abstract delete(key: Key): void;\n}\n\nexport class MemoryStorage<\n T extends Record<string, unknown> = Record<string, unknown>,\n Key extends keyof T = keyof T,\n> extends BaseStorage<T> {\n private readonly data = new Map<Key, T[Key]>();\n\n get(key: Key) {\n return this.data.get(key);\n }\n\n set(key: Key, value?: T[Key]): void {\n if (value === undefined) {\n this.data.delete(key);\n } else {\n this.data.set(key, value);\n }\n }\n\n delete(key: Key): void {\n this.data.delete(key);\n }\n}\n","import {\n CREDENTIAL_TYPES,\n DEFAULT_RENOWN_CHAIN_ID,\n DEFAULT_RENOWN_NETWORK_ID,\n DEFAULT_RENOWN_URL,\n} from \"./constants.js\";\nimport {\n buildAndSignCredential,\n verifyCredentialSignature,\n} from \"./credential.js\";\nimport { RenownCryptoSigner, type IRenownCrypto } from \"./crypto/index.js\";\nimport { MemoryStorage } from \"./storage/common.js\";\nimport {\n MissingSwitchboardError,\n type SwitchboardClient,\n} from \"./switchboard.js\";\nimport type {\n CreateBearerTokenOptions,\n IProof,\n IRenown,\n ISigner,\n LoginStatus,\n PKHDid,\n PowerhouseVerifiableCredential,\n ProfileFetcher,\n RenownEventEmitter,\n RenownEvents,\n RenownStorage,\n RenownStorageMap,\n SignInParams,\n User,\n UserActionSigner,\n} from \"./types.js\";\nimport { parsePkhDid, verifyAuthBearerToken } from \"./utils.js\";\nexport * from \"./constants.js\";\n\nexport class RenownMemoryStorage extends MemoryStorage<RenownStorageMap> {}\n\n/**\n * The acting identity an action's signature carries.\n *\n * `User` here is the whole session record — it also holds the DID, the\n * credential, the fetched profile and ENS info — while `UserActionSigner` is\n * what travels on a signed action, and it is deliberately just an address on a\n * chain. The reactor's `ReactorSignerUserInput` declares exactly these three\n * fields, and a GraphQL input object refuses a field it does not declare by\n * rejecting the WHOLE request, so handing the session record to the signer\n * makes every signed push fail before it reaches the reactor.\n *\n * Narrowed here rather than at the transport, because the session record has no\n * business being on an operation in the first place: it is stored on every\n * operation a local reactor writes, not only on the ones that go over a wire.\n *\n * The compiler cannot catch the difference — excess-property checks apply to\n * fresh object literals, never to a variable of a wider type — which is why\n * this is a function rather than a type annotation.\n */\nfunction toActionSigner(user: User | undefined): UserActionSigner | undefined {\n if (!user) return undefined;\n return {\n address: user.address,\n networkId: user.networkId,\n chainId: user.chainId,\n };\n}\n\nexport class Renown implements IRenown {\n #baseUrl: string;\n #store: RenownStorage;\n #eventEmitter: RenownEventEmitter;\n #appName: string;\n #crypto: IRenownCrypto;\n #signer: ISigner;\n #profileFetcher?: ProfileFetcher;\n #switchboard?: SwitchboardClient;\n #chainId: number;\n #status: LoginStatus = \"initial\";\n\n constructor(\n store: RenownStorage,\n eventEmitter: RenownEventEmitter,\n crypto: IRenownCrypto,\n appName: string,\n baseUrl = DEFAULT_RENOWN_URL,\n profileFetcher?: ProfileFetcher,\n switchboard?: SwitchboardClient,\n chainId = Number(DEFAULT_RENOWN_CHAIN_ID),\n ) {\n this.#store = store;\n this.#eventEmitter = eventEmitter;\n this.#baseUrl = baseUrl;\n this.#crypto = crypto;\n this.#appName = appName;\n this.#profileFetcher = profileFetcher;\n this.#switchboard = switchboard;\n this.#chainId = chainId;\n const restoredUser = this.user;\n this.#signer = new RenownCryptoSigner(\n crypto,\n this.#appName,\n toActionSigner(restoredUser),\n );\n\n // A restored, previously-verified user is an authorized session; otherwise\n // status stays \"initial\" after a reload while the user is present.\n if (restoredUser) {\n this.#status = \"authorized\";\n }\n\n this.on(\"user\", (user) => {\n this.#signer.user = toActionSigner(user);\n });\n }\n\n get baseUrl() {\n return this.#baseUrl;\n }\n\n /** Chain id this app issues credentials on; a session from another chain is rejected by {@link signIn}. */\n get chainId() {\n return this.#chainId;\n }\n\n get user() {\n return this.#store.get(\"user\");\n }\n\n get status() {\n return this.#status;\n }\n\n get signer() {\n return this.#signer;\n }\n\n get crypto() {\n return this.#crypto;\n }\n\n get did() {\n return this.#crypto.did;\n }\n\n get profileFetcher() {\n return this.#profileFetcher;\n }\n\n #updateStatus(status: LoginStatus) {\n this.#status = status;\n this.#eventEmitter.emit(\"status\", status);\n }\n\n #updateUser(user: User | undefined) {\n if (user) {\n this.#store.set(\"user\", user);\n } else {\n this.#store.delete(\"user\");\n }\n this.#eventEmitter.emit(\"user\", user);\n }\n\n async login(userDid: string): Promise<User> {\n this.#updateStatus(\"checking\");\n try {\n const result = parsePkhDid(userDid);\n const credential = await this.#getCredential(\n result.address,\n result.chainId,\n this.#crypto.did,\n );\n\n if (!credential) {\n this.#updateUser(undefined);\n throw new Error(\"Credential not found\");\n }\n\n return await this.#completeLogin(userDid, result, credential);\n } catch (error) {\n this.#updateUser(undefined);\n this.#updateStatus(\"not-authorized\");\n throw error;\n }\n }\n\n // Sign in without the Renown redirect: build + sign a delegation credential\n // with the caller's wallet signer, write it to the switchboard, then log in.\n async signIn(params: SignInParams): Promise<User> {\n if (!this.#switchboard) throw new MissingSwitchboardError();\n const {\n address,\n chainId,\n signTypedData,\n username,\n userImage,\n expiresInDays,\n } = params;\n\n // The chain id goes into the issuer DID and the EIP-712 domain, so signing on\n // another chain would mint a second identity for the same wallet.\n if (chainId !== this.#chainId) {\n throw new Error(\n `Renown signIn: wallet is on chain ${chainId}, but this app issues credentials on chain ${this.#chainId}. Switch networks and retry — signing here would create a separate identity for the same address.`,\n );\n }\n\n this.#updateStatus(\"checking\");\n try {\n const credential = await buildAndSignCredential({\n signTypedData,\n address,\n chainId,\n app: this.#appName,\n appId: this.did,\n expiresInDays,\n });\n\n await this.#switchboard.issueCredential(credential);\n\n const userDid = `did:pkh:${DEFAULT_RENOWN_NETWORK_ID}:${chainId}:${address.toLowerCase()}`;\n // Authenticate from the freshly signed credential — no read-back, which\n // avoids racing the read-model processor that indexes the write.\n const user = await this.#completeLogin(\n userDid,\n parsePkhDid(userDid),\n credential,\n );\n\n // Best-effort profile write in the background: sign-in resolves without\n // waiting for it (it may retry for a few seconds) and never fails on it.\n if (username !== undefined || userImage !== undefined) {\n void this.#writeProfile(address, { username, userImage }).catch(\n () => undefined,\n );\n }\n\n return user;\n } catch (error) {\n this.#updateUser(undefined);\n this.#updateStatus(\"not-authorized\");\n throw error;\n }\n }\n\n // Write the profile via an authenticated request, retrying while the reactor's\n // read model catches up to the just-issued credential (needed to authenticate).\n async #writeProfile(\n address: string,\n profile: { username?: string; userImage?: string },\n ): Promise<void> {\n if (!this.#switchboard) return;\n const token = await this.#crypto.getBearerToken(address);\n const attempts = 5;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n await this.#switchboard.upsertUserProfile(address, profile, { token });\n return;\n } catch (error) {\n if (attempt === attempts) throw error;\n await new Promise((resolve) => setTimeout(resolve, 600));\n }\n }\n }\n\n // Verify a credential binds to userDid + this app key, set the user, and\n // start the background profile fetch. Throws if the credential is invalid.\n async #completeLogin(\n userDid: string,\n result: PKHDid,\n credential: PowerhouseVerifiableCredential,\n ): Promise<User> {\n if (\n !(\n credential.issuer.id === userDid &&\n credential.credentialSubject.id === this.did\n )\n ) {\n throw new Error(\"Invalid credential\");\n }\n\n // The API may return proof.eip712 without domain/types; the domain is\n // canonical (version \"1\", the chainId), so reconstruct it before verifying.\n const eip712 = credential.proof.eip712 as\n | Partial<IProof[\"eip712\"]>\n | undefined;\n const verifiableCredential: PowerhouseVerifiableCredential = eip712?.domain\n ? credential\n : {\n ...credential,\n proof: {\n ...credential.proof,\n eip712: {\n domain: { version: \"1\", chainId: result.chainId },\n types: CREDENTIAL_TYPES,\n primaryType: \"VerifiableCredential\",\n },\n },\n };\n\n // Verify the EIP-712 proof was signed by the DID's address on its chain.\n if (\n credential.issuer.ethereumAddress.toLowerCase() !==\n result.address.toLowerCase() ||\n verifiableCredential.proof.eip712.domain.chainId !== result.chainId ||\n !(await verifyCredentialSignature(verifiableCredential))\n ) {\n throw new Error(\"Invalid credential signature\");\n }\n\n const user: User = {\n ...result,\n address: credential.issuer.ethereumAddress,\n did: userDid,\n credential: verifiableCredential,\n };\n\n this.#updateUser(user);\n this.#updateStatus(\"authorized\");\n\n // Fetch profile data in the background if a fetcher is configured\n if (this.#profileFetcher) {\n this.#profileFetcher(user, this.#baseUrl)\n .then((profile) => {\n if (\n profile &&\n this.user?.address === user.address &&\n this.user.chainId === user.chainId\n ) {\n this.#updateUser({\n ...this.user,\n profile,\n ens: {\n name: profile.username ?? undefined,\n avatarUrl: profile.userImage ?? undefined,\n },\n });\n }\n })\n .catch(console.error);\n }\n\n return user;\n }\n\n logout() {\n this.#updateUser(undefined);\n this.#updateStatus(\"initial\");\n return Promise.resolve();\n }\n\n // Re-check the current user's credential at the source; logs out if it was\n // revoked or expired. Network errors keep the session (fail open).\n async revalidate(): Promise<boolean> {\n const user = this.user;\n if (!user) return false;\n try {\n const credential = await this.#getCredential(\n user.address,\n user.chainId,\n this.#crypto.did,\n );\n if (!credential) {\n await this.logout();\n return false;\n }\n await this.refreshProfile();\n return true;\n } catch {\n return true;\n }\n }\n\n // Re-read profile attributes at the source so a renamed user or changed avatar\n // shows up on the next load. Carries no logout risk, unlike revalidate().\n async refreshProfile(): Promise<boolean> {\n const user = this.user;\n if (!user || !this.#profileFetcher) return false;\n try {\n const profile = await this.#profileFetcher(user, this.#baseUrl);\n if (!profile) return false;\n // Asserted because narrowing from the pre-await read would otherwise hide\n // that a logout during the fetch can clear this.\n const current = this.user as User | undefined;\n if (\n !current ||\n current.address !== user.address ||\n current.chainId !== user.chainId\n ) {\n return false;\n }\n // Narrow `profile` first: the fetched payload is unvalidated JSON, so a\n // missing documentId must not make the undefined case compare equal.\n const unchanged =\n current.profile !== undefined &&\n current.profile.documentId === profile.documentId &&\n current.profile.username === profile.username &&\n current.profile.userImage === profile.userImage;\n if (unchanged) return false;\n this.#updateUser({\n ...current,\n profile,\n ens: {\n name: profile.username ?? undefined,\n avatarUrl: profile.userImage ?? undefined,\n },\n });\n return true;\n } catch {\n return false;\n }\n }\n\n on<K extends keyof RenownEvents>(\n event: K,\n listener: (data: RenownEvents[K]) => void,\n ): () => void {\n return this.#eventEmitter.on(event, listener);\n }\n\n async #getCredential(\n address: string,\n chainId: number,\n appDid: string,\n ): Promise<PowerhouseVerifiableCredential | undefined> {\n // Direct-switchboard flow: read the credential from the reactor subgraph.\n if (this.#switchboard) {\n return this.#switchboard.getCredential({ address, chainId, appDid });\n }\n if (!this.#baseUrl) {\n throw new Error(\"RENOWN_URL is not set\");\n }\n const url = new URL(\n `/api/auth/credential?address=${encodeURIComponent(address)}&chainId=${encodeURIComponent(chainId)}&connectId=${encodeURIComponent(appDid)}&appId=${encodeURIComponent(appDid)}`,\n this.#baseUrl,\n );\n const response = await fetch(url, {\n method: \"GET\",\n });\n if (response.ok) {\n const result = (await response.json()) as {\n credential: PowerhouseVerifiableCredential;\n };\n return result.credential;\n } else {\n throw new Error(`Failed to get credential: ${response.status}`);\n }\n }\n\n async verifyBearerToken(token: string) {\n return verifyAuthBearerToken(token);\n }\n\n async getBearerToken(options: CreateBearerTokenOptions) {\n if (!this.user) {\n throw new Error(\"User not found\");\n }\n return this.#crypto.getBearerToken(this.user.address, options);\n }\n}\n","import type { RenownEvents } from \"../types.js\";\nimport type { IEventEmitter } from \"./types.js\";\n\nexport class MemoryEventEmitter implements IEventEmitter<RenownEvents> {\n #listeners = new Map<keyof RenownEvents, Set<(data: unknown) => void>>();\n\n on<K extends keyof RenownEvents>(\n event: K,\n listener: (data: RenownEvents[K]) => void,\n ): () => void {\n if (!this.#listeners.has(event)) {\n this.#listeners.set(event, new Set());\n }\n this.#listeners.get(event)!.add(listener as (data: unknown) => void);\n return () => {\n this.#listeners.get(event)?.delete(listener as (data: unknown) => void);\n };\n }\n\n emit<K extends keyof RenownEvents>(event: K, data: RenownEvents[K]): void {\n this.#listeners.get(event)?.forEach((listener) => listener(data));\n }\n}\n","import type { ProfileFetcher, RenownProfile } from \"./types.js\";\n\nexport const fetchRenownProfile: ProfileFetcher = async (user, baseUrl) => {\n try {\n const response = await fetch(`${baseUrl.replace(/\\/+$/, \"\")}/api/profile`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n ethAddress: user.address,\n }),\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const result = (await response.json()) as {\n profile?: RenownProfile;\n };\n\n return result.profile ?? undefined;\n } catch {\n return undefined;\n }\n};\n","import { Renown, RenownMemoryStorage } from \"./common.js\";\nimport { DEFAULT_RENOWN_CHAIN_ID, DEFAULT_RENOWN_URL } from \"./constants.js\";\nimport { RenownCryptoBuilder } from \"./crypto/renown-crypto-builder.js\";\nimport type { IRenownCrypto, JsonWebKeyPairStorage } from \"./crypto/types.js\";\nimport { resolveSwitchboardEndpoint } from \"./discovery.js\";\nimport { MemoryEventEmitter } from \"./event/memory.js\";\nimport { fetchRenownProfile } from \"./profile.js\";\nimport { SwitchboardClient } from \"./switchboard.js\";\nimport type {\n ProfileFetcher,\n RenownEventEmitter,\n RenownStorage,\n} from \"./types.js\";\n\nexport interface RenownBuilderOptions {\n appName: string;\n storage?: RenownStorage;\n eventEmitter?: RenownEventEmitter;\n crypto?: IRenownCrypto;\n keyPairStorage?: JsonWebKeyPairStorage;\n baseUrl?: string;\n switchboardUrl?: string;\n profileFetcher?: ProfileFetcher;\n /** Chain id credentials are issued on (default 1). The issuer DID embeds it, so `signIn` rejects a wallet session from another chain. */\n chainId?: number;\n}\n\n/**\n * Base builder for creating Renown instances.\n * Use platform-specific builders (RenownBuilder from init.browser.js or init.node.js)\n * for pre-configured defaults.\n */\nexport class BaseRenownBuilder {\n #appName: string;\n #storage?: RenownStorage;\n #eventEmitter?: RenownEventEmitter;\n #crypto?: IRenownCrypto;\n #keyPairStorage?: JsonWebKeyPairStorage;\n #baseUrl?: string;\n #switchboardUrl?: string;\n #profileFetcher?: ProfileFetcher;\n #chainId?: number;\n\n /**\n * @param appName - Application name used for signing context\n */\n constructor(appName: string) {\n this.#appName = appName;\n }\n\n /**\n * Set custom storage for user data persistence.\n * Defaults to in-memory storage if not set.\n */\n withStorage(storage: RenownStorage): this {\n this.#storage = storage;\n return this;\n }\n\n /**\n * Set custom event emitter for user state changes.\n * Defaults to in-memory event emitter if not set.\n */\n withEventEmitter(eventEmitter: RenownEventEmitter): this {\n this.#eventEmitter = eventEmitter;\n return this;\n }\n\n /**\n * Set a pre-built crypto instance.\n * Either crypto or keyPairStorage must be provided.\n */\n withCrypto(crypto: IRenownCrypto): this {\n this.#crypto = crypto;\n return this;\n }\n\n /**\n * Set key pair storage for cryptographic keys.\n * A crypto instance will be built from this storage.\n * Either crypto or keyPairStorage must be provided.\n */\n withKeyPairStorage(keyPairStorage: JsonWebKeyPairStorage): this {\n this.#keyPairStorage = keyPairStorage;\n return this;\n }\n\n /**\n * Set the Renown server URL for credential verification.\n * Defaults to https://www.renown.id\n */\n withBaseUrl(baseUrl: string): this {\n this.#baseUrl = baseUrl;\n return this;\n }\n\n // Set the Switchboard GraphQL endpoint to read from the reactor directly.\n // When unset, the SDK discovers it from the base URL, else uses the REST flow.\n withSwitchboardUrl(switchboardUrl: string): this {\n this.#switchboardUrl = switchboardUrl;\n return this;\n }\n\n // Set the chain id credentials are issued on (default 1). It is part of the\n // issuer DID, so signIn rejects a wallet session from a different chain.\n withChainId(chainId: number): this {\n this.#chainId = chainId;\n return this;\n }\n\n /**\n * Set a profile fetcher strategy for enriching user data after login.\n * The fetcher receives the authenticated user and the base URL,\n * and returns a RenownProfile. Called in the background after each login.\n * Defaults to fetchRenownProfile which calls the Renown API.\n */\n withProfileFetcher(profileFetcher: ProfileFetcher): this {\n this.#profileFetcher = profileFetcher;\n return this;\n }\n\n /**\n * Build and initialize the Renown instance.\n * If a user is stored, attempts to re-authenticate them.\n * @throws Error if neither crypto nor keyPairStorage is provided\n */\n async build(): Promise<Renown> {\n if (!this.#crypto && !this.#keyPairStorage) {\n throw new Error(\n \"Either crypto or keyPairStorage is required. Use withCrypto() or withKeyPairStorage() to set one.\",\n );\n }\n\n const chainId = this.#chainId ?? Number(DEFAULT_RENOWN_CHAIN_ID);\n const crypto =\n this.#crypto ??\n (await new RenownCryptoBuilder()\n .withKeyPairStorage(this.#keyPairStorage!)\n .withChainId(chainId)\n .build());\n\n // A crypto supplied via withCrypto() carries its own chain; the bearer token\n // it mints would then disagree with the credentials issued here.\n if (crypto.chainId !== undefined && crypto.chainId !== chainId) {\n console.warn(\n `Renown: the supplied crypto signs bearer tokens for chain ${crypto.chainId}, but credentials are issued on chain ${chainId}.`,\n );\n }\n\n const storage = this.#storage ?? new RenownMemoryStorage();\n const eventEmitter = this.#eventEmitter ?? new MemoryEventEmitter();\n const baseUrl = this.#baseUrl ?? DEFAULT_RENOWN_URL;\n\n // Pick the transport once: direct-switchboard when an endpoint resolves,\n // else the REST flow. A custom profile fetcher always takes precedence.\n const switchboardEndpoint = await resolveSwitchboardEndpoint({\n switchboardUrl: this.#switchboardUrl,\n baseUrl,\n });\n const switchboard = switchboardEndpoint\n ? new SwitchboardClient(switchboardEndpoint)\n : undefined;\n const profileFetcher =\n this.#profileFetcher ??\n (switchboard ? switchboard.profileFetcher : fetchRenownProfile);\n\n const renown = new Renown(\n storage,\n eventEmitter,\n crypto,\n this.#appName,\n baseUrl,\n profileFetcher,\n switchboard,\n chainId,\n );\n\n // A stored user is loaded as-is for an instant, offline-safe start; callers\n // revalidate against the source separately via renown.revalidate().\n return renown;\n }\n\n /**\n * Create a BaseRenownBuilder from options object for a more concise API\n */\n static from(options: RenownBuilderOptions): BaseRenownBuilder {\n const builder = new BaseRenownBuilder(options.appName);\n\n if (options.storage) {\n builder.withStorage(options.storage);\n }\n if (options.eventEmitter) {\n builder.withEventEmitter(options.eventEmitter);\n }\n if (options.crypto) {\n builder.withCrypto(options.crypto);\n }\n if (options.keyPairStorage) {\n builder.withKeyPairStorage(options.keyPairStorage);\n }\n if (options.baseUrl) {\n builder.withBaseUrl(options.baseUrl);\n }\n if (options.switchboardUrl) {\n builder.withSwitchboardUrl(options.switchboardUrl);\n }\n if (options.profileFetcher) {\n builder.withProfileFetcher(options.profileFetcher);\n }\n if (options.chainId !== undefined) {\n builder.withChainId(options.chainId);\n }\n\n return builder;\n }\n}\n","import { fetchDelegationCredential } from \"./credential.js\";\nimport type { SwitchboardRequestFn } from \"./switchboard.js\";\n\n/** Confirms an authenticated signer's delegation credential is still live. */\nexport type CredentialCheck = (params: {\n address: string;\n chainId: number;\n appId: string;\n}) => Promise<boolean>;\n\nexport interface LocalCredentialVerifierOptions {\n /** Notified when the read itself fails; the check then fails closed. */\n onError?: (error: unknown) => void;\n}\n\n/** A credential check that reads a Switchboard's own renown read model through\n * `execute`, rather than any Renown or Switchboard over HTTP. */\nexport function createLocalCredentialVerifier(\n execute: SwitchboardRequestFn,\n options: LocalCredentialVerifierOptions = {},\n): CredentialCheck {\n // fetchDelegationCredential turns a read failure into \"no credential\" — the\n // right verdict, but silent; surface it before it is swallowed.\n const request: SwitchboardRequestFn = async (query, variables) => {\n try {\n return await execute(query, variables);\n } catch (error) {\n options.onError?.(error);\n throw error;\n }\n };\n\n return async ({ address, chainId, appId }) => {\n const credential = await fetchDelegationCredential({\n address,\n chainId,\n appDid: appId,\n switchboardRequest: request,\n });\n return credential !== undefined;\n };\n}\n","// Canonical messages a caller personal_signs (EIP-191) to authorize a Renown\n// write without a login token. Kept byte-identical to renown-package\n// `subgraphs/renown-auth/core/signed-message.ts`, which verifies them; the\n// pinned strings in test/signed-message.test.ts guard both sides.\n\n// Signed actions (revoke, profile update) must carry a timestamp within this\n// window of the server's clock, in either direction. Signing a fresh message\n// and replaying an old one both fail outside this bound.\nexport const SIGNATURE_WINDOW_MS = 600_000;\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n}\n\nasync function sha256hex(input: string): Promise<string> {\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(input),\n );\n return toHex(new Uint8Array(digest));\n}\n\n/** Canonical message a caller signs to authorize revoking `credentialId`. */\nexport function revokeMessage(credentialId: string, timestamp: string): string {\n return `Revoke Renown credential ${credentialId} at ${timestamp}`;\n}\n\n/**\n * Canonical message a caller signs to authorize a profile upsert. The payload\n * is hashed (rather than embedded raw) so the signed message has a fixed\n * shape regardless of field content.\n */\nexport async function profileMessage(\n address: string,\n profile: { username?: string | null; userImage?: string | null },\n timestamp: string,\n): Promise<string> {\n const payload = JSON.stringify({\n username: profile.username ?? null,\n userImage: profile.userImage ?? null,\n });\n const hash = await sha256hex(payload);\n return `Update Renown profile ${address.toLowerCase()} ${hash} at ${timestamp}`;\n}\n"],"mappings":"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"}
|