@protobase/server 0.25.9 → 0.25.11

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.
@@ -0,0 +1,24 @@
1
+ //#region src/api-keys/api-key-settings.d.ts
2
+ /**
3
+ * The app's two keys, for clients that call it the way they call Supabase. `publishable_key` is safe in a browser and
4
+ * makes a request anonymous or its signed-in user's; `secret_key` stays on servers and makes a request the app's own.
5
+ * Each falls back to `PROTOBASE_PUBLISHABLE_KEY` and `PROTOBASE_SECRET_KEY`; without either, nothing is admitted by key.
6
+ */
7
+ export type ApiKeysOptions = {
8
+ publishable_key?: string;
9
+ secret_key?: string;
10
+ };
11
+ export type ApiKeys = {
12
+ publishable?: string;
13
+ secret?: string;
14
+ };
15
+ export declare const apiKeyVariables: {
16
+ readonly publishable_key: "PROTOBASE_PUBLISHABLE_KEY";
17
+ readonly secret_key: "PROTOBASE_SECRET_KEY";
18
+ };
19
+ /**
20
+ * The keys that are set, unless the option is `false`. A publishable key that looks like a secret one, the other way
21
+ * round, or both the same stop the server at startup, without printing the keys.
22
+ */
23
+ export declare const apiKeySettings: (option: ApiKeysOptions | false | undefined, env: Record<string, string | undefined>) => ApiKeys;
24
+ //#endregion
@@ -0,0 +1,32 @@
1
+ //#region src/api-keys/api-key-settings.ts
2
+ const apiKeyVariables = {
3
+ publishable_key: "PROTOBASE_PUBLISHABLE_KEY",
4
+ secret_key: "PROTOBASE_SECRET_KEY"
5
+ };
6
+ const wrongPrefix = {
7
+ publishable_key: "sb_secret_",
8
+ secret_key: "sb_publishable_"
9
+ };
10
+ /**
11
+ * The keys that are set, unless the option is `false`. A publishable key that looks like a secret one, the other way
12
+ * round, or both the same stop the server at startup, without printing the keys.
13
+ */
14
+ const apiKeySettings = (option, env) => {
15
+ if (option === false) return {};
16
+ const read = (name) => {
17
+ const value = (option?.[name] ?? env[apiKeyVariables[name]])?.trim();
18
+ if (!value) return void 0;
19
+ const source = option?.[name] === void 0 ? apiKeyVariables[name] : `The ${name} option`;
20
+ if (value.startsWith(wrongPrefix[name])) throw new Error(`${source} starts with ${wrongPrefix[name]}, the other key's prefix; swap the two keys`);
21
+ return value;
22
+ };
23
+ const publishable = read("publishable_key");
24
+ const secret = read("secret_key");
25
+ if (publishable !== void 0 && publishable === secret) throw new Error("The publishable and the secret key are the same; the secret key must be one of its own");
26
+ return {
27
+ ...publishable && { publishable },
28
+ ...secret && { secret }
29
+ };
30
+ };
31
+ //#endregion
32
+ export { apiKeySettings, apiKeyVariables };
@@ -0,0 +1,35 @@
1
+ import { ApiKeys } from "./api-key-settings.js";
2
+ import { JWTPayload } from "jose";
3
+ //#region src/api-keys/resolve-caller.d.ts
4
+ /**
5
+ * Who a key-admitted request runs as. `anon` and `authenticated` are the database roles of the same names; `app` is
6
+ * the app's own role, the one that owns its tables. `claims` are those of the signed-in user's token, or for the keys
7
+ * alone the `role` claim that Supabase gives them, so `auth.jwt()` reads the same.
8
+ */
9
+ export type Caller = {
10
+ role: 'anon';
11
+ claims: {
12
+ role: 'anon';
13
+ };
14
+ } | {
15
+ role: 'authenticated';
16
+ claims: JWTPayload;
17
+ } | {
18
+ role: 'app';
19
+ claims: {
20
+ role: 'service_role';
21
+ };
22
+ };
23
+ export type CallerOptions = {
24
+ keys: ApiKeys;
25
+ /** Verifies a user's token against the app's key set and returns its claims, such as `jwtVerifier(betterAuthTokens(...))`. */
26
+ verify: (token: string) => Promise<JWTPayload>;
27
+ };
28
+ /**
29
+ * Resolves the `apikey` header and `Authorization: Bearer` of a request to its caller. The key picks the role: the
30
+ * publishable key is `anon`, the secret key `app`. A user's token in the bearer makes either `authenticated` with its
31
+ * claims. A bearer that is no token must be the key itself, as supabase-js sends it when nobody is signed in. A
32
+ * missing or wrong key, a wrong bearer or a token that does not verify or names no user is a 401 problem.
33
+ */
34
+ export declare const apiKeyCaller: (options: CallerOptions) => (request: Request) => Promise<Caller>;
35
+ //#endregion
@@ -0,0 +1,39 @@
1
+ import { unauthorized } from "../problem.js";
2
+ import { createHash, timingSafeEqual } from "node:crypto";
3
+ //#region src/api-keys/resolve-caller.ts
4
+ const digest = (text) => createHash("sha256").update(text).digest();
5
+ const sameKey = (given, key) => key !== void 0 && timingSafeEqual(digest(given), digest(key));
6
+ const isJwt = (token) => token.split(".").length === 3;
7
+ /**
8
+ * Resolves the `apikey` header and `Authorization: Bearer` of a request to its caller. The key picks the role: the
9
+ * publishable key is `anon`, the secret key `app`. A user's token in the bearer makes either `authenticated` with its
10
+ * claims. A bearer that is no token must be the key itself, as supabase-js sends it when nobody is signed in. A
11
+ * missing or wrong key, a wrong bearer or a token that does not verify or names no user is a 401 problem.
12
+ */
13
+ const apiKeyCaller = (options) => async (request) => {
14
+ const key = request.headers.get("apikey")?.trim();
15
+ if (!key) throw unauthorized("Send the app's publishable or secret key in the apikey header");
16
+ const publishable = sameKey(key, options.keys.publishable);
17
+ if (!publishable && !sameKey(key, options.keys.secret)) throw unauthorized("The apikey header holds no key of this app");
18
+ const header = request.headers.get("authorization");
19
+ const [scheme, bearer] = header?.split(" ") ?? [];
20
+ if (header !== null && (scheme?.toLowerCase() !== "bearer" || !bearer)) throw unauthorized("Send a bearer token in the Authorization header");
21
+ if (bearer !== void 0 && isJwt(bearer)) {
22
+ const claims = await options.verify(bearer);
23
+ if (!claims.sub) throw unauthorized("The bearer token has no subject");
24
+ return {
25
+ role: "authenticated",
26
+ claims
27
+ };
28
+ }
29
+ if (bearer !== void 0 && !sameKey(bearer, key)) throw unauthorized("The bearer is neither a token nor the key in the apikey header");
30
+ return publishable ? {
31
+ role: "anon",
32
+ claims: { role: "anon" }
33
+ } : {
34
+ role: "app",
35
+ claims: { role: "service_role" }
36
+ };
37
+ };
38
+ //#endregion
39
+ export { apiKeyCaller };
@@ -1,5 +1,5 @@
1
1
  import { Authenticator, TenantValue } from "../types.js";
2
- import { JWTVerifyGetKey } from "jose";
2
+ import { JWTPayload, JWTVerifyGetKey } from "jose";
3
3
  //#region src/auth/jwt.d.ts
4
4
  export type JwtOptions = {
5
5
  /** JWKS endpoint of the identity provider; or pass `keys`, for example `createLocalJWKSet`. */
@@ -20,6 +20,9 @@ export type JwtOptions = {
20
20
  /** A fixed tenant (or resolver) used instead of any claim, for single-tenant deployments. */
21
21
  tenant?: TenantValue | (() => Promise<TenantValue | undefined>);
22
22
  };
23
+ export type JwtVerifyOptions = Pick<JwtOptions, 'jwksUrl' | 'keys' | 'issuer' | 'audience' | 'algorithms'>;
24
+ /** Verifies a JWT against a JWKS and returns its claims; a token that does not verify is a 401 problem. */
25
+ export declare const jwtVerifier: (options: JwtVerifyOptions) => (token: string) => Promise<JWTPayload>;
23
26
  /** Verifies `Authorization: Bearer <jwt>` against a JWKS; `sub` is the user id. Without the header, `MissingCredentials`. */
24
27
  export declare const jwtAuthenticator: (options: JwtOptions) => Authenticator;
25
28
  //#endregion
package/dist/auth/jwt.js CHANGED
@@ -17,26 +17,33 @@ const rolesOf = (claim) => {
17
17
  if (typeof claim === "string") return claim.split(/[\s,]+/).filter(Boolean);
18
18
  return Array.isArray(claim) ? claim.filter((role) => typeof role === "string") : [];
19
19
  };
20
- /** Verifies `Authorization: Bearer <jwt>` against a JWKS; `sub` is the user id. Without the header, `MissingCredentials`. */
21
- const jwtAuthenticator = (options) => {
22
- const { rolesClaim = "roles", tenantClaim = "tenant" } = options;
20
+ /** Verifies a JWT against a JWKS and returns its claims; a token that does not verify is a 401 problem. */
21
+ const jwtVerifier = (options) => {
23
22
  const keys = options.keys ?? (options.jwksUrl ? createRemoteJWKSet(new URL(options.jwksUrl)) : void 0);
24
23
  if (!keys) throw new Error("jwtAuthenticator needs jwksUrl or keys");
25
- return async (request) => {
26
- const header = request.headers.get("authorization");
27
- if (header === null) throw new MissingCredentials("Send a bearer token in the Authorization header");
28
- const [scheme, token] = header.split(" ");
29
- if (scheme?.toLowerCase() !== "bearer" || !token) throw unauthorized("Send a bearer token in the Authorization header");
30
- const verified = await jwtVerify(token, keys, {
24
+ return async (token) => {
25
+ return (await jwtVerify(token, keys, {
31
26
  algorithms: options.algorithms ?? asymmetric,
32
27
  ...options.issuer && { issuer: options.issuer },
33
28
  ...options.audience && { audience: options.audience }
34
29
  }).catch((error) => {
35
30
  if (error instanceof errors.JOSEError) throw unauthorized("The bearer token is invalid or expired");
36
31
  throw error;
37
- });
38
- const { sub, [tenantClaim]: tenant, [rolesClaim]: roles } = verified.payload;
39
- const extraRoles = options.extraRolesClaim === void 0 ? [] : rolesOf(verified.payload[options.extraRolesClaim]);
32
+ })).payload;
33
+ };
34
+ };
35
+ /** Verifies `Authorization: Bearer <jwt>` against a JWKS; `sub` is the user id. Without the header, `MissingCredentials`. */
36
+ const jwtAuthenticator = (options) => {
37
+ const { rolesClaim = "roles", tenantClaim = "tenant" } = options;
38
+ const verify = jwtVerifier(options);
39
+ return async (request) => {
40
+ const header = request.headers.get("authorization");
41
+ if (header === null) throw new MissingCredentials("Send a bearer token in the Authorization header");
42
+ const [scheme, token] = header.split(" ");
43
+ if (scheme?.toLowerCase() !== "bearer" || !token) throw unauthorized("Send a bearer token in the Authorization header");
44
+ const payload = await verify(token);
45
+ const { sub, [tenantClaim]: tenant, [rolesClaim]: roles } = payload;
46
+ const extraRoles = options.extraRolesClaim === void 0 ? [] : rolesOf(payload[options.extraRolesClaim]);
40
47
  if (!sub) throw unauthorized("The bearer token has no subject");
41
48
  const claimed = typeof tenant === "string" || typeof tenant === "number" ? tenant : void 0;
42
49
  const tenantValue = options.tenant === void 0 ? claimed : typeof options.tenant === "function" ? await options.tenant() : options.tenant;
@@ -50,4 +57,4 @@ const jwtAuthenticator = (options) => {
50
57
  };
51
58
  };
52
59
  //#endregion
53
- export { jwtAuthenticator };
60
+ export { jwtAuthenticator, jwtVerifier };
@@ -1,5 +1,6 @@
1
1
  import { AdminAuth } from "./create-auth.js";
2
2
  import { Authenticator, TenantValue } from "../types.js";
3
+ import { JwtVerifyOptions } from "../auth/jwt.js";
3
4
  import { TrustedApp } from "./app-tokens.js";
4
5
  import { JwksCacheOptions } from "./jwks-cache.js";
5
6
  //#region src/better-auth/authenticator.d.ts
@@ -19,6 +20,10 @@ export type BetterAuthAuthenticatorOptions = {
19
20
  /** Other Protobase apps whose users this API admits with their own tokens, so their admin apps can draw its resources (see `TrustedApp`). */
20
21
  apps?: TrustedApp[];
21
22
  };
23
+ /** How the tokens of this Better Auth instance verify: its key set, and its issuer as both issuer and audience. */
24
+ export declare const betterAuthTokens: (options: Pick<BetterAuthAuthenticatorOptions, "auth" | "jwksUrl" | "issuer" | "cache">) => JwtVerifyOptions & {
25
+ issuer: string;
26
+ };
22
27
  /**
23
28
  * Verifies the bearer JWT that Better Auth's jwt plugin issues. `sub` is the user id, the `role` claim (a comma
24
29
  * separated string from the admin plugin) becomes `roles`. With organizations, the `org` claim is the tenant and the
@@ -3,6 +3,17 @@ import { jwtAuthenticator } from "../auth/jwt.js";
3
3
  import { withTrustedApps } from "./app-tokens.js";
4
4
  import { cachedKeys } from "./jwks-cache.js";
5
5
  //#region src/better-auth/authenticator.ts
6
+ /** How the tokens of this Better Auth instance verify: its key set, and its issuer as both issuer and audience. */
7
+ const betterAuthTokens = (options) => {
8
+ const issuer = options.issuer ?? options.auth?.issuer;
9
+ if (!issuer) throw new Error("betterAuthAuthenticator needs `issuer` (or an `auth` instance with a baseURL)");
10
+ if (!options.auth && !options.jwksUrl) throw new Error("betterAuthAuthenticator needs `auth` or `jwksUrl`");
11
+ return {
12
+ ...options.auth ? { keys: cachedKeys(() => options.auth.api.getJwks(), options.cache) } : { jwksUrl: options.jwksUrl },
13
+ issuer,
14
+ audience: issuer
15
+ };
16
+ };
6
17
  /**
7
18
  * Verifies the bearer JWT that Better Auth's jwt plugin issues. `sub` is the user id, the `role` claim (a comma
8
19
  * separated string from the admin plugin) becomes `roles`. With organizations, the `org` claim is the tenant and the
@@ -10,14 +21,11 @@ import { cachedKeys } from "./jwks-cache.js";
10
21
  * role, is left out. With `apps`, the tokens of those apps are admitted too, with the roles each gives its users here.
11
22
  */
12
23
  const betterAuthAuthenticator = (options) => {
13
- const issuer = options.issuer ?? options.auth?.issuer;
14
- if (!issuer) throw new Error("betterAuthAuthenticator needs `issuer` (or an `auth` instance with a baseURL)");
15
- if (!options.auth && !options.jwksUrl) throw new Error("betterAuthAuthenticator needs `auth` or `jwksUrl`");
24
+ const tokens = betterAuthTokens(options);
25
+ const { issuer } = tokens;
16
26
  const organizations = options.organizations ?? Boolean(options.auth?.organizations);
17
27
  const verify = jwtAuthenticator({
18
- ...options.auth ? { keys: cachedKeys(() => options.auth.api.getJwks(), options.cache) } : { jwksUrl: options.jwksUrl },
19
- issuer,
20
- audience: issuer,
28
+ ...tokens,
21
29
  rolesClaim: "role",
22
30
  ...organizations && {
23
31
  tenantClaim: "org",
@@ -39,4 +47,4 @@ const betterAuthAuthenticator = (options) => {
39
47
  }, issuer, options.apps ?? []);
40
48
  };
41
49
  //#endregion
42
- export { betterAuthAuthenticator };
50
+ export { betterAuthAuthenticator, betterAuthTokens };
@@ -8,6 +8,8 @@ import { OrganizationsOptions, ResolvedOrganizations } from "./organizations/opt
8
8
  import { PlatformSignIn } from "./platform-sign-in.js";
9
9
  import { SsoOptions } from "./saml-sign-in.js";
10
10
  import { signUpPaths } from "./sign-up/sign-up-plugin.js";
11
+ import { PasswordHash } from "./password-hashes.js";
12
+ import { UserTableOptions } from "./user-table.js";
11
13
  import "../index.js";
12
14
  import { BetterAuthOptions } from "better-auth";
13
15
  //#region src/better-auth/create-auth.d.ts
@@ -80,9 +82,22 @@ export type CreateAuthOptions = {
80
82
  onUserCreated?: UserCreatedHook;
81
83
  /**
82
84
  * The id of a new user, by any route. Default Better Auth's random 32-character text. `.owner()` fields hold it: a
83
- * `text` column for the default, or an integer column when this takes the next value of the app's own users sequence.
85
+ * `text` column for the default, an integer column when this takes the next value of the app's own users sequence,
86
+ * or a `uuid` column with `'uuid'`, which makes a random UUID in code and makes every id column of the store a `uuid`.
84
87
  */
85
- generateUserId?: () => string | Promise<string>;
88
+ generateUserId?: (() => string | Promise<string>) | 'uuid';
89
+ /**
90
+ * The user table, when it is one another system made, such as Supabase's `auth.users`: its name, the columns named
91
+ * otherwise than Protobase names them, and a JSON column for each user's metadata, for example `{ table: 'users',
92
+ * columns: { role: 'app_role' }, metadata: 'raw_user_meta_data' }`. The other tables keep their names. Default the
93
+ * table `protobase auth migration` makes.
94
+ */
95
+ users?: UserTableOptions;
96
+ /**
97
+ * Password hashes another system wrote that sign-in accepts besides Better Auth's own, such as `['bcrypt']` for
98
+ * Supabase Auth's. The first sign-in with one stores the password again as Better Auth hashes it. Default none.
99
+ */
100
+ passwordHashes?: PasswordHash[];
86
101
  /**
87
102
  * The identity provider the operator's staff sign in with to sign in as people of this app, for support. Defaults
88
103
  * to the one the platform passes in `PROTOBASE_OPERATOR_ISSUER`, `PROTOBASE_OPERATOR_CLIENT_ID` and
@@ -123,22 +138,35 @@ export type UserCreatedHook = NonNullable<UserCreateHooks['after']>;
123
138
  export declare const normalizeRoles: (roles?: RolesInput) => string[];
124
139
  export declare const createAuth: (options: CreateAuthOptions) => import("better-auth").Auth<{
125
140
  user: {
141
+ modelName: string;
126
142
  fields: {
143
+ name: string;
144
+ email: string;
127
145
  emailVerified: string;
146
+ image: string;
128
147
  createdAt: string;
129
148
  updatedAt: string;
130
149
  };
131
150
  } | {
132
151
  additionalFields: {
133
- readonly lastOrganizationId: {
152
+ metadata?: {
153
+ readonly type: "json";
154
+ readonly required: false;
155
+ readonly fieldName: string;
156
+ } | undefined;
157
+ lastOrganizationId?: {
134
158
  readonly type: "string";
135
159
  readonly required: false;
136
160
  readonly input: false;
137
161
  readonly fieldName: "last_organization_id";
138
- };
162
+ } | undefined;
139
163
  };
164
+ modelName: string;
140
165
  fields: {
166
+ name: string;
167
+ email: string;
141
168
  emailVerified: string;
169
+ image: string;
142
170
  createdAt: string;
143
171
  updatedAt: string;
144
172
  };
@@ -178,7 +206,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
178
206
  updatedAt: string;
179
207
  };
180
208
  };
181
- plugins: [{
209
+ plugins: ({
182
210
  id: "admin";
183
211
  version: string;
184
212
  init(): {
@@ -1094,7 +1122,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1094
1122
  }>;
1095
1123
  };
1096
1124
  }>;
1097
- }, {
1125
+ } | {
1098
1126
  id: "jwt";
1099
1127
  version: string;
1100
1128
  options: NoInfer<{
@@ -1362,7 +1390,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
1362
1390
  };
1363
1391
  };
1364
1392
  };
1365
- }, {
1393
+ } | {
1366
1394
  hooks: {
1367
1395
  after: {
1368
1396
  matcher: (context: Parameters<(context: import("better-auth").HookEndpointContext) => boolean>[0]) => boolean;
@@ -2030,7 +2058,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2030
2058
  ACCOUNT_TEMPORARILY_LOCKED: import("better-auth").RawError<"ACCOUNT_TEMPORARILY_LOCKED">;
2031
2059
  INVALID_TWO_FACTOR_COOKIE: import("better-auth").RawError<"INVALID_TWO_FACTOR_COOKIE">;
2032
2060
  };
2033
- }, {
2061
+ } | {
2034
2062
  endpoints: {
2035
2063
  generatePasskeyAuthenticationOptions: import("better-auth").StrictEndpoint<"/passkey/generate-authenticate-options", {
2036
2064
  method: "GET";
@@ -2332,7 +2360,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2332
2360
  expiresAt: Date;
2333
2361
  token: string;
2334
2362
  ipAddress?: string | null | undefined;
2335
- userAgent? /** Every origin the app is served on, `baseURL`'s first. */ : string | null | undefined;
2363
+ userAgent?: string | null | undefined;
2336
2364
  };
2337
2365
  user: Record<string, any> & {
2338
2366
  id: string;
@@ -2511,7 +2539,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
2511
2539
  RESOLVED_USER_INVALID: import("better-auth").RawError<"RESOLVED_USER_INVALID">;
2512
2540
  };
2513
2541
  options: import("@better-auth/passkey").PasskeyOptions | undefined;
2514
- }, ...({
2542
+ } | {
2515
2543
  id: "email-otp";
2516
2544
  version: string;
2517
2545
  init(ctx: import("better-auth").AuthContext): {
@@ -5198,8 +5226,8 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5198
5226
  appRoles: string[];
5199
5227
  id: string;
5200
5228
  createdAt: Date;
5201
- userId: string;
5202
5229
  role: string;
5230
+ userId: string;
5203
5231
  organizationId: string;
5204
5232
  };
5205
5233
  }>;
@@ -5212,7 +5240,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5212
5240
  handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
5213
5241
  }[];
5214
5242
  };
5215
- })[], {
5243
+ } | {
5216
5244
  id: "protobase-users";
5217
5245
  endpoints: {
5218
5246
  getInvitation: import("better-auth").StrictEndpoint<"/invitation", {
@@ -5262,8 +5290,8 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5262
5290
  appRoles: string[];
5263
5291
  id: string;
5264
5292
  createdAt: Date;
5265
- userId: string;
5266
5293
  role: string;
5294
+ userId: string;
5267
5295
  organizationId: string;
5268
5296
  };
5269
5297
  }>;
@@ -5678,7 +5706,15 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5678
5706
  handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
5679
5707
  }[];
5680
5708
  };
5681
- }];
5709
+ } | {
5710
+ id: "protobase-rehash-passwords";
5711
+ hooks: {
5712
+ after: {
5713
+ matcher: (ctx: import("better-auth").HookEndpointContext) => boolean;
5714
+ handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
5715
+ }[];
5716
+ };
5717
+ })[];
5682
5718
  disabledPaths: string[];
5683
5719
  databaseHooks: {
5684
5720
  session?: {
@@ -5757,6 +5793,9 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5757
5793
  };
5758
5794
  };
5759
5795
  advanced: {
5796
+ database?: {
5797
+ generateId: "uuid";
5798
+ } | undefined;
5760
5799
  useSecureCookies: boolean;
5761
5800
  defaultCookieAttributes: {
5762
5801
  sameSite: "lax";
@@ -5785,6 +5824,12 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
5785
5824
  url: string;
5786
5825
  token: string;
5787
5826
  }) => Promise<void>) | undefined;
5827
+ password?: {
5828
+ verify: ({ hash, password }: {
5829
+ hash: string;
5830
+ password: string;
5831
+ }) => Promise<boolean>;
5832
+ } | undefined;
5788
5833
  enabled: true;
5789
5834
  disableSignUp: true;
5790
5835
  minPasswordLength: number;
@@ -21,8 +21,10 @@ import { signInPolicyPlugin } from "./policy-plugin.js";
21
21
  import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin, samlPlugin } from "./saml-sign-in.js";
22
22
  import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
23
23
  import { signUpPaths, signUpPlugin } from "./sign-up/sign-up-plugin.js";
24
+ import { rehashPasswordsPlugin, verifyAcceptedHash } from "./password-hashes.js";
24
25
  import { staffSignInPlugin } from "./staff-plugin.js";
25
26
  import { twoFactorAfterPasswordOrCode } from "./two-factor-challenge.js";
27
+ import { metadataField, userFieldsNamed, userModel, userTable, withUserColumns } from "./user-table.js";
26
28
  import { usersPlugin } from "./users/users-plugin.js";
27
29
  import { APIError } from "better-auth/api";
28
30
  import { betterAuth } from "better-auth";
@@ -44,6 +46,7 @@ const tokenLifetime = "15m";
44
46
  /** The configured roles with `admin` first; `admin` is added when missing. */
45
47
  const normalizeRoles = (roles) => roleDefinitions(roles).names;
46
48
  const adminImpersonationPaths = ["/admin/impersonate-user", "/admin/stop-impersonating"];
49
+ const newUserId = (generate) => generate === "uuid" ? crypto.randomUUID() : generate();
47
50
  const noContext = () => Promise.reject(/* @__PURE__ */ new Error("The auth context is read only once createAuth has made the instance"));
48
51
  const organizationPaths = ["/organization/set-active", "/organization/accept-invitation"];
49
52
  /**
@@ -60,6 +63,12 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
60
63
  baseURL: options.baseURL
61
64
  };
62
65
  const samlIds = saml.map((provider) => provider.id);
66
+ const users = userTable(options.users);
67
+ const passwordHashes = options.passwordHashes ?? [];
68
+ const userFields = {
69
+ ...organizations && userFieldsNamed(lastOrganizationField, users),
70
+ ...metadataField(users)
71
+ };
63
72
  return {
64
73
  appName,
65
74
  database: options.database,
@@ -75,13 +84,14 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
75
84
  enabled: true,
76
85
  disableSignUp: true,
77
86
  minPasswordLength: 12,
87
+ ...passwordHashes.length > 0 && { password: { verify: verifyAcceptedHash(passwordHashes) } },
78
88
  ...mail && passwordResetOptions(mail)
79
89
  },
80
90
  ...options.socialProviders && { socialProviders: options.socialProviders },
81
- user: organizations ? {
82
- ...coreNames.user,
83
- additionalFields: lastOrganizationField
84
- } : coreNames.user,
91
+ user: Object.keys(userFields).length > 0 ? {
92
+ ...userModel(users),
93
+ additionalFields: userFields
94
+ } : userModel(users),
85
95
  session: coreNames.session,
86
96
  verification: coreNames.verification,
87
97
  account: {
@@ -162,8 +172,9 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
162
172
  invitationUrl: organizations?.invitationUrl ?? defaultInvitationUrl(options.baseURL),
163
173
  invitationSeconds: organizations?.invitationExpiresIn ?? 604800,
164
174
  ...organizations && { organizations }
165
- })
166
- ],
175
+ }),
176
+ ...passwordHashes.length > 0 ? [rehashPasswordsPlugin(passwordHashes)] : []
177
+ ].map((plugin) => withUserColumns(plugin, users)),
167
178
  disabledPaths: [
168
179
  ...unusedEmailCodePaths,
169
180
  ...adminImpersonationPaths,
@@ -183,7 +194,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
183
194
  });
184
195
  const withId = options.generateUserId ? {
185
196
  ...user,
186
- id: await options.generateUserId()
197
+ id: await newUserId(options.generateUserId)
187
198
  } : user;
188
199
  return { data: context?.path === "/admin/create-user" || samlProvider ? {
189
200
  ...withId,
@@ -231,7 +242,8 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
231
242
  sameSite: "lax",
232
243
  secure
233
244
  },
234
- backgroundTasks: { handler: () => {} }
245
+ backgroundTasks: { handler: () => {} },
246
+ ...options.generateUserId === "uuid" && { database: { generateId: "uuid" } }
235
247
  }
236
248
  };
237
249
  };
@@ -0,0 +1,4 @@
1
+ //#region src/better-auth/password-hashes.d.ts
2
+ /** The hash formats sign-in accepts besides Better Auth's own. */
3
+ export type PasswordHash = 'bcrypt';
4
+ //#endregion
@@ -0,0 +1,31 @@
1
+ import { APIError, createAuthMiddleware } from "better-auth/api";
2
+ import { verifyPassword } from "better-auth/crypto";
3
+ import bcrypt from "bcryptjs";
4
+ //#region src/better-auth/password-hashes.ts
5
+ const formats = { bcrypt: {
6
+ matches: (hash) => /^\$2[aby]\$/.test(hash),
7
+ verify: (password, hash) => bcrypt.compare(password, hash)
8
+ } };
9
+ const formatOf = (accepted, hash) => accepted.map((name) => formats[name]).find((format) => format.matches(hash));
10
+ /** Better Auth's `password.verify`: the accepted formats first, then its own scrypt. */
11
+ const verifyAcceptedHash = (accepted) => ({ hash, password }) => formatOf(accepted, hash)?.verify(password, hash) ?? verifyPassword({
12
+ hash,
13
+ password
14
+ });
15
+ /** After a sign-in with a password in one of the accepted formats, stores it again as Better Auth hashes it. */
16
+ const rehashPasswordsPlugin = (accepted) => ({
17
+ id: "protobase-rehash-passwords",
18
+ hooks: { after: [{
19
+ matcher: (ctx) => ctx.path === "/sign-in/email",
20
+ handler: createAuthMiddleware(async (ctx) => {
21
+ if (ctx.context.returned instanceof APIError) return;
22
+ const { email, password } = ctx.body;
23
+ const found = await ctx.context.internalAdapter.findUserByEmail(email.toLowerCase(), { includeAccounts: true });
24
+ const account = found?.accounts.find((candidate) => candidate.providerId === "credential" && candidate.accountId === found.user.id);
25
+ if (!found || !account?.password || !formatOf(accepted, account.password)) return;
26
+ await ctx.context.internalAdapter.updatePassword(found.user.id, await ctx.context.password.hash(password));
27
+ })
28
+ }] }
29
+ });
30
+ //#endregion
31
+ export { rehashPasswordsPlugin, verifyAcceptedHash };
@@ -1,11 +1,6 @@
1
1
  //#region src/better-auth/snake-case-names.ts
2
- /** Better Auth's own tables, as its `user`, `session`, `account` and `verification` options name them. */
2
+ /** Better Auth's own tables, as its `session`, `account` and `verification` options name them; the user table's are in `user-table.ts`. */
3
3
  const coreNames = {
4
- user: { fields: {
5
- emailVerified: "email_verified",
6
- createdAt: "created_at",
7
- updatedAt: "updated_at"
8
- } },
9
4
  session: { fields: {
10
5
  expiresAt: "expires_at",
11
6
  createdAt: "created_at",
@@ -0,0 +1,31 @@
1
+ import "better-auth";
2
+ //#region src/better-auth/user-table.d.ts
3
+ /** Each column of the user table, by the name Protobase gives it, with Better Auth's field it holds. */
4
+ declare const userFields: {
5
+ readonly name: "name";
6
+ readonly email: "email";
7
+ readonly email_verified: "emailVerified";
8
+ readonly image: "image";
9
+ readonly created_at: "createdAt";
10
+ readonly updated_at: "updatedAt";
11
+ readonly role: "role";
12
+ readonly banned: "banned";
13
+ readonly ban_reason: "banReason";
14
+ readonly ban_expires: "banExpires";
15
+ readonly two_factor_enabled: "twoFactorEnabled";
16
+ readonly last_role: "lastRole";
17
+ readonly last_organization_id: "lastOrganizationId";
18
+ };
19
+ export type UserColumn = keyof typeof userFields;
20
+ export type UserTableOptions = {
21
+ /** The table's name, default `user`, for example `users` for Supabase's `auth.users`. */
22
+ table?: string;
23
+ /**
24
+ * The columns named otherwise than Protobase names them: `{ role: 'app_role' }`, where `role` holds something else
25
+ * already, such as the Postgres role in Supabase's `auth.users`.
26
+ */
27
+ columns?: Partial<Record<UserColumn, string>>;
28
+ /** A JSON column that holds each user's metadata, such as `raw_user_meta_data`; none by default. */
29
+ metadata?: string;
30
+ };
31
+ //#endregion
@@ -0,0 +1,69 @@
1
+ //#region src/better-auth/user-table.ts
2
+ /** Each column of the user table, by the name Protobase gives it, with Better Auth's field it holds. */
3
+ const userFields = {
4
+ name: "name",
5
+ email: "email",
6
+ email_verified: "emailVerified",
7
+ image: "image",
8
+ created_at: "createdAt",
9
+ updated_at: "updatedAt",
10
+ role: "role",
11
+ banned: "banned",
12
+ ban_reason: "banReason",
13
+ ban_expires: "banExpires",
14
+ two_factor_enabled: "twoFactorEnabled",
15
+ last_role: "lastRole",
16
+ last_organization_id: "lastOrganizationId"
17
+ };
18
+ /** The user table's names, from `createAuth`'s `users` option; unknown columns are an error. */
19
+ const userTable = ({ table = "user", columns = {}, metadata } = {}) => {
20
+ const unknown = Object.keys(columns).find((name) => !(name in userFields));
21
+ if (unknown !== void 0) throw new Error(`users.columns names "${unknown}", which is not a column of the user table: ${Object.keys(userFields).join(", ")}`);
22
+ const column = (name) => columns[name] ?? name;
23
+ return {
24
+ modelName: table,
25
+ column,
26
+ fieldNames: Object.fromEntries(Object.entries(userFields).map(([name, field]) => [field, column(name)])),
27
+ metadata
28
+ };
29
+ };
30
+ /** Better Auth's `user` option: the table and its core columns. */
31
+ const userModel = ({ modelName, column }) => ({
32
+ modelName,
33
+ fields: {
34
+ name: column("name"),
35
+ email: column("email"),
36
+ emailVerified: column("email_verified"),
37
+ image: column("image"),
38
+ createdAt: column("created_at"),
39
+ updatedAt: column("updated_at")
40
+ }
41
+ });
42
+ /** Each user field the user metadata column adds; none without one. */
43
+ const metadataField = ({ metadata }) => metadata ? { metadata: {
44
+ type: "json",
45
+ required: false,
46
+ fieldName: metadata
47
+ } } : void 0;
48
+ /** The fields, each with its column named as the app names it. */
49
+ const userFieldsNamed = (fields, { fieldNames }) => Object.fromEntries(Object.entries(fields).map(([field, attribute]) => [field, {
50
+ ...attribute,
51
+ fieldName: fieldNames[field] ?? attribute.fieldName
52
+ }]));
53
+ /** The plugin, with the columns it adds to the user table named as the app names them. */
54
+ const withUserColumns = (plugin, table) => {
55
+ const user = plugin.schema?.user;
56
+ if (!user) return plugin;
57
+ return {
58
+ ...plugin,
59
+ schema: {
60
+ ...plugin.schema,
61
+ user: {
62
+ ...user,
63
+ fields: userFieldsNamed(user.fields, table)
64
+ }
65
+ }
66
+ };
67
+ };
68
+ //#endregion
69
+ export { metadataField, userFieldsNamed, userModel, userTable, withUserColumns };
@@ -8,12 +8,14 @@ export type LinkedAccount = {
8
8
  providerId: string;
9
9
  accountId: string;
10
10
  };
11
+ /** `metadata` lands in the user metadata column, which `createAuth`'s `users.metadata` names; it needs one. */
11
12
  export type NewUser = {
12
13
  email: string;
13
14
  password: string;
14
15
  name?: string;
15
16
  role?: string;
16
17
  accounts?: LinkedAccount[];
18
+ metadata?: Record<string, unknown>;
17
19
  };
18
20
  export type StoredUser = {
19
21
  id: string;
@@ -37,7 +37,8 @@ const createUser = async (auth, input) => {
37
37
  email: input.email,
38
38
  password: input.password,
39
39
  name: input.name ?? input.email.split("@")[0],
40
- role
40
+ role,
41
+ ...input.metadata && { data: { metadata: input.metadata } }
41
42
  } });
42
43
  const { internalAdapter } = await auth.$context;
43
44
  for (const account of accounts) await internalAdapter.linkAccount({
package/dist/index.d.ts CHANGED
@@ -45,10 +45,12 @@ import { isBackgroundWork, runInBackground } from "./background-work.js";
45
45
  import { checkViewQueries } from "./check-view-queries.js";
46
46
  import { ProjectConfig, defineConfig, mergeConfig } from "./project-config.js";
47
47
  import { consoleAuditQueue } from "./audit/console-queue.js";
48
- import { JwtOptions, jwtAuthenticator } from "./auth/jwt.js";
48
+ import { JwtOptions, JwtVerifyOptions, jwtAuthenticator, jwtVerifier } from "./auth/jwt.js";
49
+ import { ApiKeys, ApiKeysOptions, apiKeySettings, apiKeyVariables } from "./api-keys/api-key-settings.js";
50
+ import { Caller, CallerOptions, apiKeyCaller } from "./api-keys/resolve-caller.js";
49
51
  import { HttpProblem, MissingCredentials, badRequest, forbidden, notFound, unauthorized } from "./problem.js";
50
52
  import { TrustedApp, withTrustedApps } from "./better-auth/app-tokens.js";
51
- import { BetterAuthAuthenticatorOptions, betterAuthAuthenticator } from "./better-auth/authenticator.js";
53
+ import { BetterAuthAuthenticatorOptions, betterAuthAuthenticator, betterAuthTokens } from "./better-auth/authenticator.js";
52
54
  import { parseRoles } from "./better-auth/parse-roles.js";
53
55
  import { LinkedAccount, NewUser, StoredUser, createUser, deleteUser, hasUsers, listUsers, roleChoices, setUserBanned, setUserRole } from "./better-auth/users.js";
54
56
  import { IssueTokenOptions, issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
@@ -93,4 +95,4 @@ import { FatalJobError } from "./jobs/run-job.js";
93
95
  import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
94
96
  import { jobsDrainApp } from "./jobs/drain-route.js";
95
97
  import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
96
- export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type MemberInput, MissingCredentials, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
98
+ export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type MemberInput, MissingCredentials, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
package/dist/index.js CHANGED
@@ -49,9 +49,11 @@ import { handleDroppedConnections } from "./dropped-connections.js";
49
49
  import { configExports } from "./resource-source.js";
50
50
  import { checkViewQueries } from "./check-view-queries.js";
51
51
  import { defineConfig, mergeConfig } from "./project-config.js";
52
- import { jwtAuthenticator } from "./auth/jwt.js";
52
+ import { jwtAuthenticator, jwtVerifier } from "./auth/jwt.js";
53
+ import { apiKeySettings, apiKeyVariables } from "./api-keys/api-key-settings.js";
54
+ import { apiKeyCaller } from "./api-keys/resolve-caller.js";
53
55
  import { withTrustedApps } from "./better-auth/app-tokens.js";
54
- import { betterAuthAuthenticator } from "./better-auth/authenticator.js";
56
+ import { betterAuthAuthenticator, betterAuthTokens } from "./better-auth/authenticator.js";
55
57
  import { issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
56
58
  import { authSchemaMigration, checkAuthSchema } from "./better-auth/auth-schema.js";
57
59
  import { checkComputed, computedMigration } from "./computed/computed-schema.js";
@@ -70,4 +72,4 @@ import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/p
70
72
  import { defineEmail } from "./mail/define-email.js";
71
73
  import { startJobs } from "./jobs/runtime.js";
72
74
  import { jobsDrainApp } from "./jobs/drain-route.js";
73
- export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
75
+ export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.25.9",
3
+ "version": "0.25.11",
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,14 +32,15 @@
32
32
  "dependencies": {
33
33
  "@better-auth/passkey": "^1.7.7",
34
34
  "@better-auth/sso": "^1.7.7",
35
- "@protobase/layout": "0.25.9",
36
- "@protobase/query": "0.25.9",
37
- "@protobase/schema": "0.25.9",
35
+ "@protobase/layout": "0.25.11",
36
+ "@protobase/query": "0.25.11",
37
+ "@protobase/schema": "0.25.11",
38
38
  "@react-email/render": "^2.1.0",
39
39
  "@scalar/hono-api-reference": "^0.12.10",
40
40
  "@sentry/core": "^11.7.0",
41
41
  "@simplewebauthn/server": "^13.3.1",
42
42
  "altcha-lib": "^2.6.0",
43
+ "bcryptjs": "^3.0.3",
43
44
  "better-auth": "^1.7.7",
44
45
  "cron-parser": "^5.10.1",
45
46
  "file-type": "^22.1.1",