@happyvertical/smrt-profiles 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +53 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +176 -0
- package/dist/chunks/ApiKey-B2LKEaP8.js +143 -0
- package/dist/chunks/ApiKey-B2LKEaP8.js.map +1 -0
- package/dist/chunks/ApiKeyCollection-B6Op817e.js +91 -0
- package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +1 -0
- package/dist/chunks/AuditLogCollection-BYqCj0uE.js +195 -0
- package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +1 -0
- package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +3065 -0
- package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +1 -0
- package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js +122 -0
- package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js.map +1 -0
- package/dist/chunks/ProfileCollection-DU6wUJTO.js +782 -0
- package/dist/chunks/ProfileCollection-DU6wUJTO.js.map +1 -0
- package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +120 -0
- package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +1 -0
- package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +184 -0
- package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +1 -0
- package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js +177 -0
- package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js.map +1 -0
- package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +117 -0
- package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +1 -0
- package/dist/chunks/ProfileRelationshipType-BXBLldea.js +103 -0
- package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +1 -0
- package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +48 -0
- package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +1 -0
- package/dist/chunks/index-jFtOWsAV.js +1014 -0
- package/dist/chunks/index-jFtOWsAV.js.map +1 -0
- package/dist/index.d.ts +1848 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +11829 -0
- package/dist/smrt-knowledge.json +3846 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +61 -0
- package/dist/utils.js +49 -0
- package/dist/utils.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
import { ObjectRegistry, foreignKey, smrt, SmrtObject, SmrtCollection, field } from "@happyvertical/smrt-core";
|
|
2
|
+
import { P as ProfileType, a as Profile } from "./ProfileCollection-DU6wUJTO.js";
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import { N as NostrIdentityCollection, g as generateNostrKeypair, e as encryptPrivkey, a as NostrIdentity, v as verifyAuthEvent } from "./NostrIdentityCollection-DadQBHWy.js";
|
|
5
|
+
import { ApiKey } from "./ApiKey-B2LKEaP8.js";
|
|
6
|
+
import "./ApiKeyCollection-B6Op817e.js";
|
|
7
|
+
import "./AuditLogCollection-BYqCj0uE.js";
|
|
8
|
+
import "./ProfileAssetCollection-D_tk1kKG.js";
|
|
9
|
+
import "./ProfileMetadataCollection-DEhmljMY.js";
|
|
10
|
+
import "./ProfileMetafieldCollection-DMKhSHXX.js";
|
|
11
|
+
import "./ProfileRelationshipCollection-C0IM8UQR.js";
|
|
12
|
+
import "./ProfileRelationshipTermCollection-CXem_qT-.js";
|
|
13
|
+
import "./ProfileRelationshipTypeCollection-CF8YvLTV.js";
|
|
14
|
+
import "./ProfileRelationshipType-BXBLldea.js";
|
|
15
|
+
ObjectRegistry.registerPackageManifest(
|
|
16
|
+
new URL("./manifest.json", import.meta.url)
|
|
17
|
+
);
|
|
18
|
+
var __defProp$1 = Object.defineProperty;
|
|
19
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
20
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
21
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
22
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
23
|
+
if (decorator = decorators[i])
|
|
24
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
25
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
const DEFAULT_EXPIRATION_MINUTES = 15;
|
|
29
|
+
let MagicLinkToken = class extends SmrtObject {
|
|
30
|
+
nostrIdentityId;
|
|
31
|
+
/**
|
|
32
|
+
* SHA-256 hash of the token (never store plaintext)
|
|
33
|
+
*/
|
|
34
|
+
tokenHash = "";
|
|
35
|
+
/**
|
|
36
|
+
* Email this token was sent to
|
|
37
|
+
*/
|
|
38
|
+
email = "";
|
|
39
|
+
/**
|
|
40
|
+
* When this token expires
|
|
41
|
+
*/
|
|
42
|
+
expiresAt = new Date(
|
|
43
|
+
Date.now() + DEFAULT_EXPIRATION_MINUTES * 60 * 1e3
|
|
44
|
+
);
|
|
45
|
+
/**
|
|
46
|
+
* When this token was used (null = unused)
|
|
47
|
+
*/
|
|
48
|
+
usedAt = null;
|
|
49
|
+
/**
|
|
50
|
+
* IP address that requested the token (for rate limiting)
|
|
51
|
+
*/
|
|
52
|
+
requestedFromIp = "";
|
|
53
|
+
/**
|
|
54
|
+
* When this token was created (for rate limiting)
|
|
55
|
+
*/
|
|
56
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
57
|
+
constructor(options = {}) {
|
|
58
|
+
super(options);
|
|
59
|
+
if (options.nostrIdentityId) this.nostrIdentityId = options.nostrIdentityId;
|
|
60
|
+
if (options.tokenHash) this.tokenHash = options.tokenHash;
|
|
61
|
+
if (options.email) this.email = options.email;
|
|
62
|
+
if (options.expiresAt) this.expiresAt = options.expiresAt;
|
|
63
|
+
if (options.usedAt !== void 0) this.usedAt = options.usedAt;
|
|
64
|
+
if (options.requestedFromIp) this.requestedFromIp = options.requestedFromIp;
|
|
65
|
+
if (options.createdAt) this.createdAt = options.createdAt;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Hash a token using SHA-256
|
|
69
|
+
*/
|
|
70
|
+
static hashToken(token) {
|
|
71
|
+
return createHash("sha256").update(token).digest("hex");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate a new magic link token
|
|
75
|
+
* @param nostrIdentity - The identity this token is for
|
|
76
|
+
* @param email - The email address to send to
|
|
77
|
+
* @param options - Additional options
|
|
78
|
+
*/
|
|
79
|
+
static async generate(nostrIdentity, email, options = {}) {
|
|
80
|
+
const tokenBytes = randomBytes(32);
|
|
81
|
+
const token = tokenBytes.toString("base64url");
|
|
82
|
+
const tokenHash = MagicLinkToken.hashToken(token);
|
|
83
|
+
const expiresInMinutes = options.expiresInMinutes ?? DEFAULT_EXPIRATION_MINUTES;
|
|
84
|
+
const expiresAt = new Date(Date.now() + expiresInMinutes * 60 * 1e3);
|
|
85
|
+
const magicLinkToken = new MagicLinkToken({
|
|
86
|
+
db: options.db ?? nostrIdentity.options?.db,
|
|
87
|
+
nostrIdentityId: nostrIdentity.id,
|
|
88
|
+
tokenHash,
|
|
89
|
+
email: email.toLowerCase(),
|
|
90
|
+
expiresAt,
|
|
91
|
+
requestedFromIp: options.requestedFromIp || ""
|
|
92
|
+
});
|
|
93
|
+
await magicLinkToken.initialize();
|
|
94
|
+
await magicLinkToken.save();
|
|
95
|
+
return { token, magicLinkToken };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Verify a token and return the MagicLinkToken if valid
|
|
99
|
+
* @param token - The plaintext token to verify
|
|
100
|
+
* @param options - Database options
|
|
101
|
+
* @returns The MagicLinkToken if valid, null otherwise
|
|
102
|
+
*/
|
|
103
|
+
static async verify(token, options = {}) {
|
|
104
|
+
const tokenHash = MagicLinkToken.hashToken(token);
|
|
105
|
+
const { MagicLinkTokenCollection: MagicLinkTokenCollection2 } = await Promise.resolve().then(() => MagicLinkTokenCollection$1);
|
|
106
|
+
const collection = await MagicLinkTokenCollection2.create(options);
|
|
107
|
+
const magicLinkToken = await collection.findOne({
|
|
108
|
+
where: { tokenHash }
|
|
109
|
+
});
|
|
110
|
+
if (!magicLinkToken) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (!magicLinkToken.isValid()) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return magicLinkToken;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if this token is valid (not expired and not used)
|
|
120
|
+
*/
|
|
121
|
+
isValid() {
|
|
122
|
+
if (this.usedAt !== null) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (/* @__PURE__ */ new Date() > this.expiresAt) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if this token has expired
|
|
132
|
+
*/
|
|
133
|
+
isExpired() {
|
|
134
|
+
return /* @__PURE__ */ new Date() > this.expiresAt;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if this token has been used
|
|
138
|
+
*/
|
|
139
|
+
isUsed() {
|
|
140
|
+
return this.usedAt !== null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Mark this token as used
|
|
144
|
+
*/
|
|
145
|
+
async markUsed() {
|
|
146
|
+
this.usedAt = /* @__PURE__ */ new Date();
|
|
147
|
+
await this.save();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the NostrIdentity this token is for
|
|
151
|
+
*/
|
|
152
|
+
async getNostrIdentity() {
|
|
153
|
+
return await this.getRelated("nostrIdentityId");
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get time remaining until expiration in seconds
|
|
157
|
+
*/
|
|
158
|
+
getTimeRemainingSeconds() {
|
|
159
|
+
const now = /* @__PURE__ */ new Date();
|
|
160
|
+
const diff = this.expiresAt.getTime() - now.getTime();
|
|
161
|
+
return Math.max(0, Math.floor(diff / 1e3));
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
__decorateClass$2([
|
|
165
|
+
foreignKey("NostrIdentity", { required: true })
|
|
166
|
+
], MagicLinkToken.prototype, "nostrIdentityId", 2);
|
|
167
|
+
MagicLinkToken = __decorateClass$2([
|
|
168
|
+
smrt({
|
|
169
|
+
tableName: "magic_link_tokens",
|
|
170
|
+
api: { exclude: ["*"] },
|
|
171
|
+
// No API access - internal only
|
|
172
|
+
mcp: { exclude: ["*"] },
|
|
173
|
+
cli: { include: ["list"] }
|
|
174
|
+
// Admin visibility only
|
|
175
|
+
})
|
|
176
|
+
], MagicLinkToken);
|
|
177
|
+
class MagicLinkTokenCollection extends SmrtCollection {
|
|
178
|
+
static _itemClass = MagicLinkToken;
|
|
179
|
+
/**
|
|
180
|
+
* Find token by hash
|
|
181
|
+
*/
|
|
182
|
+
async findByTokenHash(tokenHash) {
|
|
183
|
+
return await this.findOne({
|
|
184
|
+
where: { tokenHash }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Find active (non-expired, non-used) tokens for an email
|
|
189
|
+
*/
|
|
190
|
+
async findActiveForEmail(email) {
|
|
191
|
+
const tokens = await this.list({
|
|
192
|
+
where: { email: email.toLowerCase() }
|
|
193
|
+
});
|
|
194
|
+
return tokens.filter((token) => token.isValid());
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Find tokens for a NostrIdentity
|
|
198
|
+
*/
|
|
199
|
+
async findByIdentity(nostrIdentityId) {
|
|
200
|
+
return await this.list({
|
|
201
|
+
where: { nostrIdentityId }
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Count tokens created for an email within a time window (for rate limiting)
|
|
206
|
+
* @param email - Email address
|
|
207
|
+
* @param withinMinutes - Time window in minutes
|
|
208
|
+
*/
|
|
209
|
+
async countRecentByEmail(email, withinMinutes) {
|
|
210
|
+
const cutoff = new Date(Date.now() - withinMinutes * 60 * 1e3);
|
|
211
|
+
const tokens = await this.list({
|
|
212
|
+
where: { email: email.toLowerCase() }
|
|
213
|
+
});
|
|
214
|
+
return tokens.filter((token) => {
|
|
215
|
+
const createdAt = token.createdAt;
|
|
216
|
+
return createdAt && new Date(createdAt) > cutoff;
|
|
217
|
+
}).length;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Count tokens created from an IP within a time window (for rate limiting)
|
|
221
|
+
* @param ip - IP address
|
|
222
|
+
* @param withinMinutes - Time window in minutes
|
|
223
|
+
*/
|
|
224
|
+
async countRecentByIp(ip, withinMinutes) {
|
|
225
|
+
const cutoff = new Date(Date.now() - withinMinutes * 60 * 1e3);
|
|
226
|
+
const tokens = await this.list({
|
|
227
|
+
where: { requestedFromIp: ip }
|
|
228
|
+
});
|
|
229
|
+
return tokens.filter((token) => {
|
|
230
|
+
const createdAt = token.createdAt;
|
|
231
|
+
return createdAt && new Date(createdAt) > cutoff;
|
|
232
|
+
}).length;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Verify a token and return it if valid
|
|
236
|
+
*/
|
|
237
|
+
async verify(token) {
|
|
238
|
+
const tokenHash = MagicLinkToken.hashToken(token);
|
|
239
|
+
const magicLinkToken = await this.findByTokenHash(tokenHash);
|
|
240
|
+
if (!magicLinkToken) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
if (!magicLinkToken.isValid()) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
return magicLinkToken;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Clean up expired tokens by deleting them
|
|
250
|
+
* @returns Number of tokens deleted
|
|
251
|
+
*/
|
|
252
|
+
async cleanupExpired() {
|
|
253
|
+
const allTokens = await this.list({});
|
|
254
|
+
let deleted = 0;
|
|
255
|
+
for (const token of allTokens) {
|
|
256
|
+
if (token.isExpired() || token.isUsed()) {
|
|
257
|
+
await token.delete();
|
|
258
|
+
deleted++;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return deleted;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Revoke all tokens for a NostrIdentity (e.g., when identity is deleted)
|
|
265
|
+
*/
|
|
266
|
+
async revokeForIdentity(nostrIdentityId) {
|
|
267
|
+
const tokens = await this.findByIdentity(nostrIdentityId);
|
|
268
|
+
let revoked = 0;
|
|
269
|
+
for (const token of tokens) {
|
|
270
|
+
if (!token.isUsed()) {
|
|
271
|
+
await token.markUsed();
|
|
272
|
+
revoked++;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return revoked;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Check if rate limit is exceeded for email
|
|
279
|
+
* @param email - Email address
|
|
280
|
+
* @param maxPerHour - Maximum tokens allowed per hour (default: 5)
|
|
281
|
+
*/
|
|
282
|
+
async isRateLimitedByEmail(email, maxPerHour = 5) {
|
|
283
|
+
const count = await this.countRecentByEmail(email, 60);
|
|
284
|
+
return count >= maxPerHour;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Check if rate limit is exceeded for IP
|
|
288
|
+
* @param ip - IP address
|
|
289
|
+
* @param maxPerHour - Maximum tokens allowed per hour (default: 10)
|
|
290
|
+
*/
|
|
291
|
+
async isRateLimitedByIp(ip, maxPerHour = 10) {
|
|
292
|
+
const count = await this.countRecentByIp(ip, 60);
|
|
293
|
+
return count >= maxPerHour;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const MagicLinkTokenCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
297
|
+
__proto__: null,
|
|
298
|
+
MagicLinkTokenCollection
|
|
299
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
300
|
+
function createMagicLinkService(config) {
|
|
301
|
+
const {
|
|
302
|
+
baseUrl,
|
|
303
|
+
verifyPath = "/auth/verify",
|
|
304
|
+
expiresInMinutes = 15,
|
|
305
|
+
maxTokensPerEmailPerHour = 5,
|
|
306
|
+
maxTokensPerIpPerHour = 10,
|
|
307
|
+
masterSecret,
|
|
308
|
+
sendEmail,
|
|
309
|
+
db
|
|
310
|
+
} = config;
|
|
311
|
+
return {
|
|
312
|
+
async initiate(email, options = {}) {
|
|
313
|
+
const normalizedEmail = email.toLowerCase().trim();
|
|
314
|
+
const { requestedFromIp, nip05Username } = options;
|
|
315
|
+
const identityCollection = await NostrIdentityCollection.create({ db });
|
|
316
|
+
const tokenCollection = await MagicLinkTokenCollection.create({ db });
|
|
317
|
+
if (await tokenCollection.isRateLimitedByEmail(
|
|
318
|
+
normalizedEmail,
|
|
319
|
+
maxTokensPerEmailPerHour
|
|
320
|
+
)) {
|
|
321
|
+
return {
|
|
322
|
+
success: false,
|
|
323
|
+
created: false,
|
|
324
|
+
error: "Too many requests for this email. Please try again later.",
|
|
325
|
+
errorCode: "RATE_LIMITED_EMAIL"
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (requestedFromIp && await tokenCollection.isRateLimitedByIp(
|
|
329
|
+
requestedFromIp,
|
|
330
|
+
maxTokensPerIpPerHour
|
|
331
|
+
)) {
|
|
332
|
+
return {
|
|
333
|
+
success: false,
|
|
334
|
+
created: false,
|
|
335
|
+
error: "Too many requests from this IP. Please try again later.",
|
|
336
|
+
errorCode: "RATE_LIMITED_IP"
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
let nostrIdentity = await identityCollection.findByEmail(normalizedEmail);
|
|
340
|
+
let profile = null;
|
|
341
|
+
let created = false;
|
|
342
|
+
if (!nostrIdentity) {
|
|
343
|
+
const { Person: Person2 } = await Promise.resolve().then(() => ProfileTypes);
|
|
344
|
+
profile = new Person2({
|
|
345
|
+
db,
|
|
346
|
+
email: normalizedEmail,
|
|
347
|
+
name: normalizedEmail.split("@")[0]
|
|
348
|
+
// Default name from email
|
|
349
|
+
});
|
|
350
|
+
await profile.initialize();
|
|
351
|
+
await profile.save();
|
|
352
|
+
const keypair = generateNostrKeypair();
|
|
353
|
+
const encrypted = encryptPrivkey(keypair.privkey, masterSecret);
|
|
354
|
+
const { NostrIdentity: NostrIdentityClass } = await import("./NostrIdentityCollection-DadQBHWy.js").then((n) => n.o);
|
|
355
|
+
nostrIdentity = new NostrIdentityClass({
|
|
356
|
+
db,
|
|
357
|
+
profileId: profile.id,
|
|
358
|
+
pubkey: keypair.pubkey,
|
|
359
|
+
encryptedPrivkey: encrypted.ciphertext,
|
|
360
|
+
encryptionIv: encrypted.iv,
|
|
361
|
+
encryptionTag: encrypted.tag,
|
|
362
|
+
email: normalizedEmail,
|
|
363
|
+
nip05Username: nip05Username?.toLowerCase() || normalizedEmail.split("@")[0]
|
|
364
|
+
});
|
|
365
|
+
await nostrIdentity.initialize();
|
|
366
|
+
await nostrIdentity.save();
|
|
367
|
+
created = true;
|
|
368
|
+
} else {
|
|
369
|
+
profile = await nostrIdentity.getProfile();
|
|
370
|
+
}
|
|
371
|
+
const { token } = await MagicLinkToken.generate(
|
|
372
|
+
nostrIdentity,
|
|
373
|
+
normalizedEmail,
|
|
374
|
+
{
|
|
375
|
+
expiresInMinutes,
|
|
376
|
+
requestedFromIp,
|
|
377
|
+
db
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
const magicLink = `${baseUrl}${verifyPath}?token=${encodeURIComponent(token)}`;
|
|
381
|
+
try {
|
|
382
|
+
await sendEmail(normalizedEmail, magicLink);
|
|
383
|
+
} catch (emailError) {
|
|
384
|
+
return {
|
|
385
|
+
success: false,
|
|
386
|
+
created,
|
|
387
|
+
nostrIdentity,
|
|
388
|
+
profile: profile || void 0,
|
|
389
|
+
error: "Failed to send email. Please try again.",
|
|
390
|
+
errorCode: "EMAIL_SEND_FAILED"
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
success: true,
|
|
395
|
+
created,
|
|
396
|
+
nostrIdentity,
|
|
397
|
+
profile: profile || void 0
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
async verify(token) {
|
|
401
|
+
const magicLinkToken = await MagicLinkToken.verify(token, { db });
|
|
402
|
+
if (!magicLinkToken) {
|
|
403
|
+
const tokenHash = MagicLinkToken.hashToken(token);
|
|
404
|
+
const tokenCollection = await MagicLinkTokenCollection.create({ db });
|
|
405
|
+
const existingToken = await tokenCollection.findByTokenHash(tokenHash);
|
|
406
|
+
if (!existingToken) {
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
error: "Invalid or expired token.",
|
|
410
|
+
errorCode: "NOT_FOUND"
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (existingToken.isUsed()) {
|
|
414
|
+
return {
|
|
415
|
+
success: false,
|
|
416
|
+
error: "This link has already been used.",
|
|
417
|
+
errorCode: "USED_TOKEN"
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
if (existingToken.isExpired()) {
|
|
421
|
+
return {
|
|
422
|
+
success: false,
|
|
423
|
+
error: "This link has expired. Please request a new one.",
|
|
424
|
+
errorCode: "EXPIRED_TOKEN"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
success: false,
|
|
429
|
+
error: "Invalid token.",
|
|
430
|
+
errorCode: "INVALID_TOKEN"
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
await magicLinkToken.markUsed();
|
|
434
|
+
const nostrIdentity = await magicLinkToken.getNostrIdentity();
|
|
435
|
+
if (!nostrIdentity) {
|
|
436
|
+
return {
|
|
437
|
+
success: false,
|
|
438
|
+
error: "Identity not found.",
|
|
439
|
+
errorCode: "NOT_FOUND"
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
await nostrIdentity.markVerified();
|
|
443
|
+
const profile = await nostrIdentity.getProfile();
|
|
444
|
+
const keypair = nostrIdentity.getKeypair(masterSecret);
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
profile: profile || void 0,
|
|
448
|
+
nostrIdentity,
|
|
449
|
+
keypair
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function createNip05Handler(config) {
|
|
455
|
+
const { db, defaultRelays = [], cacheTtlSeconds = 300 } = config;
|
|
456
|
+
return async function handleNip05Request(request) {
|
|
457
|
+
const { name } = request;
|
|
458
|
+
const collection = await NostrIdentityCollection.create({ db });
|
|
459
|
+
const response = await collection.getNip05Response(name || void 0);
|
|
460
|
+
if (defaultRelays.length > 0 && Object.keys(response.names).length > 0) {
|
|
461
|
+
response.relays = {};
|
|
462
|
+
for (const pubkey of Object.values(response.names)) {
|
|
463
|
+
response.relays[pubkey] = defaultRelays;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (name && Object.keys(response.names).length === 0) {
|
|
467
|
+
return {
|
|
468
|
+
body: { names: {} },
|
|
469
|
+
headers: {
|
|
470
|
+
"Content-Type": "application/json",
|
|
471
|
+
"Access-Control-Allow-Origin": "*",
|
|
472
|
+
"Cache-Control": `public, max-age=${cacheTtlSeconds}`
|
|
473
|
+
},
|
|
474
|
+
status: 200
|
|
475
|
+
// NIP-05 spec says return 200 with empty names, not 404
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
body: response,
|
|
480
|
+
headers: {
|
|
481
|
+
"Content-Type": "application/json",
|
|
482
|
+
"Access-Control-Allow-Origin": "*",
|
|
483
|
+
"Cache-Control": `public, max-age=${cacheTtlSeconds}`
|
|
484
|
+
},
|
|
485
|
+
status: 200
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function isValidNip05Identifier(identifier) {
|
|
490
|
+
const parts = identifier.split("@");
|
|
491
|
+
if (parts.length !== 2) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
const [localPart, domain] = parts;
|
|
495
|
+
if (!/^[a-z0-9_-]+$/i.test(localPart)) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
if (!/^[a-z0-9.-]+\.[a-z]{2,}$/i.test(domain)) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
function parseNip05Identifier(identifier) {
|
|
504
|
+
if (!isValidNip05Identifier(identifier)) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
const [localPart, domain] = identifier.split("@");
|
|
508
|
+
return { localPart, domain };
|
|
509
|
+
}
|
|
510
|
+
var __defProp = Object.defineProperty;
|
|
511
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
512
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
513
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
514
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
515
|
+
if (decorator = decorators[i])
|
|
516
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
517
|
+
if (kind && result) __defProp(target, key, result);
|
|
518
|
+
return result;
|
|
519
|
+
};
|
|
520
|
+
let OidcIdentity = class extends SmrtObject {
|
|
521
|
+
profileId;
|
|
522
|
+
provider = "";
|
|
523
|
+
issuer = "";
|
|
524
|
+
subject = "";
|
|
525
|
+
email = "";
|
|
526
|
+
lastUsedAt = null;
|
|
527
|
+
constructor(options = {}) {
|
|
528
|
+
super(options);
|
|
529
|
+
if (options.profileId) this.profileId = options.profileId;
|
|
530
|
+
if (options.provider) this.provider = options.provider;
|
|
531
|
+
if (options.issuer) this.issuer = options.issuer;
|
|
532
|
+
if (options.subject) this.subject = options.subject;
|
|
533
|
+
if (options.email) this.email = options.email;
|
|
534
|
+
if (options.lastUsedAt !== void 0) this.lastUsedAt = options.lastUsedAt;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get the linked Profile
|
|
538
|
+
*/
|
|
539
|
+
async getProfile() {
|
|
540
|
+
return await this.getRelated("profileId");
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Find identity by issuer and subject
|
|
544
|
+
*/
|
|
545
|
+
static async findBySubject(issuer, subject, options = {}) {
|
|
546
|
+
const { OidcIdentityCollection: OidcIdentityCollection2 } = await Promise.resolve().then(() => OidcIdentityCollection$1);
|
|
547
|
+
const collection = await OidcIdentityCollection2.create(options);
|
|
548
|
+
return await collection.findOne({
|
|
549
|
+
where: { issuer, subject }
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Find or create identity for a profile
|
|
554
|
+
*/
|
|
555
|
+
static async findOrCreate(profile, oidcData, options = {}) {
|
|
556
|
+
const existing = await OidcIdentity.findBySubject(
|
|
557
|
+
oidcData.issuer,
|
|
558
|
+
oidcData.subject,
|
|
559
|
+
options
|
|
560
|
+
);
|
|
561
|
+
if (existing) {
|
|
562
|
+
existing.lastUsedAt = /* @__PURE__ */ new Date();
|
|
563
|
+
if (oidcData.email) existing.email = oidcData.email;
|
|
564
|
+
await existing.save();
|
|
565
|
+
return existing;
|
|
566
|
+
}
|
|
567
|
+
const identity = new OidcIdentity({
|
|
568
|
+
...options,
|
|
569
|
+
profileId: profile.id,
|
|
570
|
+
provider: oidcData.provider,
|
|
571
|
+
issuer: oidcData.issuer,
|
|
572
|
+
subject: oidcData.subject,
|
|
573
|
+
email: oidcData.email || ""
|
|
574
|
+
});
|
|
575
|
+
await identity.initialize();
|
|
576
|
+
await identity.save();
|
|
577
|
+
return identity;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Record usage of this identity
|
|
581
|
+
*/
|
|
582
|
+
async recordUsage() {
|
|
583
|
+
this.lastUsedAt = /* @__PURE__ */ new Date();
|
|
584
|
+
await this.save();
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
__decorateClass$1([
|
|
588
|
+
foreignKey("Profile", { required: true })
|
|
589
|
+
], OidcIdentity.prototype, "profileId", 2);
|
|
590
|
+
__decorateClass$1([
|
|
591
|
+
field({ type: "text" })
|
|
592
|
+
], OidcIdentity.prototype, "provider", 2);
|
|
593
|
+
__decorateClass$1([
|
|
594
|
+
field({ type: "text" })
|
|
595
|
+
], OidcIdentity.prototype, "issuer", 2);
|
|
596
|
+
__decorateClass$1([
|
|
597
|
+
field({ type: "text" })
|
|
598
|
+
], OidcIdentity.prototype, "subject", 2);
|
|
599
|
+
__decorateClass$1([
|
|
600
|
+
field({ type: "text" })
|
|
601
|
+
], OidcIdentity.prototype, "email", 2);
|
|
602
|
+
__decorateClass$1([
|
|
603
|
+
field({ type: "datetime", nullable: true })
|
|
604
|
+
], OidcIdentity.prototype, "lastUsedAt", 2);
|
|
605
|
+
OidcIdentity = __decorateClass$1([
|
|
606
|
+
smrt({
|
|
607
|
+
tableName: "oidc_identities",
|
|
608
|
+
api: { exclude: ["delete"] },
|
|
609
|
+
mcp: { include: ["list", "get"] },
|
|
610
|
+
cli: { include: ["list", "get"] }
|
|
611
|
+
})
|
|
612
|
+
], OidcIdentity);
|
|
613
|
+
async function resolveIdentity(context) {
|
|
614
|
+
const options = { db: context.db };
|
|
615
|
+
if (context.apiKey) {
|
|
616
|
+
const apiKey = await ApiKey.verify(context.apiKey, options);
|
|
617
|
+
if (apiKey) {
|
|
618
|
+
const profile = await apiKey.getProfile();
|
|
619
|
+
if (profile) {
|
|
620
|
+
return {
|
|
621
|
+
profile,
|
|
622
|
+
source: "api_key",
|
|
623
|
+
apiKey
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (context.oidcSession?.sub && context.oidcSession?.iss) {
|
|
629
|
+
const oidcIdentity = await OidcIdentity.findBySubject(
|
|
630
|
+
context.oidcSession.iss,
|
|
631
|
+
context.oidcSession.sub,
|
|
632
|
+
options
|
|
633
|
+
);
|
|
634
|
+
if (oidcIdentity) {
|
|
635
|
+
await oidcIdentity.recordUsage();
|
|
636
|
+
const profile = await oidcIdentity.getProfile();
|
|
637
|
+
if (profile) {
|
|
638
|
+
return {
|
|
639
|
+
profile,
|
|
640
|
+
source: "oidc",
|
|
641
|
+
oidcIdentity
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (context.nostrAuth?.event && context.nostrAuth?.challenge) {
|
|
647
|
+
const { event, challenge } = context.nostrAuth;
|
|
648
|
+
const verifyResult = verifyAuthEvent(event, challenge);
|
|
649
|
+
if (verifyResult.valid) {
|
|
650
|
+
const nostrIdentity = await NostrIdentity.findByPubkey(
|
|
651
|
+
event.pubkey,
|
|
652
|
+
options
|
|
653
|
+
);
|
|
654
|
+
if (nostrIdentity) {
|
|
655
|
+
await nostrIdentity.recordUsage();
|
|
656
|
+
const profile = await nostrIdentity.getProfile();
|
|
657
|
+
if (profile) {
|
|
658
|
+
return {
|
|
659
|
+
profile,
|
|
660
|
+
source: "nostr",
|
|
661
|
+
nostrIdentity
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (context.actor) {
|
|
668
|
+
const profile = await findProfileByExternalId(
|
|
669
|
+
"github",
|
|
670
|
+
context.actor,
|
|
671
|
+
options
|
|
672
|
+
);
|
|
673
|
+
if (profile) {
|
|
674
|
+
return {
|
|
675
|
+
profile,
|
|
676
|
+
source: "actor"
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return {
|
|
681
|
+
profile: null,
|
|
682
|
+
source: "none"
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
async function findProfileByExternalId(provider, externalId, options) {
|
|
686
|
+
const { OidcIdentityCollection: OidcIdentityCollection2 } = await Promise.resolve().then(() => OidcIdentityCollection$1);
|
|
687
|
+
const oidcCollection = await OidcIdentityCollection2.create(options);
|
|
688
|
+
const identities = await oidcCollection.findByProvider(provider);
|
|
689
|
+
for (const identity of identities) {
|
|
690
|
+
if (identity.subject === externalId || identity.email?.includes(externalId)) {
|
|
691
|
+
const profile = await identity.getProfile();
|
|
692
|
+
if (profile) return profile;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
const { ProfileCollection } = await import("./ProfileCollection-DU6wUJTO.js").then((n) => n.d);
|
|
696
|
+
const profileCollection = await ProfileCollection.create(options);
|
|
697
|
+
const profiles = await profileCollection.list({
|
|
698
|
+
where: {
|
|
699
|
+
email: `${externalId}@users.noreply.github.com`
|
|
700
|
+
},
|
|
701
|
+
limit: 1
|
|
702
|
+
});
|
|
703
|
+
if (profiles.length > 0) {
|
|
704
|
+
return profiles[0];
|
|
705
|
+
}
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
async function createProfileFromOidc(claims, provider, options) {
|
|
709
|
+
const existingIdentity = await OidcIdentity.findBySubject(
|
|
710
|
+
claims.iss,
|
|
711
|
+
claims.sub,
|
|
712
|
+
options
|
|
713
|
+
);
|
|
714
|
+
if (existingIdentity) {
|
|
715
|
+
const profile2 = await existingIdentity.getProfile();
|
|
716
|
+
if (profile2) {
|
|
717
|
+
if (claims.email) existingIdentity.email = claims.email;
|
|
718
|
+
existingIdentity.lastUsedAt = /* @__PURE__ */ new Date();
|
|
719
|
+
await existingIdentity.save();
|
|
720
|
+
return {
|
|
721
|
+
profile: profile2,
|
|
722
|
+
oidcIdentity: existingIdentity,
|
|
723
|
+
created: false
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (claims.email && claims.email_verified) {
|
|
728
|
+
const { ProfileCollection } = await import("./ProfileCollection-DU6wUJTO.js").then((n) => n.d);
|
|
729
|
+
const profileCollection = await ProfileCollection.create(options);
|
|
730
|
+
const existingProfile = await profileCollection.findByEmail(claims.email);
|
|
731
|
+
if (existingProfile) {
|
|
732
|
+
const oidcIdentity2 = await OidcIdentity.findOrCreate(
|
|
733
|
+
existingProfile,
|
|
734
|
+
{
|
|
735
|
+
provider,
|
|
736
|
+
issuer: claims.iss,
|
|
737
|
+
subject: claims.sub,
|
|
738
|
+
email: claims.email
|
|
739
|
+
},
|
|
740
|
+
options
|
|
741
|
+
);
|
|
742
|
+
return {
|
|
743
|
+
profile: existingProfile,
|
|
744
|
+
oidcIdentity: oidcIdentity2,
|
|
745
|
+
created: false
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
const { Person: Person2 } = await Promise.resolve().then(() => ProfileTypes);
|
|
750
|
+
const { ProfileTypeCollection: ProfileTypeCollection2 } = await Promise.resolve().then(() => ProfileTypeCollection$1);
|
|
751
|
+
const typeCollection = await ProfileTypeCollection2.create(options);
|
|
752
|
+
let personType = await typeCollection.getBySlug("person");
|
|
753
|
+
if (!personType) {
|
|
754
|
+
const { ProfileType: ProfileType2 } = await import("./ProfileCollection-DU6wUJTO.js").then((n) => n.c);
|
|
755
|
+
personType = new ProfileType2({
|
|
756
|
+
...options,
|
|
757
|
+
slug: "person",
|
|
758
|
+
name: "Person",
|
|
759
|
+
description: "Individual person profile"
|
|
760
|
+
});
|
|
761
|
+
await personType.initialize();
|
|
762
|
+
await personType.save();
|
|
763
|
+
}
|
|
764
|
+
const profile = new Person2({
|
|
765
|
+
...options,
|
|
766
|
+
typeId: personType.id,
|
|
767
|
+
email: claims.email || "",
|
|
768
|
+
name: claims.name || claims.preferred_username || claims.sub
|
|
769
|
+
});
|
|
770
|
+
await profile.initialize();
|
|
771
|
+
await profile.save();
|
|
772
|
+
const oidcIdentity = await OidcIdentity.findOrCreate(
|
|
773
|
+
profile,
|
|
774
|
+
{
|
|
775
|
+
provider,
|
|
776
|
+
issuer: claims.iss,
|
|
777
|
+
subject: claims.sub,
|
|
778
|
+
email: claims.email
|
|
779
|
+
},
|
|
780
|
+
options
|
|
781
|
+
);
|
|
782
|
+
return {
|
|
783
|
+
profile,
|
|
784
|
+
oidcIdentity,
|
|
785
|
+
created: true
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
async function createProfileFromNostr(email, nostrData, options) {
|
|
789
|
+
const normalizedEmail = email.toLowerCase();
|
|
790
|
+
const existingIdentity = await NostrIdentity.findByEmail(
|
|
791
|
+
normalizedEmail,
|
|
792
|
+
options
|
|
793
|
+
);
|
|
794
|
+
if (existingIdentity) {
|
|
795
|
+
const profile2 = await existingIdentity.getProfile();
|
|
796
|
+
if (profile2) {
|
|
797
|
+
return {
|
|
798
|
+
profile: profile2,
|
|
799
|
+
nostrIdentity: existingIdentity,
|
|
800
|
+
created: false
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
const { Person: Person2 } = await Promise.resolve().then(() => ProfileTypes);
|
|
805
|
+
const { ProfileTypeCollection: ProfileTypeCollection2 } = await Promise.resolve().then(() => ProfileTypeCollection$1);
|
|
806
|
+
const typeCollection = await ProfileTypeCollection2.create(options);
|
|
807
|
+
let personType = await typeCollection.getBySlug("person");
|
|
808
|
+
if (!personType) {
|
|
809
|
+
const { ProfileType: ProfileType2 } = await import("./ProfileCollection-DU6wUJTO.js").then((n) => n.c);
|
|
810
|
+
personType = new ProfileType2({
|
|
811
|
+
...options,
|
|
812
|
+
slug: "person",
|
|
813
|
+
name: "Person",
|
|
814
|
+
description: "Individual person profile"
|
|
815
|
+
});
|
|
816
|
+
await personType.initialize();
|
|
817
|
+
await personType.save();
|
|
818
|
+
}
|
|
819
|
+
const profile = new Person2({
|
|
820
|
+
...options,
|
|
821
|
+
typeId: personType.id,
|
|
822
|
+
email: normalizedEmail,
|
|
823
|
+
name: normalizedEmail.split("@")[0]
|
|
824
|
+
// Default name from email
|
|
825
|
+
});
|
|
826
|
+
await profile.initialize();
|
|
827
|
+
await profile.save();
|
|
828
|
+
const nostrIdentity = new NostrIdentity({
|
|
829
|
+
...options,
|
|
830
|
+
profileId: profile.id,
|
|
831
|
+
pubkey: nostrData.pubkey,
|
|
832
|
+
encryptedPrivkey: nostrData.encryptedPrivkey,
|
|
833
|
+
encryptionIv: nostrData.encryptionIv,
|
|
834
|
+
encryptionTag: nostrData.encryptionTag,
|
|
835
|
+
email: normalizedEmail,
|
|
836
|
+
nip05Username: nostrData.nip05Username?.toLowerCase() || normalizedEmail.split("@")[0]
|
|
837
|
+
});
|
|
838
|
+
await nostrIdentity.initialize();
|
|
839
|
+
await nostrIdentity.save();
|
|
840
|
+
return {
|
|
841
|
+
profile,
|
|
842
|
+
nostrIdentity,
|
|
843
|
+
created: true
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
class OidcIdentityCollection extends SmrtCollection {
|
|
847
|
+
static _itemClass = OidcIdentity;
|
|
848
|
+
/**
|
|
849
|
+
* Find identities for a profile
|
|
850
|
+
*/
|
|
851
|
+
async findByProfile(profileId) {
|
|
852
|
+
return await this.list({
|
|
853
|
+
where: { profileId }
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Find identity by issuer and subject
|
|
858
|
+
*/
|
|
859
|
+
async findBySubject(issuer, subject) {
|
|
860
|
+
return await this.findOne({
|
|
861
|
+
where: { issuer, subject }
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Find identities by provider
|
|
866
|
+
*/
|
|
867
|
+
async findByProvider(provider) {
|
|
868
|
+
return await this.list({
|
|
869
|
+
where: { provider }
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Link a new OIDC identity to a profile
|
|
874
|
+
*/
|
|
875
|
+
async linkToProfile(profile, oidcData) {
|
|
876
|
+
const existing = await this.findBySubject(
|
|
877
|
+
oidcData.issuer,
|
|
878
|
+
oidcData.subject
|
|
879
|
+
);
|
|
880
|
+
if (existing) {
|
|
881
|
+
existing.lastUsedAt = /* @__PURE__ */ new Date();
|
|
882
|
+
if (oidcData.email) existing.email = oidcData.email;
|
|
883
|
+
await existing.save();
|
|
884
|
+
return existing;
|
|
885
|
+
}
|
|
886
|
+
const identity = new OidcIdentity({
|
|
887
|
+
...this.options,
|
|
888
|
+
profileId: profile.id,
|
|
889
|
+
provider: oidcData.provider,
|
|
890
|
+
issuer: oidcData.issuer,
|
|
891
|
+
subject: oidcData.subject,
|
|
892
|
+
email: oidcData.email || ""
|
|
893
|
+
});
|
|
894
|
+
await identity.initialize();
|
|
895
|
+
await identity.save();
|
|
896
|
+
return identity;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Unlink an OIDC identity from a profile
|
|
900
|
+
*/
|
|
901
|
+
async unlinkFromProfile(profileId, issuer, subject) {
|
|
902
|
+
const identity = await this.findOne({
|
|
903
|
+
where: { profileId, issuer, subject }
|
|
904
|
+
});
|
|
905
|
+
if (identity) {
|
|
906
|
+
await identity.delete();
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const OidcIdentityCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
913
|
+
__proto__: null,
|
|
914
|
+
OidcIdentityCollection
|
|
915
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
916
|
+
class ProfileTypeCollection extends SmrtCollection {
|
|
917
|
+
static _itemClass = ProfileType;
|
|
918
|
+
/**
|
|
919
|
+
* Get profile type by slug
|
|
920
|
+
*
|
|
921
|
+
* @param slug - The slug to search for
|
|
922
|
+
* @returns ProfileType instance or null
|
|
923
|
+
*/
|
|
924
|
+
async getBySlug(slug) {
|
|
925
|
+
return await this.get({ slug });
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get or create a profile type by slug
|
|
929
|
+
*
|
|
930
|
+
* @param slug - The slug to search for
|
|
931
|
+
* @param defaults - Default values if creating
|
|
932
|
+
* @returns ProfileType instance
|
|
933
|
+
*/
|
|
934
|
+
async getOrCreateBySlug(slug, defaults) {
|
|
935
|
+
const existing = await this.getBySlug(slug);
|
|
936
|
+
if (existing) return existing;
|
|
937
|
+
const profileType = await this.create({ slug, ...defaults });
|
|
938
|
+
await profileType.save();
|
|
939
|
+
return profileType;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const ProfileTypeCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
943
|
+
__proto__: null,
|
|
944
|
+
ProfileTypeCollection
|
|
945
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
946
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
947
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
948
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
949
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
950
|
+
if (decorator = decorators[i])
|
|
951
|
+
result = decorator(result) || result;
|
|
952
|
+
return result;
|
|
953
|
+
};
|
|
954
|
+
let Person = class extends Profile {
|
|
955
|
+
constructor(options = {}) {
|
|
956
|
+
super(options);
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
Person = __decorateClass([
|
|
960
|
+
smrt({
|
|
961
|
+
tableStrategy: "sti"
|
|
962
|
+
})
|
|
963
|
+
], Person);
|
|
964
|
+
let Organization = class extends Profile {
|
|
965
|
+
constructor(options = {}) {
|
|
966
|
+
super(options);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
Organization = __decorateClass([
|
|
970
|
+
smrt({
|
|
971
|
+
tableStrategy: "sti"
|
|
972
|
+
})
|
|
973
|
+
], Organization);
|
|
974
|
+
let Bot = class extends Profile {
|
|
975
|
+
constructor(options = {}) {
|
|
976
|
+
super(options);
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
Bot = __decorateClass([
|
|
980
|
+
smrt({
|
|
981
|
+
tableStrategy: "sti"
|
|
982
|
+
})
|
|
983
|
+
], Bot);
|
|
984
|
+
const ProfileTypes = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
985
|
+
__proto__: null,
|
|
986
|
+
get Bot() {
|
|
987
|
+
return Bot;
|
|
988
|
+
},
|
|
989
|
+
get Organization() {
|
|
990
|
+
return Organization;
|
|
991
|
+
},
|
|
992
|
+
get Person() {
|
|
993
|
+
return Person;
|
|
994
|
+
}
|
|
995
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
996
|
+
export {
|
|
997
|
+
Bot as B,
|
|
998
|
+
MagicLinkToken as M,
|
|
999
|
+
OidcIdentity as O,
|
|
1000
|
+
Person as P,
|
|
1001
|
+
MagicLinkTokenCollection as a,
|
|
1002
|
+
OidcIdentityCollection as b,
|
|
1003
|
+
Organization as c,
|
|
1004
|
+
ProfileTypeCollection as d,
|
|
1005
|
+
createMagicLinkService as e,
|
|
1006
|
+
createNip05Handler as f,
|
|
1007
|
+
createProfileFromNostr as g,
|
|
1008
|
+
createProfileFromOidc as h,
|
|
1009
|
+
isValidNip05Identifier as i,
|
|
1010
|
+
OidcIdentityCollection$1 as j,
|
|
1011
|
+
parseNip05Identifier as p,
|
|
1012
|
+
resolveIdentity as r
|
|
1013
|
+
};
|
|
1014
|
+
//# sourceMappingURL=index-jFtOWsAV.js.map
|