@headless-lms/adapter-auth 0.3.0 → 0.4.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/dist/access-grant.d.ts +2 -4
- package/dist/access-grant.js +11 -20
- package/dist/access-grant.js.map +1 -1
- package/dist/access.d.ts +17 -78
- package/dist/access.js +7 -45
- package/dist/access.js.map +1 -1
- package/dist/better-auth.d.ts +598 -461
- package/dist/better-auth.js +221 -156
- package/dist/better-auth.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/package.json +3 -3
package/dist/better-auth.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { accessTokenAuthorization, consentOrgId, grantFromClaims, isCompactJws, needsOrgChoice, orgIdFromMetadata,
|
|
2
|
-
import { ac, roles } from "./access.js";
|
|
1
|
+
import { accessTokenAuthorization, consentOrgId, grantFromClaims, isCompactJws, needsOrgChoice, orgIdFromMetadata, roleFromMetadata } from "./access-grant.js";
|
|
2
|
+
import { OWNER_MEMBERSHIP, PARTICIPANT_MEMBERSHIP, ac, roles } from "./access.js";
|
|
3
3
|
import { withLoopbackNativeDefault } from "./client-registration.js";
|
|
4
4
|
import { ACCESS_TOKEN_TTL_SECONDS, ORG_CLAIM } from "@headless-lms/core/identity";
|
|
5
|
-
import {
|
|
5
|
+
import { ALL_PERMISSIONS, OrganizationRuleError } from "@headless-lms/core/organizations";
|
|
6
6
|
import { createDpopReplayStore, enforceDpopBinding, isDpopBindingError, verifyJwsAccessToken } from "better-auth/oauth2";
|
|
7
7
|
import { fromNodeHeaders } from "better-auth/node";
|
|
8
8
|
import { APIError, betterAuth } from "better-auth";
|
|
@@ -10,20 +10,30 @@ import { createAuthMiddleware } from "better-auth/api";
|
|
|
10
10
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
11
11
|
import * as authSchema from "@headless-lms/adapter-db/schema/better-auth";
|
|
12
12
|
import { prefixId } from "@headless-lms/core/shared/id";
|
|
13
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
14
|
-
import {
|
|
13
|
+
import { ConflictError, NotFoundError } from "@headless-lms/core/shared/errors";
|
|
14
|
+
import { jwt, magicLink, organization } from "better-auth/plugins";
|
|
15
|
+
import { getOrgAdapter } from "better-auth/plugins/organization";
|
|
15
16
|
import { oauthProviderAuthServerMetadata } from "@better-auth/oauth-provider";
|
|
16
17
|
import { mcp } from "@better-auth/mcp";
|
|
17
18
|
import { cimd } from "@better-auth/cimd";
|
|
18
19
|
import { fetchClientMetadataResource } from "@better-auth/cimd/node";
|
|
19
20
|
import { apiKey } from "@better-auth/api-key";
|
|
20
|
-
import { eq } from "drizzle-orm";
|
|
21
|
+
import { and, count, eq, sql } from "drizzle-orm";
|
|
21
22
|
//#region src/better-auth.ts
|
|
22
23
|
const JWT_ALG = "EdDSA";
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const API_KEY_PREFIX = "hlms_";
|
|
25
|
+
const API_KEY_NAME_MAX = 80;
|
|
26
|
+
const DAY_SECONDS = 1440 * 60;
|
|
27
|
+
const OPEN_ORG_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
28
|
+
"/organization/set-active",
|
|
29
|
+
"/organization/list",
|
|
30
|
+
"/organization/get-active-member"
|
|
31
|
+
]);
|
|
32
|
+
const CORE_API_KEY_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
33
|
+
"/api-key/get",
|
|
34
|
+
"/api-key/list",
|
|
35
|
+
"/api-key/update",
|
|
36
|
+
"/api-key/delete"
|
|
27
37
|
]);
|
|
28
38
|
const OAUTH_CLIENT_WRITES = /* @__PURE__ */ new Set([
|
|
29
39
|
"/oauth2/create-client",
|
|
@@ -42,8 +52,8 @@ function mcpOAuthPlugins(opts) {
|
|
|
42
52
|
resource: opts.mcp.resource,
|
|
43
53
|
loginPage: opts.mcp.loginPage,
|
|
44
54
|
consentPage: opts.mcp.consentPage,
|
|
45
|
-
scopes: [...
|
|
46
|
-
clientRegistrationDefaultScopes: [...
|
|
55
|
+
scopes: [...ALL_PERMISSIONS, "offline_access"],
|
|
56
|
+
clientRegistrationDefaultScopes: [...ALL_PERMISSIONS, "offline_access"],
|
|
47
57
|
accessTokenExpiresIn: ACCESS_TOKEN_TTL_SECONDS,
|
|
48
58
|
allowDynamicClientRegistration: true,
|
|
49
59
|
allowUnauthenticatedClientRegistration: true,
|
|
@@ -71,6 +81,10 @@ function buildAuthOptions(opts) {
|
|
|
71
81
|
baseURL: opts.baseUrl,
|
|
72
82
|
secret: opts.secret,
|
|
73
83
|
trustedOrigins: opts.trustedOrigins,
|
|
84
|
+
logger: { log: (level, message, ...args) => {
|
|
85
|
+
const [first] = args;
|
|
86
|
+
opts.logger[level](message, first instanceof Error ? { err: first } : args.length > 0 ? { details: args } : void 0);
|
|
87
|
+
} },
|
|
74
88
|
session: { cookieCache: {
|
|
75
89
|
enabled: true,
|
|
76
90
|
maxAge: 300
|
|
@@ -105,7 +119,8 @@ function buildAuthOptions(opts) {
|
|
|
105
119
|
organization({
|
|
106
120
|
ac,
|
|
107
121
|
roles,
|
|
108
|
-
creatorRole:
|
|
122
|
+
creatorRole: OWNER_MEMBERSHIP,
|
|
123
|
+
membershipLimit: Number.MAX_SAFE_INTEGER,
|
|
109
124
|
organizationHooks: opts.hooks
|
|
110
125
|
}),
|
|
111
126
|
jwt({
|
|
@@ -115,37 +130,15 @@ function buildAuthOptions(opts) {
|
|
|
115
130
|
...opts.mcp.oauthEnabled ? mcpOAuthPlugins(opts) : [],
|
|
116
131
|
apiKey({
|
|
117
132
|
enableMetadata: true,
|
|
133
|
+
defaultPrefix: API_KEY_PREFIX,
|
|
134
|
+
maximumNameLength: API_KEY_NAME_MAX,
|
|
118
135
|
rateLimit: { enabled: false }
|
|
119
|
-
}),
|
|
120
|
-
customSession(async ({ user, session }) => {
|
|
121
|
-
const memberships = await opts.db.select({
|
|
122
|
-
role: authSchema.member.role,
|
|
123
|
-
id: authSchema.organization.id,
|
|
124
|
-
name: authSchema.organization.name,
|
|
125
|
-
slug: authSchema.organization.slug
|
|
126
|
-
}).from(authSchema.member).innerJoin(authSchema.organization, eq(authSchema.member.organizationId, authSchema.organization.id)).where(eq(authSchema.member.userId, user.id));
|
|
127
|
-
const activeOrgId = session.activeOrganizationId ?? null;
|
|
128
|
-
const active = memberships.find((m) => m.id === activeOrgId) ?? null;
|
|
129
|
-
return {
|
|
130
|
-
user,
|
|
131
|
-
session,
|
|
132
|
-
activeMemberRole: active?.role ?? null,
|
|
133
|
-
activeOrganization: active ? {
|
|
134
|
-
id: active.id,
|
|
135
|
-
name: active.name,
|
|
136
|
-
slug: active.slug
|
|
137
|
-
} : null,
|
|
138
|
-
organizations: memberships.map(({ id, name, slug }) => ({
|
|
139
|
-
id,
|
|
140
|
-
name,
|
|
141
|
-
slug
|
|
142
|
-
}))
|
|
143
|
-
};
|
|
144
136
|
})
|
|
145
137
|
],
|
|
146
138
|
hooks: { before: createAuthMiddleware(async (ctx) => {
|
|
147
139
|
if (ctx.path === "/api-key/create" && (ctx.request || ctx.headers)) throw new APIError("FORBIDDEN", { message: "API keys are created through the LMS" });
|
|
148
|
-
if (
|
|
140
|
+
if (ctx.request && ctx.path.startsWith("/organization/") && !OPEN_ORG_ENDPOINTS.has(ctx.path)) throw new APIError("FORBIDDEN", { message: "Organizations are managed through the LMS" });
|
|
141
|
+
if (ctx.request && CORE_API_KEY_ENDPOINTS.has(ctx.path)) throw new APIError("FORBIDDEN", { message: "API keys are managed through the LMS" });
|
|
149
142
|
if (OAUTH_CLIENT_WRITES.has(ctx.path) && (ctx.request || ctx.headers)) throw new APIError("FORBIDDEN", { message: "Apps register themselves when they connect" });
|
|
150
143
|
if (ctx.path === "/api-key/update" && ctx.body?.metadata !== void 0) throw new APIError("FORBIDDEN", { message: "An API key stays in the organization it was made for" });
|
|
151
144
|
if (ctx.path !== "/oauth2/register") return;
|
|
@@ -161,14 +154,34 @@ function buildAuthOptions(opts) {
|
|
|
161
154
|
function createAuth(opts) {
|
|
162
155
|
return betterAuth(buildAuthOptions(opts));
|
|
163
156
|
}
|
|
157
|
+
function toStored(key) {
|
|
158
|
+
return {
|
|
159
|
+
id: key.id,
|
|
160
|
+
name: key.name ?? null,
|
|
161
|
+
start: key.start ?? null,
|
|
162
|
+
roleKey: roleFromMetadata(key.metadata),
|
|
163
|
+
createdAt: new Date(key.createdAt),
|
|
164
|
+
lastUsedAt: key.lastRequest ? new Date(key.lastRequest) : null,
|
|
165
|
+
expiresAt: key.expiresAt ? new Date(key.expiresAt) : null
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function apiKeyHeader(headers) {
|
|
169
|
+
const header = headers["x-api-key"];
|
|
170
|
+
if (typeof header === "string" && header.length > 0) return header;
|
|
171
|
+
const authorization = accessTokenAuthorization(headers.authorization);
|
|
172
|
+
if (authorization?.scheme === "Bearer" && !isCompactJws(authorization.token)) return authorization.token;
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
164
175
|
var BetterAuth = class {
|
|
165
176
|
auth;
|
|
177
|
+
db;
|
|
166
178
|
logger;
|
|
167
179
|
mcp;
|
|
168
180
|
issuer;
|
|
169
181
|
jwksCacheKey = {};
|
|
170
182
|
constructor(opts, auth) {
|
|
171
183
|
this.auth = auth ?? createAuth(opts);
|
|
184
|
+
this.db = opts.db;
|
|
172
185
|
this.logger = opts.logger;
|
|
173
186
|
this.mcp = opts.mcp;
|
|
174
187
|
this.issuer = `${opts.baseUrl.replace(/\/+$/, "")}/api/auth`;
|
|
@@ -183,13 +196,16 @@ var BetterAuth = class {
|
|
|
183
196
|
return oauthProviderAuthServerMetadata(this.auth)(request);
|
|
184
197
|
}
|
|
185
198
|
async verifyAccess(request) {
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
199
|
+
const key = apiKeyHeader(request.headers);
|
|
200
|
+
if (key) return this.grantForKey(key);
|
|
188
201
|
const authorization = accessTokenAuthorization(request.headers.authorization);
|
|
189
202
|
if (!authorization) return null;
|
|
190
|
-
if (authorization.scheme === "Bearer" && !isCompactJws(authorization.token)) return this.verifyApiKey(authorization.token);
|
|
191
203
|
return this.mcp.oauthEnabled ? this.verifyAccessToken(authorization, request) : null;
|
|
192
204
|
}
|
|
205
|
+
async verifyApiKey(headers) {
|
|
206
|
+
const key = apiKeyHeader(headers);
|
|
207
|
+
return key ? this.grantForKey(key) : null;
|
|
208
|
+
}
|
|
193
209
|
async verifyAccessToken(authorization, request) {
|
|
194
210
|
let claims;
|
|
195
211
|
try {
|
|
@@ -239,37 +255,82 @@ var BetterAuth = class {
|
|
|
239
255
|
}
|
|
240
256
|
]
|
|
241
257
|
});
|
|
242
|
-
|
|
258
|
+
if (!consent || !("scopes" in grant.limit)) return null;
|
|
259
|
+
return {
|
|
243
260
|
...grant,
|
|
244
|
-
scopes: grant.scopes.filter((scope) => consent.scopes.includes(scope))
|
|
245
|
-
}
|
|
261
|
+
limit: { scopes: grant.limit.scopes.filter((scope) => consent.scopes.includes(scope)) }
|
|
262
|
+
};
|
|
246
263
|
}
|
|
247
|
-
async
|
|
264
|
+
async grantForKey(key) {
|
|
248
265
|
const result = await this.pluginApi.verifyApiKey({ body: { key } });
|
|
249
266
|
if (!result.valid || !result.key) return null;
|
|
250
267
|
const orgId = orgIdFromMetadata(result.key.metadata);
|
|
251
|
-
|
|
268
|
+
const roleKey = roleFromMetadata(result.key.metadata);
|
|
269
|
+
if (!orgId || !roleKey) return null;
|
|
252
270
|
return {
|
|
253
271
|
userId: result.key.referenceId,
|
|
254
272
|
orgId,
|
|
255
|
-
|
|
256
|
-
|
|
273
|
+
clientId: `api-key:${result.key.id}`,
|
|
274
|
+
limit: { roleKey }
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
async ownKeys(orgId, userExternalId) {
|
|
278
|
+
return (await (await this.auth.$context).adapter.findMany({
|
|
279
|
+
model: "apikey",
|
|
280
|
+
where: [{
|
|
281
|
+
field: "referenceId",
|
|
282
|
+
value: userExternalId
|
|
283
|
+
}]
|
|
284
|
+
})).filter((key) => orgIdFromMetadata(key.metadata) === orgId);
|
|
285
|
+
}
|
|
286
|
+
async list(orgId, userExternalId) {
|
|
287
|
+
return (await this.ownKeys(orgId, userExternalId)).map(toStored);
|
|
288
|
+
}
|
|
289
|
+
async create(orgId, userExternalId, input) {
|
|
290
|
+
const created = await this.pluginApi.createApiKey({ body: {
|
|
291
|
+
name: input.name,
|
|
292
|
+
userId: userExternalId,
|
|
293
|
+
metadata: {
|
|
294
|
+
organizationId: orgId,
|
|
295
|
+
role: input.roleKey
|
|
296
|
+
},
|
|
297
|
+
...input.expiresInDays !== void 0 && { expiresIn: input.expiresInDays * DAY_SECONDS }
|
|
298
|
+
} });
|
|
299
|
+
return {
|
|
300
|
+
...toStored(created),
|
|
301
|
+
key: created.key
|
|
257
302
|
};
|
|
258
303
|
}
|
|
259
|
-
async
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
return
|
|
304
|
+
async countWithRole(orgId, roleKey) {
|
|
305
|
+
const metadata = sql`${authSchema.apikey.metadata}::jsonb`;
|
|
306
|
+
const [row] = await this.db.select({ value: count() }).from(authSchema.apikey).where(and(sql`${metadata} ->> 'organizationId' = ${orgId}`, sql`${metadata} ->> 'role' = ${roleKey}`));
|
|
307
|
+
return row?.value ?? 0;
|
|
308
|
+
}
|
|
309
|
+
async createAccount(input) {
|
|
310
|
+
return { externalId: (await (await this.auth.$context).internalAdapter.createUser({
|
|
311
|
+
email: input.email,
|
|
312
|
+
name: input.name,
|
|
313
|
+
emailVerified: true
|
|
314
|
+
}, { method: "admin" })).id };
|
|
315
|
+
}
|
|
316
|
+
async revoke(orgId, userExternalId, keyId) {
|
|
317
|
+
if (!(await this.ownKeys(orgId, userExternalId)).some((key) => key.id === keyId)) throw new NotFoundError("API key", keyId);
|
|
318
|
+
await (await this.auth.$context).adapter.delete({
|
|
319
|
+
model: "apikey",
|
|
320
|
+
where: [{
|
|
321
|
+
field: "id",
|
|
322
|
+
value: keyId
|
|
323
|
+
}]
|
|
324
|
+
});
|
|
263
325
|
}
|
|
264
|
-
async
|
|
265
|
-
const userId = await this.sessionUserId(headers);
|
|
326
|
+
async listApps(orgId, userExternalId) {
|
|
266
327
|
if (!this.mcp.oauthEnabled) return [];
|
|
267
328
|
const ctx = await this.auth.$context;
|
|
268
329
|
const consents = await ctx.adapter.findMany({
|
|
269
330
|
model: "oauthConsent",
|
|
270
331
|
where: [{
|
|
271
332
|
field: "userId",
|
|
272
|
-
value:
|
|
333
|
+
value: userExternalId
|
|
273
334
|
}, {
|
|
274
335
|
field: "referenceId",
|
|
275
336
|
value: orgId
|
|
@@ -292,8 +353,7 @@ var BetterAuth = class {
|
|
|
292
353
|
};
|
|
293
354
|
}));
|
|
294
355
|
}
|
|
295
|
-
async
|
|
296
|
-
const userId = await this.sessionUserId(headers);
|
|
356
|
+
async revokeApp(orgId, userExternalId, clientId) {
|
|
297
357
|
if (!this.mcp.oauthEnabled) return;
|
|
298
358
|
const ctx = await this.auth.$context;
|
|
299
359
|
const where = [
|
|
@@ -303,7 +363,7 @@ var BetterAuth = class {
|
|
|
303
363
|
},
|
|
304
364
|
{
|
|
305
365
|
field: "userId",
|
|
306
|
-
value:
|
|
366
|
+
value: userExternalId
|
|
307
367
|
},
|
|
308
368
|
{
|
|
309
369
|
field: "referenceId",
|
|
@@ -323,38 +383,41 @@ var BetterAuth = class {
|
|
|
323
383
|
where
|
|
324
384
|
});
|
|
325
385
|
}
|
|
326
|
-
async
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
386
|
+
async revokeAllFor(orgId, userExternalId) {
|
|
387
|
+
const ctx = await this.auth.$context;
|
|
388
|
+
const keys = await this.ownKeys(orgId, userExternalId);
|
|
389
|
+
for (const key of keys) await ctx.adapter.delete({
|
|
390
|
+
model: "apikey",
|
|
391
|
+
where: [{
|
|
392
|
+
field: "id",
|
|
393
|
+
value: key.id
|
|
394
|
+
}]
|
|
395
|
+
});
|
|
396
|
+
if (this.mcp.oauthEnabled) {
|
|
397
|
+
const where = [{
|
|
398
|
+
field: "userId",
|
|
399
|
+
value: userExternalId
|
|
400
|
+
}, {
|
|
401
|
+
field: "referenceId",
|
|
402
|
+
value: orgId
|
|
403
|
+
}];
|
|
404
|
+
await ctx.adapter.deleteMany({
|
|
405
|
+
model: "oauthAccessToken",
|
|
406
|
+
where
|
|
407
|
+
});
|
|
408
|
+
await ctx.adapter.deleteMany({
|
|
409
|
+
model: "oauthRefreshToken",
|
|
410
|
+
where
|
|
411
|
+
});
|
|
412
|
+
await ctx.adapter.deleteMany({
|
|
413
|
+
model: "oauthConsent",
|
|
414
|
+
where
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
this.logger.info("access revoked for person", {
|
|
418
|
+
orgId,
|
|
419
|
+
userExternalId,
|
|
420
|
+
apiKeys: keys.length
|
|
358
421
|
});
|
|
359
422
|
}
|
|
360
423
|
async verify(headers) {
|
|
@@ -398,35 +461,42 @@ var BetterAuth = class {
|
|
|
398
461
|
createdAt: org.createdAt
|
|
399
462
|
};
|
|
400
463
|
}
|
|
464
|
+
async createOrganizationFor(ownerExternalId, input) {
|
|
465
|
+
let org;
|
|
466
|
+
try {
|
|
467
|
+
org = await this.auth.api.createOrganization({ body: {
|
|
468
|
+
name: input.name,
|
|
469
|
+
slug: input.slug,
|
|
470
|
+
userId: ownerExternalId
|
|
471
|
+
} });
|
|
472
|
+
} catch (err) {
|
|
473
|
+
if (err instanceof APIError && err.body?.code === "ORGANIZATION_ALREADY_EXISTS") throw new ConflictError("That slug is already in use");
|
|
474
|
+
throw err;
|
|
475
|
+
}
|
|
476
|
+
if (!org) throw new Error(`Couldn't create organization`);
|
|
477
|
+
return {
|
|
478
|
+
id: org.id,
|
|
479
|
+
name: org.name,
|
|
480
|
+
slug: org.slug,
|
|
481
|
+
createdAt: org.createdAt
|
|
482
|
+
};
|
|
483
|
+
}
|
|
401
484
|
async setActiveOrganization(headers, externalId) {
|
|
402
485
|
await this.auth.api.setActiveOrganization({
|
|
403
486
|
body: { organizationId: externalId },
|
|
404
487
|
headers: fromNodeHeaders(headers)
|
|
405
488
|
});
|
|
406
489
|
}
|
|
407
|
-
async
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
headers: fromNodeHeaders(headers)
|
|
418
|
-
});
|
|
419
|
-
if (!org) throw new Error(`Couldn't update organization`);
|
|
420
|
-
return {
|
|
421
|
-
id: org.id,
|
|
422
|
-
name: org.name,
|
|
423
|
-
slug: org.slug,
|
|
424
|
-
createdAt: org.createdAt
|
|
425
|
-
};
|
|
426
|
-
} catch (err) {
|
|
427
|
-
if (err instanceof APIError) throw new OrganizationRuleError(err.message || "Could not update organization");
|
|
428
|
-
throw err;
|
|
429
|
-
}
|
|
490
|
+
async orgAdapter() {
|
|
491
|
+
return getOrgAdapter(await this.auth.$context, {});
|
|
492
|
+
}
|
|
493
|
+
async updateOrganization(externalId, input) {
|
|
494
|
+
const data = {
|
|
495
|
+
...input.name !== void 0 && { name: input.name },
|
|
496
|
+
...input.slug !== void 0 && { slug: input.slug }
|
|
497
|
+
};
|
|
498
|
+
if (Object.keys(data).length === 0) return;
|
|
499
|
+
if (!await (await this.orgAdapter()).updateOrganization(externalId, data)) throw new NotFoundError("Organization", externalId);
|
|
430
500
|
}
|
|
431
501
|
async deleteOrganization(headers, externalId) {
|
|
432
502
|
try {
|
|
@@ -439,55 +509,50 @@ var BetterAuth = class {
|
|
|
439
509
|
throw err;
|
|
440
510
|
}
|
|
441
511
|
}
|
|
442
|
-
async grantMembership(orgExternalId, userExternalId
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
512
|
+
async grantMembership(orgExternalId, userExternalId) {
|
|
513
|
+
const adapter = await this.orgAdapter();
|
|
514
|
+
if (await adapter.findMemberByOrgId({
|
|
515
|
+
userId: userExternalId,
|
|
516
|
+
organizationId: orgExternalId
|
|
517
|
+
})) return;
|
|
518
|
+
await adapter.createMember({
|
|
519
|
+
organizationId: orgExternalId,
|
|
520
|
+
userId: userExternalId,
|
|
521
|
+
role: PARTICIPANT_MEMBERSHIP
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async setMembershipOwner(orgExternalId, userExternalId, owner) {
|
|
525
|
+
const adapter = await this.orgAdapter();
|
|
526
|
+
const role = owner ? OWNER_MEMBERSHIP : PARTICIPANT_MEMBERSHIP;
|
|
527
|
+
const existing = await adapter.findMemberByOrgId({
|
|
528
|
+
userId: userExternalId,
|
|
529
|
+
organizationId: orgExternalId
|
|
530
|
+
});
|
|
531
|
+
if (!existing) {
|
|
532
|
+
await adapter.createMember({
|
|
446
533
|
organizationId: orgExternalId,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
throw err;
|
|
534
|
+
userId: userExternalId,
|
|
535
|
+
role
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
452
538
|
}
|
|
539
|
+
if (existing.role !== role) await adapter.updateMember(existing.id, role);
|
|
453
540
|
}
|
|
454
|
-
async
|
|
455
|
-
await this.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
organizationId: ctx.authOrgId
|
|
460
|
-
},
|
|
461
|
-
headers: this.headersOf(ctx)
|
|
541
|
+
async removeMembership(orgExternalId, userExternalId) {
|
|
542
|
+
const adapter = await this.orgAdapter();
|
|
543
|
+
const existing = await adapter.findMemberByOrgId({
|
|
544
|
+
userId: userExternalId,
|
|
545
|
+
organizationId: orgExternalId
|
|
462
546
|
});
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
organizationId: ctx.authOrgId
|
|
469
|
-
},
|
|
470
|
-
headers: this.headersOf(ctx)
|
|
547
|
+
if (!existing) return;
|
|
548
|
+
await adapter.deleteMember({
|
|
549
|
+
memberId: existing.id,
|
|
550
|
+
organizationId: orgExternalId,
|
|
551
|
+
userId: userExternalId
|
|
471
552
|
});
|
|
472
553
|
}
|
|
473
|
-
headersOf(ctx) {
|
|
474
|
-
return fromNodeHeaders(ctx.headers);
|
|
475
|
-
}
|
|
476
|
-
async resolveMemberId(ctx, userExternalId) {
|
|
477
|
-
const memberId = (await this.auth.api.listMembers({
|
|
478
|
-
query: {
|
|
479
|
-
organizationId: ctx.authOrgId,
|
|
480
|
-
filterField: "userId",
|
|
481
|
-
filterValue: userExternalId,
|
|
482
|
-
limit: 1
|
|
483
|
-
},
|
|
484
|
-
headers: this.headersOf(ctx)
|
|
485
|
-
}))?.members?.[0]?.id;
|
|
486
|
-
if (!memberId) throw new OrganizationRuleError("That person is no longer a member of this organization");
|
|
487
|
-
return memberId;
|
|
488
|
-
}
|
|
489
554
|
};
|
|
490
555
|
//#endregion
|
|
491
|
-
export { BetterAuth, buildAuthOptions, createAuth };
|
|
556
|
+
export { API_KEY_PREFIX, BetterAuth, buildAuthOptions, createAuth };
|
|
492
557
|
|
|
493
558
|
//# sourceMappingURL=better-auth.js.map
|