@protobase/server 0.28.3 → 0.28.4
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/app-sign-in-policy.js +29 -0
- package/dist/better-auth/create-auth.d.ts +12 -4
- package/dist/better-auth/create-auth.js +13 -3
- package/dist/better-auth/invitations/account-from-invitation.js +1 -1
- package/dist/better-auth/invitations/invitation-endpoints.js +3 -2
- package/dist/better-auth/policy-plugin.js +6 -6
- package/dist/better-auth/policy-store.js +4 -4
- package/dist/better-auth/sign-up/sign-up-plugin.js +2 -2
- package/dist/better-auth/staff-plugin.js +2 -1
- package/dist/better-auth/staff-sign-in-endpoints.js +2 -2
- package/dist/better-auth/status-route.js +1 -1
- package/dist/better-auth/users/users-plugin.js +1 -0
- package/dist/better-auth/users.d.ts +5 -2
- package/dist/better-auth/users.js +1 -1
- package/dist/gotrue/email-code-routes.js +1 -1
- package/dist/gotrue/user-routes.js +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defaultSignInPolicy, parseSignInPolicy, signInPolicyProblem } from "./sign-in-policy.js";
|
|
2
|
+
//#region src/better-auth/app-sign-in-policy.ts
|
|
3
|
+
const firstAdmin = {
|
|
4
|
+
password: true,
|
|
5
|
+
passkeys: 0,
|
|
6
|
+
twoFactor: false,
|
|
7
|
+
authenticatorApp: false,
|
|
8
|
+
socialSignIn: false
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The policy an app applies until an admin saves one: `defaultSignInPolicy` with the methods `given` names changed.
|
|
12
|
+
* It is checked as an admin's save is, with mail on: without mail the policy applies as a saved one does (see
|
|
13
|
+
* `effectiveSignInPolicy`), so a missing mail setting turns passwords back on rather than stopping the app.
|
|
14
|
+
*/
|
|
15
|
+
const appSignInPolicy = (given = {}) => {
|
|
16
|
+
const policy = parseSignInPolicy({
|
|
17
|
+
...defaultSignInPolicy,
|
|
18
|
+
...given
|
|
19
|
+
});
|
|
20
|
+
if (!policy) throw new Error("signInPolicy: choose one of the offered rules for every sign-in method (see signInPolicyMethods)");
|
|
21
|
+
const problem = signInPolicyProblem(policy, {
|
|
22
|
+
mail: true,
|
|
23
|
+
admin: firstAdmin
|
|
24
|
+
});
|
|
25
|
+
if (problem) throw new Error(`signInPolicy: ${problem}`);
|
|
26
|
+
return policy;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { appSignInPolicy };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SignInPolicy } from "./sign-in-policy.js";
|
|
2
1
|
import { StaffSignIn } from "./staff-log.js";
|
|
3
2
|
import { Mailer } from "../mail/smtp-mailer.js";
|
|
4
3
|
import { OperatorProvider } from "./operator-provider.js";
|
|
@@ -6,6 +5,7 @@ import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js
|
|
|
6
5
|
import { OrganizationRole } from "./organizations/store.js";
|
|
7
6
|
import { OrganizationsOptions, ResolvedOrganizations } from "./organizations/options.js";
|
|
8
7
|
import { PlatformSignIn } from "./platform-sign-in.js";
|
|
8
|
+
import { SignInPolicy } from "./sign-in-policy.js";
|
|
9
9
|
import { SsoOptions } from "./saml-sign-in.js";
|
|
10
10
|
import { signUpPaths } from "./sign-up/sign-up-plugin.js";
|
|
11
11
|
import { PasswordHash } from "./password-hashes.js";
|
|
@@ -63,6 +63,12 @@ export type CreateAuthOptions = {
|
|
|
63
63
|
signUp?: {
|
|
64
64
|
role: string;
|
|
65
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* The sign-in policy that applies until an admin saves one on the Sign-in policy page, and when the saved one no
|
|
68
|
+
* longer reads: the methods it names, for example `{ password: 'forbidden' }` for emailed codes and passkeys only,
|
|
69
|
+
* with everything else allowed. Checked at startup as an admin's save is. Default everything allowed.
|
|
70
|
+
*/
|
|
71
|
+
signInPolicy?: Partial<SignInPolicy>;
|
|
66
72
|
/** Sign-in attempts allowed per minute and client, default 5; also the sign-up requests allowed. */
|
|
67
73
|
signInPerMinute?: number;
|
|
68
74
|
/**
|
|
@@ -339,7 +345,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
339
345
|
expiresAt: Date;
|
|
340
346
|
token: string;
|
|
341
347
|
ipAddress?: string | null | undefined;
|
|
342
|
-
userAgent
|
|
348
|
+
userAgent? /** Signs cookies and encrypts the JWT private keys. Generate with `openssl rand -base64 32`. */ : string | null | undefined;
|
|
343
349
|
};
|
|
344
350
|
};
|
|
345
351
|
}>>[];
|
|
@@ -1295,7 +1301,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
1295
1301
|
email: string;
|
|
1296
1302
|
emailVerified: boolean;
|
|
1297
1303
|
name: string;
|
|
1298
|
-
image
|
|
1304
|
+
image?: string | null | undefined;
|
|
1299
1305
|
};
|
|
1300
1306
|
};
|
|
1301
1307
|
}>>[];
|
|
@@ -4415,7 +4421,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
4415
4421
|
twoFactor: boolean;
|
|
4416
4422
|
authenticatorApp: boolean;
|
|
4417
4423
|
};
|
|
4418
|
-
missing: ("
|
|
4424
|
+
missing: ("passkey" | "twoFactor")[];
|
|
4419
4425
|
}>;
|
|
4420
4426
|
};
|
|
4421
4427
|
hooks: {
|
|
@@ -5964,6 +5970,8 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
5964
5970
|
/** New users get their ids from `generateUserId` instead of Better Auth's random text. */
|
|
5965
5971
|
generatesUserIds: boolean;
|
|
5966
5972
|
mail: boolean;
|
|
5973
|
+
/** The policy until an admin saves one. */
|
|
5974
|
+
signInPolicy: SignInPolicy;
|
|
5967
5975
|
passwordReset: boolean;
|
|
5968
5976
|
/** Open sign-up is on and offered: the app asks for it, and can mail the codes. */
|
|
5969
5977
|
signUp: boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appOrigins, multiHostBaseURL, readOrigins, resolveOrigins } from "./origins.js";
|
|
2
2
|
import { appNameOver, defaultAppName } from "../app-name.js";
|
|
3
3
|
import { goTrueIssuer, goTrueKeyPair } from "../gotrue/keys.js";
|
|
4
|
+
import { defaultSignInPolicy } from "./sign-in-policy.js";
|
|
4
5
|
import { roleDefinitions } from "./organizations/role-definitions.js";
|
|
5
6
|
import { narrowClaims } from "./roles/active-role.js";
|
|
6
7
|
import { signInPolicyPlugin } from "./policy-plugin.js";
|
|
@@ -20,6 +21,7 @@ import { passkeyPlugin } from "./passkey-plugin.js";
|
|
|
20
21
|
import { passwordResetOptions } from "./password-reset.js";
|
|
21
22
|
import { rolesPlugin } from "./roles/roles-plugin.js";
|
|
22
23
|
import { platformSignInFor, platformSignInProvider, readPlatformSignIn, resolvePlatformSignIn } from "./platform-sign-in.js";
|
|
24
|
+
import { appSignInPolicy } from "./app-sign-in-policy.js";
|
|
23
25
|
import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin, samlPlugin } from "./saml-sign-in.js";
|
|
24
26
|
import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
|
|
25
27
|
import { signUpPaths, signUpPlugin } from "./sign-up/sign-up-plugin.js";
|
|
@@ -55,7 +57,7 @@ const organizationPaths = ["/organization/set-active", "/organization/accept-inv
|
|
|
55
57
|
* The options `createAuth` gives Better Auth, with the roles, default role, mailer, operator provider and platform
|
|
56
58
|
* sign-in provider already resolved.
|
|
57
59
|
*/
|
|
58
|
-
const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, context = noContext }) => {
|
|
60
|
+
const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, context = noContext, signInPolicy = defaultSignInPolicy }) => {
|
|
59
61
|
const secure = options.baseURL.startsWith("https://");
|
|
60
62
|
const signIn = platformSignInFor(platformSignIn, Object.keys(options.socialProviders ?? {}));
|
|
61
63
|
const hosts = passkeyHosts([options.baseURL, ...origins]);
|
|
@@ -69,6 +71,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
69
71
|
const passwordHashes = options.passwordHashes ?? [];
|
|
70
72
|
const policy = {
|
|
71
73
|
mail: Boolean(mailer),
|
|
74
|
+
defaultPolicy: signInPolicy,
|
|
72
75
|
socialProviders: [...Object.keys(options.socialProviders ?? {}), ...samlIds],
|
|
73
76
|
...operator && { operator: operator.name },
|
|
74
77
|
...signIn && { platformSignIn: {
|
|
@@ -153,7 +156,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
153
156
|
...mail ? [emailOTP(emailCodeOptions(mail))] : [],
|
|
154
157
|
...mail && signUpRole ? [signUpPlugin({
|
|
155
158
|
role: signUpRole,
|
|
156
|
-
mail
|
|
159
|
+
mail,
|
|
160
|
+
defaultPolicy: signInPolicy
|
|
157
161
|
})] : [],
|
|
158
162
|
...signIn ? [genericOAuth({ config: [platformSignInProvider(signIn)] })] : [],
|
|
159
163
|
...saml.length > 0 ? [samlPlugin(saml, options.baseURL), samlGuardPlugin(samlIds)] : [],
|
|
@@ -165,7 +169,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
165
169
|
}),
|
|
166
170
|
staffSignInPlugin({
|
|
167
171
|
...operator && { operator },
|
|
168
|
-
...mail && { mail }
|
|
172
|
+
...mail && { mail },
|
|
173
|
+
defaultPolicy: signInPolicy
|
|
169
174
|
}),
|
|
170
175
|
rolesPlugin({ definitions }),
|
|
171
176
|
lastAdminGuardPlugin(),
|
|
@@ -180,6 +185,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
180
185
|
})] : [],
|
|
181
186
|
usersPlugin({
|
|
182
187
|
definitions,
|
|
188
|
+
defaultPolicy: signInPolicy,
|
|
183
189
|
...mail && { mail },
|
|
184
190
|
invitationUrl: organizations?.invitationUrl ?? defaultInvitationUrl(options.baseURL),
|
|
185
191
|
invitationSeconds: organizations?.invitationExpiresIn ?? 604800,
|
|
@@ -273,6 +279,7 @@ const createAuth = (options) => {
|
|
|
273
279
|
const roles = definitions.names;
|
|
274
280
|
if (options.defaultRole !== void 0 && !roles.includes(options.defaultRole)) throw new Error(`defaultRole "${options.defaultRole}" is not one of the roles: ${roles.join(", ")}`);
|
|
275
281
|
const signUpRole = options.signUp && checkSignUpRole(options.signUp.role, roles);
|
|
282
|
+
const signInPolicy = appSignInPolicy(options.signInPolicy);
|
|
276
283
|
const others = roles.filter((role) => role !== "admin");
|
|
277
284
|
const defaultRole = options.defaultRole ?? (options.organizations ? "user" : others.length === 1 ? others[0] : void 0);
|
|
278
285
|
const appName = appNameOver(globalThis.process?.env ?? {}, options.appName);
|
|
@@ -300,6 +307,7 @@ const createAuth = (options) => {
|
|
|
300
307
|
operator,
|
|
301
308
|
platformSignIn,
|
|
302
309
|
saml,
|
|
310
|
+
signInPolicy,
|
|
303
311
|
...organizations && {
|
|
304
312
|
organizations,
|
|
305
313
|
context
|
|
@@ -320,6 +328,8 @@ const createAuth = (options) => {
|
|
|
320
328
|
/** New users get their ids from `generateUserId` instead of Better Auth's random text. */
|
|
321
329
|
generatesUserIds: Boolean(options.generateUserId),
|
|
322
330
|
mail: Boolean(mailer),
|
|
331
|
+
/** The policy until an admin saves one. */
|
|
332
|
+
signInPolicy,
|
|
323
333
|
passwordReset: Boolean(mailer),
|
|
324
334
|
/** Open sign-up is on and offered: the app asks for it, and can mail the codes. */
|
|
325
335
|
signUp: Boolean(signUpRole && mailer),
|
|
@@ -10,7 +10,7 @@ import { setSessionCookie } from "better-auth/cookies";
|
|
|
10
10
|
*/
|
|
11
11
|
const createInvitedAccount = async (ctx, input) => {
|
|
12
12
|
const { internalAdapter } = ctx.context;
|
|
13
|
-
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(ctx.context.adapter)).policy, { mail: input.mail }), input.password);
|
|
13
|
+
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(ctx.context.adapter, input.defaultPolicy)).policy, { mail: input.mail }), input.password);
|
|
14
14
|
const user = await internalAdapter.createUser({
|
|
15
15
|
email: input.email.toLowerCase(),
|
|
16
16
|
name: input.name,
|
|
@@ -14,7 +14,7 @@ const tokenQuery = z$1.object({ token: z$1.string().min(1) });
|
|
|
14
14
|
* vouched for, and the link in the mail proves the person reads it. Accepting an organization's invitation signed in is
|
|
15
15
|
* the organization plugin's.
|
|
16
16
|
*/
|
|
17
|
-
const invitationEndpoints = ({ mail, organizations }) => ({
|
|
17
|
+
const invitationEndpoints = ({ mail, defaultPolicy, organizations }) => ({
|
|
18
18
|
/**
|
|
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.
|
|
@@ -75,7 +75,8 @@ const invitationEndpoints = ({ mail, organizations }) => ({
|
|
|
75
75
|
email,
|
|
76
76
|
name: ctx.body.name,
|
|
77
77
|
...ctx.body.password !== void 0 && { password: ctx.body.password },
|
|
78
|
-
mail
|
|
78
|
+
mail,
|
|
79
|
+
defaultPolicy
|
|
79
80
|
};
|
|
80
81
|
if (found.kind === "app") {
|
|
81
82
|
const user = await createInvitedAccount(ctx, {
|
|
@@ -31,14 +31,14 @@ const accountMethods = async (context, user, socialProviders) => {
|
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
const refuse = (status, refusal) => new APIError(status, refusal);
|
|
34
|
-
const currentPolicy = async (context, mail) => effectiveSignInPolicy((await readSignInPolicy(context.adapter)).policy, { mail });
|
|
34
|
+
const currentPolicy = async (context, { mail, defaultPolicy }) => effectiveSignInPolicy((await readSignInPolicy(context.adapter, defaultPolicy)).policy, { mail });
|
|
35
35
|
const providersUnder = (policy, { socialProviders, platformSignIn }) => [...socialProviders, ...platformSignIn && policy.platformSignIn === "allowed" ? [platformSignIn.provider] : []];
|
|
36
36
|
/**
|
|
37
37
|
* Why `user` gets no API token yet, or `undefined` when they may have one: the policy requires a method, such as
|
|
38
38
|
* two-factor authentication, that their account has not set up. `read` is the effective policy, when read already.
|
|
39
39
|
*/
|
|
40
40
|
const tokenRefusal = async (context, user, options, read) => {
|
|
41
|
-
const policy = read ?? await currentPolicy(context, options
|
|
41
|
+
const policy = read ?? await currentPolicy(context, options);
|
|
42
42
|
if (policy.twoFactor !== "required" && policy.passkey !== "required") return void 0;
|
|
43
43
|
const account = await accountMethods(context, user, providersUnder(policy, options));
|
|
44
44
|
return missingRequiredMethods(policy, account).length > 0 ? setupRequired : void 0;
|
|
@@ -49,10 +49,10 @@ const tokenRefusal = async (context, user, options, read) => {
|
|
|
49
49
|
* someone without what it requires gets a session, so they can set it up, but no API token (`403 SIGN_IN_SETUP_REQUIRED`).
|
|
50
50
|
*/
|
|
51
51
|
const signInPolicyPlugin = (options) => {
|
|
52
|
-
const { mail, operator, platformSignIn } = options;
|
|
52
|
+
const { mail, defaultPolicy, operator, platformSignIn } = options;
|
|
53
53
|
const policyAdmin = adminOnly("Only an admin can change how people sign in.");
|
|
54
54
|
const policyAnswer = async (context) => {
|
|
55
|
-
const stored = await readSignInPolicy(context.adapter);
|
|
55
|
+
const stored = await readSignInPolicy(context.adapter, defaultPolicy);
|
|
56
56
|
const savedBy = stored.savedBy && (await context.internalAdapter.findUserById(stored.savedBy))?.email;
|
|
57
57
|
return {
|
|
58
58
|
policy: stored.policy,
|
|
@@ -105,7 +105,7 @@ const signInPolicyPlugin = (options) => {
|
|
|
105
105
|
method: "GET",
|
|
106
106
|
use: [sessionMiddleware]
|
|
107
107
|
}, async (ctx) => {
|
|
108
|
-
const policy = await currentPolicy(ctx.context,
|
|
108
|
+
const policy = await currentPolicy(ctx.context, options);
|
|
109
109
|
const { socialSignIn: _, ...account } = await accountMethods(ctx.context, ctx.context.session.user, providersUnder(policy, options));
|
|
110
110
|
return ctx.json({
|
|
111
111
|
policy,
|
|
@@ -123,7 +123,7 @@ const signInPolicyPlugin = (options) => {
|
|
|
123
123
|
code: "SIGN_IN_CODES_ONLY",
|
|
124
124
|
message: "Only sign-in codes can be emailed."
|
|
125
125
|
});
|
|
126
|
-
const policy = await currentPolicy(ctx.context,
|
|
126
|
+
const policy = await currentPolicy(ctx.context, options);
|
|
127
127
|
const refusal = policyRefusal(policy, {
|
|
128
128
|
path,
|
|
129
129
|
body: ctx.body,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseSignInPolicy } from "./sign-in-policy.js";
|
|
2
2
|
//#region src/better-auth/policy-store.ts
|
|
3
3
|
/** Better Auth's model for the sign-in policy: one row per save, the newest applies, the older ones are its history. */
|
|
4
4
|
const signInPolicyModel = "signInPolicy";
|
|
@@ -47,8 +47,8 @@ const signInPolicySchema = { [signInPolicyModel]: {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
} };
|
|
50
|
-
/** The newest saved policy, or the default when none was saved. A row that no longer parses counts as none. */
|
|
51
|
-
const readSignInPolicy = async (adapter) => {
|
|
50
|
+
/** The newest saved policy, or the app's default (see `appSignInPolicy`) when none was saved. A row that no longer parses counts as none. */
|
|
51
|
+
const readSignInPolicy = async (adapter, appDefault) => {
|
|
52
52
|
const [row] = await adapter.findMany({
|
|
53
53
|
model: signInPolicyModel,
|
|
54
54
|
sortBy: {
|
|
@@ -58,7 +58,7 @@ const readSignInPolicy = async (adapter) => {
|
|
|
58
58
|
limit: 1
|
|
59
59
|
});
|
|
60
60
|
const policy = row && parseSignInPolicy(row);
|
|
61
|
-
if (!policy) return { policy:
|
|
61
|
+
if (!policy) return { policy: appDefault };
|
|
62
62
|
return {
|
|
63
63
|
policy,
|
|
64
64
|
savedAt: new Date(row.createdAt).toISOString(),
|
|
@@ -27,7 +27,7 @@ const noAdminYet = () => new APIError("FORBIDDEN", {
|
|
|
27
27
|
* has an account or not; one that has gets a mail saying so, with a link to sign in, instead of a code. While the store
|
|
28
28
|
* has no user, nobody signs up: the first admin is made on the host.
|
|
29
29
|
*/
|
|
30
|
-
const signUpPlugin = ({ role, mail }) => ({
|
|
30
|
+
const signUpPlugin = ({ role, mail, defaultPolicy }) => ({
|
|
31
31
|
id: "protobase-sign-up",
|
|
32
32
|
endpoints: {
|
|
33
33
|
/** `POST /sign-up/send-code` with `{ email }`: mails a code for making an account with the address. */
|
|
@@ -78,7 +78,7 @@ const signUpPlugin = ({ role, mail }) => ({
|
|
|
78
78
|
const { ticket, name, password } = ctx.body;
|
|
79
79
|
const { internalAdapter, adapter } = ctx.context;
|
|
80
80
|
if (await adapter.count({ model: "user" }) === 0) throw noAdminYet();
|
|
81
|
-
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(adapter)).policy, { mail: true }), password);
|
|
81
|
+
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(adapter, defaultPolicy)).policy, { mail: true }), password);
|
|
82
82
|
const email = await consumeSignUpTicket(ctx.context, ticket);
|
|
83
83
|
const passwordHash = password === void 0 ? void 0 : await ctx.context.password.hash(password);
|
|
84
84
|
const user = await createSignedUpAccount(ctx.context, {
|
|
@@ -11,12 +11,13 @@ import { deleteSessionCookie } from "better-auth/cookies";
|
|
|
11
11
|
* staff sign-in is logged for the app's admins. The plugin is there without an operator provider too, so its table
|
|
12
12
|
* exists from the first auth migration.
|
|
13
13
|
*/
|
|
14
|
-
const staffSignInPlugin = ({ operator, mail }) => ({
|
|
14
|
+
const staffSignInPlugin = ({ operator, mail, defaultPolicy }) => ({
|
|
15
15
|
id: "protobase-staff-sign-in",
|
|
16
16
|
schema: staffSignInSchema,
|
|
17
17
|
endpoints: {
|
|
18
18
|
...operator && staffSignInEndpoints({
|
|
19
19
|
operator,
|
|
20
|
+
defaultPolicy,
|
|
20
21
|
...mail && { mail }
|
|
21
22
|
}),
|
|
22
23
|
/** `GET /staff/session`: for a staff session, who started it, as whom, why, and until when; otherwise `{ staff: null }`. */
|
|
@@ -35,7 +35,7 @@ const withError = (url, code) => {
|
|
|
35
35
|
* permission and sign-in and starts a session as the person, of Better Auth's admin plugin (`impersonatedBy` names
|
|
36
36
|
* them). The session is short and never extended, and the person's two-factor and passkey requirements do not stop it.
|
|
37
37
|
*/
|
|
38
|
-
const staffSignInEndpoints = ({ operator, mail }) => {
|
|
38
|
+
const staffSignInEndpoints = ({ operator, mail, defaultPolicy }) => {
|
|
39
39
|
const clients = /* @__PURE__ */ new Map();
|
|
40
40
|
const clientOf = (context) => {
|
|
41
41
|
const callback = `${context.baseURL}/staff/callback`;
|
|
@@ -52,7 +52,7 @@ const staffSignInEndpoints = ({ operator, mail }) => {
|
|
|
52
52
|
now: Math.floor(Date.now() / 1e3)
|
|
53
53
|
});
|
|
54
54
|
if (refusal) return refusal;
|
|
55
|
-
const policy = effectiveSignInPolicy((await readSignInPolicy(ctx.context.adapter)).policy, { mail: Boolean(mail) });
|
|
55
|
+
const policy = effectiveSignInPolicy((await readSignInPolicy(ctx.context.adapter, defaultPolicy)).policy, { mail: Boolean(mail) });
|
|
56
56
|
const off = policyRefusal(policy, { path: staffSignInPath });
|
|
57
57
|
if (off) return off;
|
|
58
58
|
const found = await ctx.context.internalAdapter.findUserByEmail(started.email.toLowerCase());
|
|
@@ -22,7 +22,7 @@ const statusRoute = (auth, { anonymous = false, plugins = [], locale } = {}) =>
|
|
|
22
22
|
const app = new Hono();
|
|
23
23
|
app.get("/status", async (c) => {
|
|
24
24
|
const context = await auth.$context;
|
|
25
|
-
const policy = effectiveSignInPolicy((await readSignInPolicy(context.adapter)).policy, { mail: auth.mail });
|
|
25
|
+
const policy = effectiveSignInPolicy((await readSignInPolicy(context.adapter, auth.signInPolicy)).policy, { mail: auth.mail });
|
|
26
26
|
const signInMethods = [
|
|
27
27
|
...policy.password === "allowed" ? ["password"] : [],
|
|
28
28
|
...policy.emailCode === "allowed" ? ["emailCode"] : [],
|
|
@@ -8,10 +8,13 @@ export type LinkedAccount = {
|
|
|
8
8
|
providerId: string;
|
|
9
9
|
accountId: string;
|
|
10
10
|
};
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Without `password` the user signs in by emailed code, passkey or a sign-in provider only. `metadata` lands in the
|
|
13
|
+
* user metadata column, which `createAuth`'s `users.metadata` names; it needs one.
|
|
14
|
+
*/
|
|
12
15
|
export type NewUser = {
|
|
13
16
|
email: string;
|
|
14
|
-
password
|
|
17
|
+
password?: string;
|
|
15
18
|
name?: string;
|
|
16
19
|
role?: string;
|
|
17
20
|
accounts?: LinkedAccount[];
|
|
@@ -35,7 +35,7 @@ const createUser = async (auth, input) => {
|
|
|
35
35
|
await checkAccounts(auth, accounts);
|
|
36
36
|
const { user } = await auth.api.createUser({ body: {
|
|
37
37
|
email: input.email,
|
|
38
|
-
password: input.password,
|
|
38
|
+
...input.password !== void 0 && { password: input.password },
|
|
39
39
|
name: input.name ?? input.email.split("@")[0],
|
|
40
40
|
role,
|
|
41
41
|
...input.metadata && { data: { metadata: input.metadata } }
|
|
@@ -41,7 +41,7 @@ const emailCodeRoutes = ({ auth, context, sessionOf }) => {
|
|
|
41
41
|
const checkMetadata = (data) => {
|
|
42
42
|
if (Object.keys(data).length > 0 && !auth.userMetadata) throw new GoTrueError(400, "validation_failed", "This app keeps no user metadata: leave data out");
|
|
43
43
|
};
|
|
44
|
-
const signUpPolicy = async () => effectiveSignInPolicy((await readSignInPolicy((await context()).adapter)).policy, { mail: true });
|
|
44
|
+
const signUpPolicy = async () => effectiveSignInPolicy((await readSignInPolicy((await context()).adapter, auth.signInPolicy)).policy, { mail: true });
|
|
45
45
|
const sendSignUpCode = (request, email) => callBetterAuth(auth, request, "/sign-up/send-code", { email });
|
|
46
46
|
const sendSignInCode = (request, email) => callBetterAuth(auth, request, "/email-otp/send-verification-otp", {
|
|
47
47
|
email,
|
|
@@ -69,7 +69,7 @@ const userRoutes = ({ auth, context }) => {
|
|
|
69
69
|
if (email !== void 0 && email.trim().toLowerCase() !== user.email) throw new GoTrueError(422, "email_change_not_allowed", "Changing the email address is not offered");
|
|
70
70
|
if (data !== void 0 && !auth.userMetadata) throw new GoTrueError(400, "validation_failed", "This app keeps no user metadata: leave data out");
|
|
71
71
|
if (password !== void 0) {
|
|
72
|
-
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(ctx.adapter)).policy, { mail: auth.mail }), password);
|
|
72
|
+
checkNewAccountPassword(effectiveSignInPolicy((await readSignInPolicy(ctx.adapter, auth.signInPolicy)).policy, { mail: auth.mail }), password);
|
|
73
73
|
const hash = await ctx.password.hash(password);
|
|
74
74
|
if (await ctx.internalAdapter.findCredentialAccount(user.id)) await ctx.internalAdapter.updatePassword(user.id, hash);
|
|
75
75
|
else await ctx.internalAdapter.linkAccount({
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MobileConfig } from "./mobile-config.js";
|
|
2
2
|
import { AppIdentity } from "./app-identity.js";
|
|
3
3
|
import { AppIcons, PngIcon, pngIcons } from "./web-app/app-icons.js";
|
|
4
|
-
import { SignInPolicy, SignInRule, StaffAccessRule, defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
|
|
5
4
|
import { StaffSignIn } from "./better-auth/staff-log.js";
|
|
6
5
|
import { MailMessage, Mailer } from "./mail/smtp-mailer.js";
|
|
7
6
|
import { OperatorProvider, readOperatorSettings } from "./better-auth/operator-provider.js";
|
|
@@ -9,6 +8,7 @@ import { RoleDefinition, RolesInput } from "./better-auth/organizations/role-def
|
|
|
9
8
|
import { OrganizationRole, StoredMember, StoredOrganization } from "./better-auth/organizations/store.js";
|
|
10
9
|
import { OrganizationsOptions } from "./better-auth/organizations/options.js";
|
|
11
10
|
import { PlatformSignIn, readPlatformSignIn } from "./better-auth/platform-sign-in.js";
|
|
11
|
+
import { SignInPolicy, SignInRule, StaffAccessRule, defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
|
|
12
12
|
import { EmailComponent, EmailOptions, EmailTemplate, RenderedEmail, defineEmail } from "./mail/define-email.js";
|
|
13
13
|
import { AdminAuth, CreateAuthOptions, UserCreatedHook, createAuth, normalizeRoles } from "./better-auth/create-auth.js";
|
|
14
14
|
import { EventsOptions } from "./changes/events-options.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.4",
|
|
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": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@better-auth/passkey": "^1.7.7",
|
|
34
34
|
"@better-auth/sso": "^1.7.7",
|
|
35
|
-
"@protobase/layout": "0.28.
|
|
36
|
-
"@protobase/query": "0.28.
|
|
37
|
-
"@protobase/schema": "0.28.
|
|
35
|
+
"@protobase/layout": "0.28.4",
|
|
36
|
+
"@protobase/query": "0.28.4",
|
|
37
|
+
"@protobase/schema": "0.28.4",
|
|
38
38
|
"@react-email/render": "^2.1.0",
|
|
39
39
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
40
40
|
"@sentry/core": "^11.7.0",
|