diff --git a/e2e/smoke/test/fixtures/tsconfig-decelration/package.json b/e2e/smoke/test/fixtures/tsconfig-decelration/package.json
index 7afdeebeaf..5d285600ad 100644
--- a/e2e/smoke/test/fixtures/tsconfig-decelration/package.json
+++ b/e2e/smoke/test/fixtures/tsconfig-decelration/package.json
@@ -6,7 +6,9 @@
"typecheck": "tsc --project tsconfig.json"
},
"dependencies": {
+ "@better-auth/sso": "workspace:*",
+ "@better-auth/stripe": "workspace:*",
"better-auth": "workspace:*",
- "better-sqlite3": "^12.4.1"
+ "stripe": "^18.5.0"
}
}
diff --git a/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts b/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts
new file mode 100644
index 0000000000..ae42a34580
--- /dev/null
+++ b/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts
@@ -0,0 +1,209 @@
+import { betterAuth } from "better-auth";
+import {
+ bearer,
+ admin,
+ multiSession,
+ organization,
+ twoFactor,
+ oneTap,
+ oAuthProxy,
+ openAPI,
+ customSession,
+ deviceAuthorization,
+ lastLoginMethod,
+} from "better-auth/plugins";
+import { nextCookies } from "better-auth/next-js";
+import { passkey } from "better-auth/plugins/passkey";
+import { stripe } from "@better-auth/stripe";
+import { sso } from "@better-auth/sso";
+import { Stripe } from "stripe";
+
+export const auth = betterAuth({
+ appName: "Better Auth Demo",
+ plugins: [
+ organization({}),
+ twoFactor({}),
+ passkey(),
+ openAPI(),
+ bearer(),
+ admin({ adminUserIds: ["EXD5zjob2SD6CBWcEQ6OpLRHcyoUbnaB"] }),
+ multiSession(),
+ oAuthProxy(),
+ nextCookies(),
+ oneTap(),
+ customSession(async (session) => {
+ return {
+ ...session,
+ user: {
+ ...session.user,
+ dd: "test",
+ },
+ };
+ }),
+ stripe({
+ stripeClient: new Stripe(process.env.STRIPE_KEY || "sk_test_"),
+ stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
+ subscription: {
+ enabled: true,
+ allowReTrialsForDifferentPlans: true,
+ plans: () => {
+ const PRO_PRICE_ID = {
+ default:
+ process.env.STRIPE_PRO_PRICE_ID ??
+ "price_1RoxnRHmTADgihIt4y8c0lVE",
+ annual:
+ process.env.STRIPE_PRO_ANNUAL_PRICE_ID ??
+ "price_1RoxnoHmTADgihItzFvVP8KT",
+ };
+ const PLUS_PRICE_ID = {
+ default:
+ process.env.STRIPE_PLUS_PRICE_ID ??
+ "price_1RoxnJHmTADgihIthZTLmrPn",
+ annual:
+ process.env.STRIPE_PLUS_ANNUAL_PRICE_ID ??
+ "price_1Roxo5HmTADgihItEbJu5llL",
+ };
+
+ return [
+ {
+ name: "Plus",
+ priceId: PLUS_PRICE_ID.default,
+ annualDiscountPriceId: PLUS_PRICE_ID.annual,
+ freeTrial: {
+ days: 7,
+ },
+ },
+ {
+ name: "Pro",
+ priceId: PRO_PRICE_ID.default,
+ annualDiscountPriceId: PRO_PRICE_ID.annual,
+ freeTrial: {
+ days: 7,
+ },
+ },
+ ];
+ },
+ },
+ }),
+ sso({
+ defaultSSO: [
+ {
+ domain: "http://localhost:3000",
+ providerId: "sso",
+ samlConfig: {
+ issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata",
+ entryPoint:
+ "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435",
+ cert: `-----BEGIN CERTIFICATE-----
+ MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL
+ BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx
+ MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD
+ ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ
+ HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj
+ yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g
+ 0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7
+ mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO
+ aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE
+ FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm
+ RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z
+ FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm
+ 2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx
+ UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J
+ QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9
+ vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+
+ jzGhYL6m9gFTm/8=
+ -----END CERTIFICATE-----`,
+ spMetadata: {
+ metadata: `
+
+
+
+
+
+ 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
+
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
+ Organization Name
+ Organization DisplayName
+ http://localhost:3000/
+
+
+ Technical Contact Name
+ technical_contact@gmail.com
+
+
+ Support Contact Name
+ support_contact@gmail.com
+
+
+ `,
+ },
+ idpMetadata: {
+ entityURL:
+ "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/metadata",
+ entityID:
+ "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435",
+ redirectURL:
+ "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso",
+ singleSignOnService: [
+ {
+ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
+ Location:
+ "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso",
+ },
+ ],
+ cert: `-----BEGIN CERTIFICATE-----
+ MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL
+ BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx
+ MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD
+ ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ
+ HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj
+ yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g
+ 0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7
+ mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO
+ aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE
+ FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm
+ RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z
+ FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm
+ 2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx
+ UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J
+ QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9
+ vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+
+ jzGhYL6m9gFTm/8=
+ -----END CERTIFICATE-----`,
+ },
+ callbackUrl: "/dashboard",
+ },
+ },
+ ],
+ }),
+ deviceAuthorization({
+ expiresIn: "3min",
+ interval: "5s",
+ }),
+ lastLoginMethod(),
+ ],
+});
+
+auth.api
+ .createOrganization({
+ body: {
+ name: "My Org",
+ slug: "my-org",
+ },
+ })
+ .catch();
diff --git a/packages/better-auth/src/api/index.ts b/packages/better-auth/src/api/index.ts
index d792e2d4d1..ca7ad2b4e4 100644
--- a/packages/better-auth/src/api/index.ts
+++ b/packages/better-auth/src/api/index.ts
@@ -6,7 +6,7 @@ import {
} from "better-call";
import type { BetterAuthOptions } from "@better-auth/core";
import type { UnionToIntersection } from "../types/helper";
-import { originCheckMiddleware } from "./middlewares/origin-check";
+import { originCheckMiddleware } from "./middlewares";
import {
callbackOAuth,
forgetPassword,
@@ -37,9 +37,9 @@ import {
requestPasswordReset,
requestPasswordResetCallback,
} from "./routes";
-import { ok } from "./routes/ok";
-import { signUpEmail } from "./routes/sign-up";
-import { error } from "./routes/error";
+import { ok } from "./routes";
+import { signUpEmail } from "./routes";
+import { error } from "./routes";
import { type InternalLogger, logger } from "@better-auth/core/env";
import type { BetterAuthPlugin } from "@better-auth/core";
import { onRequestRateLimit } from "./rate-limiter";
diff --git a/packages/better-auth/src/plugins/oauth-proxy/index.ts b/packages/better-auth/src/plugins/oauth-proxy/index.ts
index e6915e1f52..e20598efb8 100644
--- a/packages/better-auth/src/plugins/oauth-proxy/index.ts
+++ b/packages/better-auth/src/plugins/oauth-proxy/index.ts
@@ -21,7 +21,7 @@ function getVenderBaseURL() {
return vercel || netlify || render || aws || google || azure;
}
-interface OAuthProxyOptions {
+export interface OAuthProxyOptions {
/**
* The current URL of the application.
* The plugin will attempt to infer the current URL from your environment
diff --git a/packages/better-auth/src/plugins/open-api/generator.ts b/packages/better-auth/src/plugins/open-api/generator.ts
index a65d930d04..5d7700be0c 100644
--- a/packages/better-auth/src/plugins/open-api/generator.ts
+++ b/packages/better-auth/src/plugins/open-api/generator.ts
@@ -81,13 +81,13 @@ function getTypeFromZodType(zodType: z.ZodType) {
return allowedType.has(type) ? (type as AllowedType) : "string";
}
-type FieldSchema = {
+export type FieldSchema = {
type: DBFieldType;
default?: DBFieldAttributeConfig["defaultValue"] | "Generated at runtime";
readOnly?: boolean;
};
-type OpenAPIModelSchema = {
+export type OpenAPIModelSchema = {
type: "object";
properties: Record;
required?: string[];
diff --git a/packages/better-auth/src/plugins/open-api/index.ts b/packages/better-auth/src/plugins/open-api/index.ts
index 7467d1a729..0be0b6f6ec 100644
--- a/packages/better-auth/src/plugins/open-api/index.ts
+++ b/packages/better-auth/src/plugins/open-api/index.ts
@@ -6,6 +6,8 @@ import type { LiteralString } from "../../types/helper";
import { APIError } from "../../api";
import { createAuthEndpoint } from "@better-auth/core/api";
+export type { FieldSchema, Path, OpenAPIModelSchema } from "./generator";
+
type ScalarTheme =
| "alternate"
| "default"
diff --git a/packages/better-auth/src/plugins/organization/client.ts b/packages/better-auth/src/plugins/organization/client.ts
index 79d826d6d9..a65a197f54 100644
--- a/packages/better-auth/src/plugins/organization/client.ts
+++ b/packages/better-auth/src/plugins/organization/client.ts
@@ -10,7 +10,7 @@ import type {
import type { Prettify } from "../../types/helper";
import { type AccessControl, type Role } from "../access";
import type { BetterAuthClientPlugin } from "@better-auth/core";
-import { organization } from "./organization";
+import { type OrganizationPlugin } from "./organization";
import { useAuthQuery } from "../../client";
import {
defaultStatements,
@@ -129,29 +129,27 @@ export const organizationClient = (
type Schema = CO["schema"];
return {
id: "organization",
- $InferServerPlugin: {} as ReturnType<
- typeof organization<{
- ac: CO["ac"] extends AccessControl
- ? CO["ac"]
- : AccessControl;
- roles: CO["roles"] extends Record
- ? CO["roles"]
- : {
- admin: Role;
- member: Role;
- owner: Role;
- };
- teams: {
- enabled: CO["teams"] extends { enabled: true } ? true : false;
- };
- schema: Schema;
- dynamicAccessControl: {
- enabled: CO["dynamicAccessControl"] extends { enabled: true }
- ? true
- : false;
- };
- }>
- >,
+ $InferServerPlugin: {} as OrganizationPlugin<{
+ ac: CO["ac"] extends AccessControl
+ ? CO["ac"]
+ : AccessControl;
+ roles: CO["roles"] extends Record
+ ? CO["roles"]
+ : {
+ admin: Role;
+ member: Role;
+ owner: Role;
+ };
+ teams: {
+ enabled: CO["teams"] extends { enabled: true } ? true : false;
+ };
+ schema: Schema;
+ dynamicAccessControl: {
+ enabled: CO["dynamicAccessControl"] extends { enabled: true }
+ ? true
+ : false;
+ };
+ }>,
getActions: ($fetch, _$store, co) => ({
$Infer: {
ActiveOrganization: {} as OrganizationReturn,
diff --git a/packages/better-auth/src/plugins/organization/organization.ts b/packages/better-auth/src/plugins/organization/organization.ts
index e77887ffbc..82d072d840 100644
--- a/packages/better-auth/src/plugins/organization/organization.ts
+++ b/packages/better-auth/src/plugins/organization/organization.ts
@@ -2,7 +2,7 @@ import { APIError } from "better-call";
import * as z from "zod";
import type { BetterAuthPluginDBSchema } from "@better-auth/core/db";
import { createAuthEndpoint } from "@better-auth/core/api";
-import { getSessionFromCtx } from "../../api/routes";
+import { getSessionFromCtx } from "../../api";
import type { BetterAuthPlugin } from "@better-auth/core";
import { shimContext } from "../../utils/shim";
import { type AccessControl } from "../access";
@@ -50,6 +50,7 @@ import type {
InferInvitation,
InferMember,
InferOrganization,
+ OrganizationSchema,
Team,
TeamMember,
} from "./schema";
@@ -70,6 +71,211 @@ export function parseRoles(roles: string | string[]): string {
return Array.isArray(roles) ? roles.join(",") : roles;
}
+export type DynamicAccessControlEndpoints = {
+ createOrgRole: ReturnType>;
+ deleteOrgRole: ReturnType>;
+ listOrgRoles: ReturnType>;
+ getOrgRole: ReturnType>;
+ updateOrgRole: ReturnType>;
+};
+
+export type TeamEndpoints = {
+ createTeam: ReturnType>;
+ listOrganizationTeams: ReturnType>;
+ removeTeam: ReturnType>;
+ updateTeam: ReturnType>;
+ setActiveTeam: ReturnType>;
+ listUserTeams: ReturnType>;
+ listTeamMembers: ReturnType>;
+ addTeamMember: ReturnType>;
+ removeTeamMember: ReturnType>;
+};
+
+export type OrganizationEndpoints = {
+ createOrganization: ReturnType>;
+ updateOrganization: ReturnType>;
+ deleteOrganization: ReturnType>;
+ setActiveOrganization: ReturnType>;
+ getFullOrganization: ReturnType>;
+ listOrganizations: ReturnType>;
+ createInvitation: ReturnType>;
+ cancelInvitation: ReturnType>;
+ acceptInvitation: ReturnType>;
+ getInvitation: ReturnType>;
+ rejectInvitation: ReturnType>;
+ listInvitations: ReturnType>;
+ getActiveMember: ReturnType>;
+ checkOrganizationSlug: ReturnType>;
+ addMember: ReturnType>;
+ removeMember: ReturnType>;
+ updateMemberRole: ReturnType>;
+ leaveOrganization: ReturnType>;
+ listUserInvitations: ReturnType>;
+ listMembers: ReturnType>;
+ getActiveMemberRole: ReturnType>;
+ hasPermission: ReturnType>;
+};
+
+const createHasPermission = (options: O) => {
+ type DefaultStatements = typeof defaultStatements;
+ type Statements = O["ac"] extends AccessControl
+ ? S
+ : DefaultStatements;
+ type PermissionType = {
+ [key in keyof Statements]?: Array<
+ Statements[key] extends readonly unknown[]
+ ? Statements[key][number]
+ : never
+ >;
+ };
+ type PermissionExclusive =
+ | {
+ /**
+ * @deprecated Use `permissions` instead
+ */
+ permission: PermissionType;
+ permissions?: never;
+ }
+ | {
+ permissions: PermissionType;
+ permission?: never;
+ };
+
+ return createAuthEndpoint(
+ "/organization/has-permission",
+ {
+ method: "POST",
+ requireHeaders: true,
+ body: z
+ .object({
+ organizationId: z.string().optional(),
+ })
+ .and(
+ z.union([
+ z.object({
+ permission: z.record(z.string(), z.array(z.string())),
+ permissions: z.undefined(),
+ }),
+ z.object({
+ permission: z.undefined(),
+ permissions: z.record(z.string(), z.array(z.string())),
+ }),
+ ]),
+ ),
+ use: [orgSessionMiddleware],
+ metadata: {
+ $Infer: {
+ body: {} as PermissionExclusive & {
+ organizationId?: string;
+ },
+ },
+ openapi: {
+ description: "Check if the user has permission",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ permission: {
+ type: "object",
+ description: "The permission to check",
+ deprecated: true,
+ },
+ permissions: {
+ type: "object",
+ description: "The permission to check",
+ },
+ },
+ required: ["permissions"],
+ },
+ },
+ },
+ },
+ responses: {
+ "200": {
+ description: "Success",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ },
+ success: {
+ type: "boolean",
+ },
+ },
+ required: ["success"],
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const activeOrganizationId =
+ ctx.body.organizationId ||
+ ctx.context.session.session.activeOrganizationId;
+ if (!activeOrganizationId) {
+ throw new APIError("BAD_REQUEST", {
+ message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,
+ });
+ }
+ const adapter = getOrgAdapter(ctx.context, options);
+ const member = await adapter.findMemberByOrgId({
+ userId: ctx.context.session.user.id,
+ organizationId: activeOrganizationId,
+ });
+ if (!member) {
+ throw new APIError("UNAUTHORIZED", {
+ message:
+ ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,
+ });
+ }
+ const result = await hasPermission(
+ {
+ role: member.role,
+ options: options || {},
+ permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
+ organizationId: activeOrganizationId,
+ },
+ ctx,
+ );
+
+ return ctx.json({
+ error: null,
+ success: result,
+ });
+ },
+ );
+};
+
+export type OrganizationPlugin = {
+ id: "organization";
+ endpoints: OrganizationEndpoints &
+ (O extends { teams: { enabled: true } } ? TeamEndpoints : {}) &
+ (O extends { dynamicAccessControl: { enabled: true } }
+ ? DynamicAccessControlEndpoints
+ : {});
+ schema: OrganizationSchema;
+ $Infer: {
+ Organization: InferOrganization;
+ Invitation: InferInvitation;
+ Member: InferMember;
+ Team: O["teams"] extends { enabled: true } ? Team : any;
+ TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
+ ActiveOrganization: Awaited<
+ ReturnType>>
+ >;
+ };
+ $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
+ options: O;
+};
+
/**
* Organization plugin for Better Auth. Organization allows you to create teams, members,
* and manage access control for your users.
@@ -77,15 +283,106 @@ export function parseRoles(roles: string | string[]): string {
* @example
* ```ts
* const auth = betterAuth({
- * plugins: [
- * organization({
- * allowUserToCreateOrganization: true,
- * }),
- * ],
+ * plugins: [
+ * organization({
+ * allowUserToCreateOrganization: true,
+ * }),
+ * ],
* });
* ```
*/
-export const organization = (options?: O) => {
+export function organization<
+ O extends OrganizationOptions & {
+ teams: { enabled: true };
+ },
+>(
+ options?: O,
+): {
+ id: "organization";
+ endpoints: OrganizationEndpoints & TeamEndpoints;
+ schema: OrganizationSchema;
+ $Infer: {
+ Organization: InferOrganization;
+ Invitation: InferInvitation;
+ Member: InferMember;
+ Team: O["teams"] extends { enabled: true } ? Team : any;
+ TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
+ ActiveOrganization: Awaited<
+ ReturnType>>
+ >;
+ };
+ $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
+ options: O;
+};
+export function organization<
+ O extends OrganizationOptions & {
+ teams: { enabled: true };
+ dynamicAccessControl: { enabled: true };
+ },
+>(
+ options?: O,
+): {
+ id: "organization";
+ endpoints: OrganizationEndpoints &
+ TeamEndpoints &
+ DynamicAccessControlEndpoints;
+ schema: OrganizationSchema;
+ $Infer: {
+ Organization: InferOrganization;
+ Invitation: InferInvitation;
+ Member: InferMember;
+ Team: O["teams"] extends { enabled: true } ? Team : any;
+ TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
+ ActiveOrganization: Awaited<
+ ReturnType>>
+ >;
+ };
+ $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
+ options: O;
+};
+export function organization<
+ O extends OrganizationOptions & {
+ dynamicAccessControl: { enabled: true };
+ },
+>(
+ options?: O,
+): {
+ id: "organization";
+ endpoints: OrganizationEndpoints & DynamicAccessControlEndpoints;
+ schema: OrganizationSchema;
+ $Infer: {
+ Organization: InferOrganization;
+ Invitation: InferInvitation;
+ Member: InferMember;
+ Team: O["teams"] extends { enabled: true } ? Team : any;
+ TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
+ ActiveOrganization: Awaited<
+ ReturnType>>
+ >;
+ };
+ $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
+ options: O;
+};
+export function organization(
+ options?: O,
+): {
+ id: "organization";
+ endpoints: OrganizationEndpoints;
+ schema: OrganizationSchema;
+ $Infer: {
+ Organization: InferOrganization;
+ Invitation: InferInvitation;
+ Member: InferMember;
+ Team: O["teams"] extends { enabled: true } ? Team : any;
+ TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
+ ActiveOrganization: Awaited<
+ ReturnType>>
+ >;
+ };
+ $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
+ options: O;
+};
+export function organization(options?: O): any {
let endpoints = {
/**
* ### Endpoint
@@ -857,153 +1154,11 @@ export const organization = (options?: O) => {
},
});
- type DefaultStatements = typeof defaultStatements;
- type Statements = O["ac"] extends AccessControl
- ? S
- : DefaultStatements;
- type PermissionType = {
- [key in keyof Statements]?: Array<
- Statements[key] extends readonly unknown[]
- ? Statements[key][number]
- : never
- >;
- };
- type PermissionExclusive =
- | {
- /**
- * @deprecated Use `permissions` instead
- */
- permission: PermissionType;
- permissions?: never;
- }
- | {
- permissions: PermissionType;
- permission?: never;
- };
-
- type OrganizationEndpoints = (O["dynamicAccessControl"] extends {
- enabled: true;
- }
- ? typeof dynamicAccessControlEndpoints
- : {}) &
- (O["teams"] extends { enabled: true } ? typeof teamEndpoints : {}) &
- typeof endpoints;
-
return {
id: "organization",
endpoints: {
- ...(api as OrganizationEndpoints),
- hasPermission: createAuthEndpoint(
- "/organization/has-permission",
- {
- method: "POST",
- requireHeaders: true,
- body: z
- .object({
- organizationId: z.string().optional(),
- })
- .and(
- z.union([
- z.object({
- permission: z.record(z.string(), z.array(z.string())),
- permissions: z.undefined(),
- }),
- z.object({
- permission: z.undefined(),
- permissions: z.record(z.string(), z.array(z.string())),
- }),
- ]),
- ),
- use: [orgSessionMiddleware],
- metadata: {
- $Infer: {
- body: {} as PermissionExclusive & {
- organizationId?: string;
- },
- },
- openapi: {
- description: "Check if the user has permission",
- requestBody: {
- content: {
- "application/json": {
- schema: {
- type: "object",
- properties: {
- permission: {
- type: "object",
- description: "The permission to check",
- deprecated: true,
- },
- permissions: {
- type: "object",
- description: "The permission to check",
- },
- },
- required: ["permissions"],
- },
- },
- },
- },
- responses: {
- "200": {
- description: "Success",
- content: {
- "application/json": {
- schema: {
- type: "object",
- properties: {
- error: {
- type: "string",
- },
- success: {
- type: "boolean",
- },
- },
- required: ["success"],
- },
- },
- },
- },
- },
- },
- },
- },
- async (ctx) => {
- const activeOrganizationId =
- ctx.body.organizationId ||
- ctx.context.session.session.activeOrganizationId;
- if (!activeOrganizationId) {
- throw new APIError("BAD_REQUEST", {
- message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,
- });
- }
- const adapter = getOrgAdapter(ctx.context, options);
- const member = await adapter.findMemberByOrgId({
- userId: ctx.context.session.user.id,
- organizationId: activeOrganizationId,
- });
- if (!member) {
- throw new APIError("UNAUTHORIZED", {
- message:
- ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,
- });
- }
- const result = await hasPermission(
- {
- role: member.role,
- options: options || {},
- permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
- organizationId: activeOrganizationId,
- },
- ctx,
- );
-
- return ctx.json({
- error: null,
- success: result,
- });
- },
- ),
+ ...(api as unknown as OrganizationEndpoints),
+ hasPermission: createHasPermission(options as O),
},
schema: {
...(schema as BetterAuthPluginDBSchema),
@@ -1057,4 +1212,4 @@ export const organization = (options?: O) => {
$ERROR_CODES: ORGANIZATION_ERROR_CODES,
options: options as O,
} satisfies BetterAuthPlugin;
-};
+}
diff --git a/packages/better-auth/src/plugins/organization/schema.ts b/packages/better-auth/src/plugins/organization/schema.ts
index fefc8f29d5..2771291179 100644
--- a/packages/better-auth/src/plugins/organization/schema.ts
+++ b/packages/better-auth/src/plugins/organization/schema.ts
@@ -4,6 +4,516 @@ import type { OrganizationOptions } from "./types";
import type { InferAdditionalFieldsFromPluginOptions } from "../../db";
import type { Prettify } from "better-call";
+// TODO: we need a better way to define plugin schema and endpoints
+export type OrganizationSchema =
+ O["dynamicAccessControl"] extends { enabled: true }
+ ? {
+ organizationRole: {
+ modelName: O["schema"] extends {
+ organizationRole: { modelName: infer M };
+ }
+ ? M extends string
+ ? M
+ : string | undefined
+ : string | undefined;
+ fields: {
+ organizationId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "organization";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ organizationRole: {
+ fields: { organizationId: infer F };
+ };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ role: {
+ type: "string";
+ required: true;
+ fieldName?: O["schema"] extends {
+ organizationRole: {
+ fields: { role: infer F };
+ };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ permission: {
+ type: "string";
+ required: true;
+ fieldName?: O["schema"] extends {
+ organizationRole: {
+ fields: { permission: infer F };
+ };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ createdAt: {
+ type: "date";
+ required: true;
+ defaultValue: Date;
+ fieldName?: O["schema"] extends {
+ organizationRole: {
+ fields: { createdAt: infer F };
+ };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ updatedAt: {
+ type: "date";
+ required: false;
+ fieldName?: O["schema"] extends {
+ organizationRole: {
+ fields: { updatedAt: infer F };
+ };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & (O["schema"] extends {
+ organizationRole: { additionalFields: infer F };
+ }
+ ? F
+ : {});
+ };
+ }
+ : {} & (O["teams"] extends { enabled: true }
+ ? {
+ team: {
+ modelName: O["schema"] extends { team: { modelName: infer M } }
+ ? M extends string
+ ? M
+ : string | undefined
+ : string | undefined;
+ fields: {
+ name: {
+ type: "string";
+ required: true;
+ fieldName?: O["schema"] extends {
+ team: { fields: { name: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ organizationId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "organization";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ team: { fields: { organizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ createdAt: {
+ type: "date";
+ required: true;
+ fieldName?: O["schema"] extends {
+ team: { fields: { createdAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ updatedAt: {
+ type: "date";
+ required: false;
+ fieldName?: O["schema"] extends {
+ team: { fields: { updatedAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & (O["schema"] extends { team: { additionalFields: infer F } }
+ ? F
+ : {});
+ };
+ teamMember: {
+ modelName: O["schema"] extends {
+ teamMember: { modelName: infer M };
+ }
+ ? M extends string
+ ? M
+ : string | undefined
+ : string | undefined;
+ fields: {
+ teamId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "team";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ teamMember: { fields: { teamId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ userId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "user";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ teamMember: { fields: { userId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ createdAt: {
+ type: "date";
+ required: false;
+ fieldName?: O["schema"] extends {
+ teamMember: { fields: { createdAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ };
+ };
+ }
+ : {}) & {
+ organization: {
+ modelName: O["schema"] extends {
+ organization: { modelName: infer M };
+ }
+ ? M extends string
+ ? M
+ : string | undefined
+ : string | undefined;
+ fields: {
+ name: {
+ type: "string";
+ required: true;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ organization: { fields: { name: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ slug: {
+ type: "string";
+ required: true;
+ unique: true;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ organization: { fields: { slug: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ logo: {
+ type: "string";
+ required: false;
+ fieldName?: O["schema"] extends {
+ organization: { fields: { logo: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ createdAt: {
+ type: "date";
+ required: true;
+ fieldName?: O["schema"] extends {
+ organization: { fields: { createdAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ updatedAt: {
+ type: "date";
+ required: false;
+ fieldName?: O["schema"] extends {
+ organization: { fields: { updatedAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ };
+ };
+ member: {
+ modelName: O["schema"] extends { member: { modelName: infer M } }
+ ? M extends string
+ ? M
+ : string | undefined
+ : string | undefined;
+ fields: {
+ organizationId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "organization";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ member: { fields: { organizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ userId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "user";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ member: { fields: { userId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ role: {
+ type: "string";
+ required: true;
+ fieldName?: O["schema"] extends {
+ member: { fields: { role: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ defaultValue: "member";
+ };
+ createdAt: {
+ type: "date";
+ required: true;
+ fieldName?: O["schema"] extends {
+ member: { fields: { createdAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & (O["schema"] extends { member: { additionalFields: infer F } }
+ ? F
+ : {});
+ };
+ invitation: {
+ modelName: O["schema"] extends {
+ invitation: { modelName: infer M };
+ }
+ ? M
+ : string | undefined;
+ fields: {
+ organizationId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "organization";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { organizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ email: {
+ type: "string";
+ required: true;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { email: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ role: {
+ type: "string";
+ required: true;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { role: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ status: {
+ type: "string";
+ required: true;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { status: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ defaultValue: "pending";
+ };
+ expiresAt: {
+ type: "date";
+ required: false;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { expiresAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ createdAt: {
+ type: "date";
+ required: true;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { createdAt: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ defaultValue: Date;
+ };
+ inviterId: {
+ type: "string";
+ required: true;
+ references: {
+ model: "user";
+ field: "id";
+ };
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { inviterId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & (O["schema"] extends {
+ invitation: { additionalFields: infer F };
+ }
+ ? F
+ : {}) &
+ O extends { teams: { enabled: true } }
+ ? {
+ teamId: {
+ type: "string";
+ required: false;
+ sortable: true;
+ fieldName?: O["schema"] extends {
+ invitation: { fields: { teamId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ }
+ : {};
+ };
+ session: {
+ fields: {
+ activeOrganizationId: {
+ type: "string";
+ required: false;
+ fieldName?: O["schema"] extends {
+ session: { fields: { activeOrganizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & O["teams"] extends { enabled: true }
+ ? {
+ activeTeamId: {
+ type: "string";
+ required: false;
+ };
+ activeOrganizationId: {
+ type: "string";
+ required: false;
+ fieldName?: O["schema"] extends {
+ session: { fields: { activeOrganizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ }
+ : {
+ activeOrganizationId: {
+ type: "string";
+ required: false;
+ fieldName?: O["schema"] extends {
+ session: { fields: { activeOrganizationId: infer F } };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : string | undefined;
+ };
+ } & (O["schema"] extends {
+ session: { additionalFields: infer F };
+ }
+ ? F extends string
+ ? F
+ : string | undefined
+ : {});
+ };
+ };
+
export const role = z.string();
export const invitationStatus = z
.enum(["pending", "accepted", "rejected", "canceled"])
diff --git a/packages/better-auth/src/types/types.test.ts b/packages/better-auth/src/types/types.test.ts
index 8d7ccd0486..67b41b5e2a 100644
--- a/packages/better-auth/src/types/types.test.ts
+++ b/packages/better-auth/src/types/types.test.ts
@@ -44,7 +44,7 @@ describe("general types", async (it) => {
twoFactorEnabled: boolean | undefined | null;
}>();
- expectTypeOf().toEqualTypeOf<{
+ expectTypeOf().toMatchObjectType<{
id: string;
userId: string;
expiresAt: Date;
diff --git a/packages/core/src/db/index.ts b/packages/core/src/db/index.ts
index 4379d02cd5..e89dcb6e27 100644
--- a/packages/core/src/db/index.ts
+++ b/packages/core/src/db/index.ts
@@ -4,7 +4,6 @@ import type {
DBFieldType,
DBPrimitive,
BetterAuthDBSchema,
- DBPreservedModels,
} from "./type";
import type { BetterAuthPluginDBSchema } from "./plugin";
export type { BetterAuthPluginDBSchema } from "./plugin";
@@ -23,7 +22,7 @@ export type {
DBPrimitive,
BetterAuthDBSchema,
DBPreservedModels,
-};
+} from "./type";
/**
* @deprecated Backport for 1.3.x, we will remove this in 1.4.x
diff --git a/packages/core/src/types/init-options.ts b/packages/core/src/types/init-options.ts
index c799099c41..b477427436 100644
--- a/packages/core/src/types/init-options.ts
+++ b/packages/core/src/types/init-options.ts
@@ -6,7 +6,7 @@ import type {
DBFieldAttribute,
DBPreservedModels,
SecondaryStorage,
-} from "../db/type";
+} from "../db";
import type { Account, RateLimit, Session, User, Verification } from "../db";
import type { Database as BunDatabase } from "bun:sqlite";
import type { DatabaseSync } from "node:sqlite";
@@ -15,7 +15,7 @@ import type { SocialProviderList, SocialProviders } from "../social-providers";
import type { Logger } from "../env";
import type { AuthContext, GenericEndpointContext } from "./context";
import type { AuthMiddleware } from "../api";
-import type { BetterAuthPlugin } from "..";
+import type { BetterAuthPlugin } from "./plugin";
type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql";
type OmitId = Omit;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2da01d535d..c576a0d883 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -352,7 +352,7 @@ importers:
version: 12.23.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
geist:
specifier: ^1.4.2
- version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
+ version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
input-otp:
specifier: ^1.4.2
version: 1.4.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@@ -608,7 +608,7 @@ importers:
version: 15.8.3(@oramacloud/client@2.1.4)(@tanstack/react-router@1.131.27(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(algoliasearch@5.36.0)(lucide-react@0.542.0(react@19.2.0))(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(tailwindcss@4.1.13)
geist:
specifier: ^1.4.2
- version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
+ version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
gray-matter:
specifier: ^4.0.3
version: 4.0.3
@@ -839,12 +839,18 @@ importers:
e2e/smoke/test/fixtures/tsconfig-decelration:
dependencies:
+ '@better-auth/sso':
+ specifier: workspace:*
+ version: link:../../../../../packages/sso
+ '@better-auth/stripe':
+ specifier: workspace:*
+ version: link:../../../../../packages/stripe
better-auth:
specifier: workspace:*
version: link:../../../../../packages/better-auth
- better-sqlite3:
- specifier: ^12.4.1
- version: 12.4.1
+ stripe:
+ specifier: ^18.5.0
+ version: 18.5.0(@types/node@24.7.2)
e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types:
dependencies:
@@ -17523,7 +17529,9 @@ snapshots:
metro-runtime: 0.83.3
transitivePeerDependencies:
- '@babel/core'
+ - bufferutil
- supports-color
+ - utf-8-validate
optional: true
'@react-native/normalize-colors@0.74.89': {}
@@ -21641,7 +21649,7 @@ snapshots:
function-bind@1.1.2: {}
- geist@1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
+ geist@1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
dependencies:
next: 16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)