@protobase/server 0.44.4 → 0.44.5
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/better-auth/create-auth.d.ts +42 -4
- package/dist/better-auth/create-auth.js +5 -2
- package/dist/better-auth/endpoint-key-clashes.js +23 -0
- package/dist/better-auth/invitations/invitation-endpoints.js +1 -1
- package/dist/better-auth/organizations/accept-invitation.js +22 -12
- package/dist/better-auth/organizations/invitation-endpoints.js +24 -16
- package/dist/better-auth/users/user-endpoints.js +1 -1
- package/dist/package.js +1 -1
- package/package.json +4 -4
|
@@ -4445,7 +4445,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
4445
4445
|
twoFactor: boolean;
|
|
4446
4446
|
authenticatorApp: boolean;
|
|
4447
4447
|
};
|
|
4448
|
-
missing: ("
|
|
4448
|
+
missing: ("twoFactor" | "passkey")[];
|
|
4449
4449
|
}>;
|
|
4450
4450
|
};
|
|
4451
4451
|
hooks: {
|
|
@@ -5318,7 +5318,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
5318
5318
|
}[];
|
|
5319
5319
|
total: number;
|
|
5320
5320
|
}>;
|
|
5321
|
-
|
|
5321
|
+
acceptInvitationOfLink: import("better-call").StrictEndpoint<"/invitation/accept", {
|
|
5322
5322
|
method: "POST";
|
|
5323
5323
|
body: import("zod").ZodObject<{
|
|
5324
5324
|
token: import("zod").ZodString;
|
|
@@ -5356,6 +5356,44 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
5356
5356
|
organizationId: string;
|
|
5357
5357
|
};
|
|
5358
5358
|
}>;
|
|
5359
|
+
acceptOwnInvitation: import("better-call").StrictEndpoint<"/invitation/accept-own", {
|
|
5360
|
+
method: "POST";
|
|
5361
|
+
body: import("zod").ZodObject<{
|
|
5362
|
+
invitationId: import("zod").ZodString;
|
|
5363
|
+
}, import("zod/v4/core").$strip>;
|
|
5364
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
5365
|
+
session: {
|
|
5366
|
+
session: Record<string, any> & {
|
|
5367
|
+
id: string;
|
|
5368
|
+
createdAt: Date;
|
|
5369
|
+
updatedAt: Date;
|
|
5370
|
+
userId: string;
|
|
5371
|
+
expiresAt: Date;
|
|
5372
|
+
token: string;
|
|
5373
|
+
ipAddress?: string | null | undefined;
|
|
5374
|
+
userAgent?: string | null | undefined;
|
|
5375
|
+
};
|
|
5376
|
+
user: Record<string, any> & {
|
|
5377
|
+
id: string;
|
|
5378
|
+
createdAt: Date;
|
|
5379
|
+
updatedAt: Date;
|
|
5380
|
+
email: string;
|
|
5381
|
+
emailVerified: boolean;
|
|
5382
|
+
name: string;
|
|
5383
|
+
image?: string | null | undefined;
|
|
5384
|
+
};
|
|
5385
|
+
};
|
|
5386
|
+
}>>[];
|
|
5387
|
+
}, {
|
|
5388
|
+
member: {
|
|
5389
|
+
appRoles: string[];
|
|
5390
|
+
id: string;
|
|
5391
|
+
createdAt: Date;
|
|
5392
|
+
role: string;
|
|
5393
|
+
userId: string;
|
|
5394
|
+
organizationId: string;
|
|
5395
|
+
};
|
|
5396
|
+
}>;
|
|
5359
5397
|
};
|
|
5360
5398
|
hooks: {
|
|
5361
5399
|
after: {
|
|
@@ -5368,7 +5406,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
5368
5406
|
} | {
|
|
5369
5407
|
id: "protobase-users";
|
|
5370
5408
|
endpoints: {
|
|
5371
|
-
|
|
5409
|
+
invitationOfLink: import("better-call").StrictEndpoint<"/invitation", {
|
|
5372
5410
|
method: "GET";
|
|
5373
5411
|
query: import("zod").ZodObject<{
|
|
5374
5412
|
token: import("zod").ZodString;
|
|
@@ -5523,7 +5561,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
5523
5561
|
roles: string[];
|
|
5524
5562
|
expiresAt: string;
|
|
5525
5563
|
}>;
|
|
5526
|
-
|
|
5564
|
+
listAppInvitations: import("better-call").StrictEndpoint<"/users/invitations", {
|
|
5527
5565
|
method: "GET";
|
|
5528
5566
|
use: import("better-call").Middleware<{
|
|
5529
5567
|
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
@@ -26,6 +26,7 @@ import { passwordResetOptions } from "./password-reset.js";
|
|
|
26
26
|
import { rolesPlugin } from "./roles/roles-plugin.js";
|
|
27
27
|
import { platformSignInFor, platformSignInProvider, readPlatformSignIn, resolvePlatformSignIn } from "./platform-sign-in.js";
|
|
28
28
|
import { appSignInPolicy } from "./app-sign-in-policy.js";
|
|
29
|
+
import { assertNoEndpointKeyClashes } from "./endpoint-key-clashes.js";
|
|
29
30
|
import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin, samlPlugin } from "./saml-sign-in.js";
|
|
30
31
|
import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
|
|
31
32
|
import { signUpPaths, signUpPlugin } from "./sign-up/sign-up-plugin.js";
|
|
@@ -313,7 +314,7 @@ const createAuth = (options) => {
|
|
|
313
314
|
let instance;
|
|
314
315
|
const context = () => instance?.$context ?? Promise.reject(/* @__PURE__ */ new Error("The auth instance is not made yet"));
|
|
315
316
|
const mobile = mobileRequests();
|
|
316
|
-
const
|
|
317
|
+
const authOptions = betterAuthOptions(options, {
|
|
317
318
|
mobile,
|
|
318
319
|
origins,
|
|
319
320
|
passkeyDomain,
|
|
@@ -330,7 +331,9 @@ const createAuth = (options) => {
|
|
|
330
331
|
...organizations && { organizations },
|
|
331
332
|
...mcp && { mcp },
|
|
332
333
|
...(organizations || mcp) && { context }
|
|
333
|
-
})
|
|
334
|
+
});
|
|
335
|
+
assertNoEndpointKeyClashes(authOptions.plugins);
|
|
336
|
+
const auth = betterAuth(authOptions);
|
|
334
337
|
instance = auth;
|
|
335
338
|
return Object.assign(auth, {
|
|
336
339
|
/**
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/better-auth/endpoint-key-clashes.ts
|
|
2
|
+
/**
|
|
3
|
+
* Each endpoint key more than one plugin registers, with those plugins' ids. Better Auth merges the plugins' endpoints
|
|
4
|
+
* by key into one object, so a later plugin's endpoint silently replaces an earlier one's of the same key, and the
|
|
5
|
+
* earlier one's path answers 404.
|
|
6
|
+
*/
|
|
7
|
+
const endpointKeyClashes = (plugins) => {
|
|
8
|
+
const owners = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const plugin of plugins) for (const key of Object.keys(plugin.endpoints ?? {})) owners.set(key, [...owners.get(key) ?? [], plugin.id]);
|
|
10
|
+
return [...owners].filter(([, ids]) => ids.length > 1).map(([key, ids]) => ({
|
|
11
|
+
key,
|
|
12
|
+
plugins: ids
|
|
13
|
+
}));
|
|
14
|
+
};
|
|
15
|
+
/** Fails when two plugins register an endpoint of the same key, as one would hide the other. */
|
|
16
|
+
const assertNoEndpointKeyClashes = (plugins) => {
|
|
17
|
+
const clashes = endpointKeyClashes(plugins);
|
|
18
|
+
if (clashes.length === 0) return;
|
|
19
|
+
const list = clashes.map(({ key, plugins: ids }) => `${key} (${ids.join(", ")})`).join("; ");
|
|
20
|
+
throw new Error(`Auth plugins register the same endpoint keys, so one hides the other: ${list}`);
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { assertNoEndpointKeyClashes, endpointKeyClashes };
|
|
@@ -19,7 +19,7 @@ const invitationEndpoints = ({ mail, defaultPolicy, organizations }) => ({
|
|
|
19
19
|
* `GET /invitation?token=`: who invites, the address and roles, until when, and whether the address has an account;
|
|
20
20
|
* for an invitation to the app also the app's name, and for an organization's the organization and the role there.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
invitationOfLink: createAuthEndpoint("/invitation", {
|
|
23
23
|
method: "GET",
|
|
24
24
|
query: tokenQuery
|
|
25
25
|
}, async (ctx) => {
|
|
@@ -3,23 +3,33 @@ import { findMember, membersOf } from "./store.js";
|
|
|
3
3
|
import { invalidInvitation, invitationIdOf, verifyInvitationToken } from "../invitations/invitation-token.js";
|
|
4
4
|
import { APIError } from "better-auth/api";
|
|
5
5
|
//#region src/better-auth/organizations/accept-invitation.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}]
|
|
15
|
-
});
|
|
16
|
-
if (!row || !await verifyInvitationToken(context.secret, token, row)) throw new APIError("BAD_REQUEST", invalidInvitation);
|
|
6
|
+
const findInvitation = (context, id) => context.adapter.findOne({
|
|
7
|
+
model: "invitation",
|
|
8
|
+
where: [{
|
|
9
|
+
field: "id",
|
|
10
|
+
value: id
|
|
11
|
+
}]
|
|
12
|
+
});
|
|
13
|
+
const pending = (row) => {
|
|
17
14
|
if (row.status !== "pending" || new Date(row.expiresAt).getTime() < Date.now()) throw new APIError("BAD_REQUEST", invalidInvitation);
|
|
18
15
|
return {
|
|
19
16
|
...row,
|
|
20
17
|
appRoles: parseAppRoles(row.appRoles)
|
|
21
18
|
};
|
|
22
19
|
};
|
|
20
|
+
/** The pending invitation an emailed link's token names, or a 400 `INVITATION_INVALID`. */
|
|
21
|
+
const invitationOfToken = async (context, token) => {
|
|
22
|
+
const id = invitationIdOf(token);
|
|
23
|
+
const row = id && await findInvitation(context, id);
|
|
24
|
+
if (!row || !await verifyInvitationToken(context.secret, token, row)) throw new APIError("BAD_REQUEST", invalidInvitation);
|
|
25
|
+
return pending(row);
|
|
26
|
+
};
|
|
27
|
+
/** The pending invitation of `id`, or a 400 `INVITATION_INVALID`; only its own address may accept it (`acceptInvitation`). */
|
|
28
|
+
const invitationOfId = async (context, id) => {
|
|
29
|
+
const row = await findInvitation(context, id);
|
|
30
|
+
if (!row) throw new APIError("BAD_REQUEST", invalidInvitation);
|
|
31
|
+
return pending(row);
|
|
32
|
+
};
|
|
23
33
|
/**
|
|
24
34
|
* Makes `user` a member as the invitation says, with its organization role and app roles, in one transaction with the
|
|
25
35
|
* invitation's acceptance, so a member never exists without the app roles they were invited with. Better Auth's own
|
|
@@ -73,4 +83,4 @@ const acceptInvitation = async (context, invitation, user, membershipLimit) => {
|
|
|
73
83
|
});
|
|
74
84
|
};
|
|
75
85
|
//#endregion
|
|
76
|
-
export { acceptInvitation, invitationOfToken };
|
|
86
|
+
export { acceptInvitation, invitationOfId, invitationOfToken };
|
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
import { acceptInvitation, invitationOfToken } from "./accept-invitation.js";
|
|
1
|
+
import { acceptInvitation, invitationOfId, invitationOfToken } from "./accept-invitation.js";
|
|
2
2
|
import * as z$1 from "zod";
|
|
3
3
|
import { createAuthEndpoint, sessionMiddleware } from "better-auth/api";
|
|
4
4
|
//#region src/better-auth/organizations/invitation-endpoints.ts
|
|
5
|
+
const accept = async (context, invitation, membershipLimit) => {
|
|
6
|
+
const { user, session } = context.session;
|
|
7
|
+
const member = await acceptInvitation(context, invitation, user, membershipLimit);
|
|
8
|
+
await context.internalAdapter.updateSession(session.token, { activeOrganizationId: invitation.organizationId });
|
|
9
|
+
return { member };
|
|
10
|
+
};
|
|
5
11
|
/**
|
|
6
|
-
* Accepting an organization's
|
|
7
|
-
*
|
|
12
|
+
* Accepting an organization's invitation signed in, from its emailed link or from the person's own invitations (Better
|
|
13
|
+
* Auth's `GET /organization/list-user-invitations`). What a link is for, and making an account from it, are the shared
|
|
14
|
+
* invitation endpoints (`invitations/invitation-endpoints.ts`), which serve invitations to the app as well.
|
|
8
15
|
*/
|
|
9
|
-
const invitationEndpoints = ({ resolved }) => ({
|
|
10
|
-
/** `POST /invitation/accept` with `{ token }`, signed in as the invited address: becomes a member, and works there. */
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}, async (ctx) =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
})
|
|
16
|
+
const invitationEndpoints = ({ resolved }) => ({
|
|
17
|
+
/** `POST /invitation/accept` with `{ token }`, signed in as the invited address: becomes a member, and works there. */
|
|
18
|
+
acceptInvitationOfLink: createAuthEndpoint("/invitation/accept", {
|
|
19
|
+
method: "POST",
|
|
20
|
+
body: z$1.object({ token: z$1.string().min(1) }),
|
|
21
|
+
use: [sessionMiddleware]
|
|
22
|
+
}, async (ctx) => ctx.json(await accept(ctx.context, await invitationOfToken(ctx.context, ctx.body.token), resolved.membershipLimit))),
|
|
23
|
+
/** `POST /invitation/accept-own` with `{ invitationId }`, signed in as the invited address: the same, from their list. */
|
|
24
|
+
acceptOwnInvitation: createAuthEndpoint("/invitation/accept-own", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
body: z$1.object({ invitationId: z$1.string().min(1) }),
|
|
27
|
+
use: [sessionMiddleware]
|
|
28
|
+
}, async (ctx) => ctx.json(await accept(ctx.context, await invitationOfId(ctx.context, ctx.body.invitationId), resolved.membershipLimit)))
|
|
29
|
+
});
|
|
22
30
|
//#endregion
|
|
23
31
|
export { invitationEndpoints };
|
|
@@ -56,7 +56,7 @@ const userEndpoints = ({ definitions, mail, invitationUrl, invitationSeconds })
|
|
|
56
56
|
return ctx.json(answerOf(invitation));
|
|
57
57
|
}),
|
|
58
58
|
/** `GET /users/invitations`: the pending invitations to the app, oldest first. */
|
|
59
|
-
|
|
59
|
+
listAppInvitations: createAuthEndpoint("/users/invitations", {
|
|
60
60
|
method: "GET",
|
|
61
61
|
use: [admins]
|
|
62
62
|
}, async (ctx) => ctx.json({ invitations: (await listAppInvitations(ctx.context)).map(answerOf) })),
|
package/dist/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.5",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@better-auth/passkey": "^1.7.12",
|
|
37
37
|
"@better-auth/sso": "^1.7.12",
|
|
38
38
|
"@modelcontextprotocol/server": "^2.3.1",
|
|
39
|
-
"@protobase/layout": "0.44.
|
|
40
|
-
"@protobase/query": "0.44.
|
|
41
|
-
"@protobase/schema": "0.44.
|
|
39
|
+
"@protobase/layout": "0.44.5",
|
|
40
|
+
"@protobase/query": "0.44.5",
|
|
41
|
+
"@protobase/schema": "0.44.5",
|
|
42
42
|
"@react-email/render": "^2.1.0",
|
|
43
43
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
44
44
|
"@sentry/core": "^11.7.0",
|